NHibernate IStatelessSession CreateQuery 失败

NHibernate IStatelessSession CreateQuery failure

当我们上周五发布时,我收到一个错误,我没有接受。错误信息是:

could not execute update query[SQL: delete from dbo.MyTable where col1=? and col2=? and col3=? and col4=? and col5=?]

我的C#代码如下:

var hqlDelete = "DELETE MyTable m WHERE m.Col1 = :var_1 AND m.Col2 = :var_2 AND m.Col3= :var_3 AND m.Col4 = :var_4 AND m.Col5= :var_5";
var deletedEntities = session.CreateQuery(hqlDelete)
                             .SetString("var_1", variable1)
                             .SetString("var_2", variable2)
                             .SetString("var_3", variable3)
                             .SetString("var_4", variable4)
                             .SetString("var_5", variable5)
                             .ExecuteUpdate();
transaction.Commit();
session.Close();

现在,正如我所说,在接受测试时没有触发错误。此外,当我使用生产数据库(来自我的开发人员席位的代码)进行测试时,它也可以正常工作。

当我调用 Web 服务并向其 POST "measurement" 时触发代码。唯一的区别是我在测试时调用该服务,而在生产时另一家公司将测量值发送到 Web 服务。

我认为这可能与 sessions/transactions 的数量有关,但这并不能真正解释为什么变量在错误消息中显示为 ?

有什么想法吗?我可以提供更多信息以便您帮助我解决这个问题吗?

编辑:InnerExeption 是

{"Transaction (Process ID 68) was deadlocked on lock | communication buffer resources with another process and has been chosen as the deadlock victim. Rerun the transaction."}

解决死锁可能是一件困难的事情,尤其是在使用 ORM 时。死锁的发生通常是因为不同进程(或线程)获取数据库对象的锁的顺序不一致,导致它们互相等待。

ORM 无法让您对锁获取顺序进行太多控制。您可以修改查询顺序,但这可能很乏味。特别是当缓存导致其中一些不命中 DB 时。此外,它应该在使用相同数据库的任何其他应用程序上以相同的顺序完成。

您可能会检测到死锁错误并执行消息中的操作:重试整个过程。对于 NHibernate,这意味着放弃当前会话并重试整个工作单元。

如果您的数据库是 SQL 服务器,则有一个默认设置会大大增加死锁风险:禁用 read committed snapshot mode。如果它在您的数据库上被禁用,您可以通过启用它来大大降低死锁风险。此模式允许读取已提交隔离级别下的读取停止发出读取锁。

您可以使用

检查此设置
select snapshot_isolation_state_desc, is_read_committed_snapshot_on
    from sys.databases 
    where name = 'YourDbName'

您可以通过

启用此设置
alter database YourDbName
    set allow_snapshot_isolation on

alter database YourDbName
    set read_committed_snapshot on

这需要在目标数据库上有 none 运行 事务。当然,这需要数据库的管理员权限。

在我无法更改此设置的应用程序上,我不得不采用更古怪的方式:将 NHibernate 默认隔离模式 (connection.isolation configuration parameter) 设置为 ReadUncommitted。我的应用程序大部分是只读的,并且我在少数必须读取然后写入数据的事务上显式提升了隔离模式(例如使用 session.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))。

您还应该检查所有使用数据库的应用程序当前使用的隔离模式:其中一些是否使用比实际需要更高的隔离级别? (如果可能,应避免 RepeatableReadSerializable。)这是一个耗时的过程,因为它需要很好地理解隔离级别,同时研究每个用例以确定什么是合适的最小隔离级别。