Asp.net nhibernate死锁

Asp.net nhibernate死锁,asp.net,sql-server,nhibernate,Asp.net,Sql Server,Nhibernate,我正在ASP.NET页面中使用以下代码创建一条记录,然后对记录进行计数,以确保没有超过设置的限制,如果超过了,则回滚事务 using (var session = NhibernateHelper.OpenSession()) using (var transaction = session.BeginTransaction()) { session.Lock(mall, LockMode.None); var voucher = new Voucher(); vouc

我正在ASP.NET页面中使用以下代码创建一条记录,然后对记录进行计数,以确保没有超过设置的限制,如果超过了,则回滚事务

using (var session = NhibernateHelper.OpenSession())
using (var transaction = session.BeginTransaction())
{
    session.Lock(mall, LockMode.None);

    var voucher = new Voucher();
    voucher.FirstName = firstName ?? string.Empty;
    voucher.LastName = lastName ?? string.Empty;
    voucher.Address = address ?? string.Empty;
    voucher.Address2 = address2 ?? string.Empty;
    voucher.City = city ?? string.Empty;
    voucher.State = state ?? string.Empty;
    voucher.Zip = zip ?? string.Empty;
    voucher.Email = email ?? string.Empty;
    voucher.Mall = mall;
    session.Save(voucher);

    var issued = session.CreateCriteria<Voucher>()
        .Add(Restrictions.Eq("Mall", mall))
        .SetProjection(Projections.Count("ID"))
        .UniqueResult<int>();

    if (issued >= mall.TotalVouchers)
    {
        transaction.Rollback();
        throw new VoucherLimitException();
    }

    transaction.Commit();
    return voucher;
}        
使用(var session=NhibernateHelper.OpenSession())
使用(var transaction=session.BeginTransaction())
{
session.Lock(mall,LockMode.None);
var凭证=新凭证();
凭证.FirstName=FirstName??字符串.空;
凭证.LastName=LastName??字符串.空;
凭证.地址=地址??字符串.空;
凭证.Address2=Address2??字符串.空;
凭证.城市=城市??字符串.空;
凭证.State=状态??字符串.Empty;
凭证.Zip=Zip??字符串.空;
凭证.Email=Email??字符串,空;
购物中心=购物中心;
会话保存(凭证);
var issued=session.CreateCriteria()
.Add(限制条件Eq(“商场”,商场))
.SetProjection(Projections.Count(“ID”))
.UniqueResult();
如果(已发行>=购物中心总凭单)
{
transaction.Rollback();
抛出新VoucherLimitException();
}
Commit();
退货凭证;
}        
然而,我遇到了一大堆僵局。我假设发生这种情况是因为我正在尝试统计我刚刚执行插入的表中的记录,而插入的行上仍然持有锁,从而导致死锁

  • 有人能证实这一点吗
  • 有人能提出解决办法吗
我尝试在最后一个查询中调用SetLockMode(LockMode.None),但这会导致一个我无法理解的NullReferenceException

编辑:如果我在保存对象之前运行查询,它会工作,但是我没有完成验证插入是否超出限制的目标(在并发插入的情况下)

编辑:我发现在session.BeginTransaction调用中使用IsolationLevel.ReadUncommitted可以解决问题,但我不是数据库专家。这是问题的适当解决方案还是我应该如何调整我的逻辑?

2个问题:

  • 凭证删除的频率如何
  • 对某个问题有任何反对意见(超越纯粹性) db电平触发器

  • 这种设计很容易死锁——通常(并非总是)一个连接本身不可能死锁,但针对同一个表执行插入和聚合的多个连接很可能死锁。这是因为,虽然从执行工作的连接的角度来看,一个事务中的所有活动看起来都是完整的——db不会从“自己的”记录中锁定一个事务——但来自其他事务的聚合查询会试图同时锁定整个表或其中的大部分,而这些查询会死锁

    在本例中,Read Uncommitted不是您的朋友,因为它基本上说是“忽略锁”,这意味着违反了您在数据方面设置的规则。即,表中记录的计数将不准确,您将根据不准确的计数进行操作。当实际答案为11时,计数将返回10或13

    我最好的建议是重新安排插入逻辑,这样就可以捕获计数的概念,而不必逐行计数。你可以往几个方向走。我的一个想法是:用序列对插入的凭证进行编号,并对序列本身进行限制

  • 用列(我猜是)MallID、nextVoucher和maxvoucher创建一个序列表
  • 在该表中添加mallids,1,以及每个mall的限制
  • 将插入逻辑更改为以下伪代码:
  • 开始交易 在序列表中检查Mall的下一个通道是否完好;如果存在太多,则中止 如果少于购物中心的最大购物券,则{ 检查、提取、锁定和增加下一步 如果增量成功,则使用nextVoucher的值执行插入。 将其包含在目标表中。 } 错误?回降 没有错误?犯罪 像这样的序列表会对并发性造成一些伤害,但我认为不会像不断计算表中的行那样严重。一定要完成测试。 此外,[check,fetch,lock and increment]也很重要-您必须以独占方式锁定序列表中的行,以防止其他连接在增量之前的瞬间使用相同的值。我知道这方面的SQL语法,但恐怕我不是nHibernate专家


    对于读取未提交的数据错误,请检查以下内容:(免责声明:Merrill Aldrich是我:-)

    理论上,凭证永远不会被删除甚至更新。此外,出于性能原因,我倾向于避免使用触发器(尽管纯度也是一个不错的选择),但假设凭证经常被删除。什么样的正确方法可以确保通过插入凭证,我从未超过任意限制?如果凭证没有被删除,我只需在尝试插入.JBland之前检查(mall.dictions.Count