如何用nHibernate添加NOLOCK?

mrb*_*lah 24 nhibernate criteria fluent-nhibernate

使用nhibernate时如何添加NOLOCK?(标准查询)

Ant*_*ioR 21

SetLockMode(LockMode.None)或者connection.isolation ReadUncomitted不附加NOLOCK到您的查询.

Ayende 在他的博客上找到了正确的答案:

如果您正在使用<sql-query>,可以执行以下操作:

<sql-query name="PeopleByName">
    <return alias="person"
                    class="Person"/>
    SELECT {person.*}
    FROM People {person} WITH(nolock)
    WHERE {person}.Name LIKE :name
</sql-query>
Run Code Online (Sandbox Code Playgroud)

请注意WTIH(nolock)FROM子句的附加内容.

  • 但是将连接事务隔离级别设置为"read uncommitted"等同于向查询中的每个表添加(nolock),对吧? (2认同)

cbp*_*cbp 18

我将解释如何执行此操作,以便您可以添加NOLOCK(或任何其他查询提示),同时仍然使用ICriteria或HQL,而无需将查询的知识粘贴到映射或会话工厂配置中.

我为NHibernate 2.1编写了这个.有一些主要的警告,主要是由于NHibernate中的"use_sql_comments"打开时的错误(见下文).我不确定这些错误是否已在NH 3中修复,但尝试一下.更新:从NH 3.3开始,错误尚未得到修复.我在这里描述的技术和解决方法仍然有效.

首先,创建一个拦截器,如下所示:

[Serializable]
public class QueryHintInterceptor : EmptyInterceptor
{
    internal const string QUERY_HINT_NOLOCK_COMMENT = "queryhint-nolock: ";

    /// <summary>
    /// Gets a comment to add to a sql query to tell this interceptor to add 'OPTION (TABLE HINT(table_alias, INDEX = index_name))' to the query.
    /// </summary>
    internal static string GetQueryHintNoLock(string tableName)
    {
        return QUERY_HINT_NOLOCK_COMMENT + tableName;
    }

    public override SqlString OnPrepareStatement(SqlString sql)
    {
        if (sql.ToString().Contains(QUERY_HINT_NOLOCK_COMMENT))
        {
            sql = ApplyQueryHintNoLock(sql, sql.ToString());
        }

        return base.OnPrepareStatement(sql);
    }

    private static SqlString ApplyQueryHintNoLock(SqlString sql, string sqlString)
    {
        var indexOfTableName = sqlString.IndexOf(QUERY_HINT_NOLOCK_COMMENT) + QUERY_HINT_NOLOCK_COMMENT.Length;

        if (indexOfTableName < 0)
            throw new InvalidOperationException(
                "Query hint comment should contain name of table, like this: '/* queryhint-nolock: tableName */'");

        var indexOfTableNameEnd = sqlString.IndexOf(" ", indexOfTableName + 1);

        if (indexOfTableNameEnd < 0)
            throw new InvalidOperationException(
                "Query hint comment should contain name of table, like this: '/* queryhint-nlock: tableName */'");

        var tableName = sqlString.Substring(indexOfTableName, indexOfTableNameEnd - indexOfTableName).Trim();

        var regex = new Regex(@"{0}\s(\w+)".F(tableName));

        var aliasMatches = regex.Matches(sqlString, indexOfTableNameEnd);

        if (aliasMatches.Count == 0)
            throw new InvalidOperationException("Could not find aliases for table with name: " + tableName);

        var q = 0;
        foreach (Match aliasMatch in aliasMatches)
        {
            var alias = aliasMatch.Groups[1].Value;
            var aliasIndex = aliasMatch.Groups[1].Index + q + alias.Length;

            sql = sql.Insert(aliasIndex, " WITH (NOLOCK)");
            q += " WITH (NOLOCK)".Length;
        }
        return sql;
    }

    private static SqlString InsertOption(SqlString sql, string option)
    {
        // The original code used just "sql.Length". I found that the end of the sql string actually contains new lines and a semi colon.
        // Might need to change in future versions of NHibernate.
        var regex = new Regex(@"[^\;\s]", RegexOptions.RightToLeft);
        var insertAt = regex.Match(sql.ToString()).Index + 1;
        return sql.Insert(insertAt, option);
    }
}
Run Code Online (Sandbox Code Playgroud)

然后在某处创建一些不错的扩展方法:

public static class NHibernateQueryExtensions
{
    public static IQuery QueryHintNoLock(this IQuery query, string tableName)
    {
        return query.SetComment(QueryHintInterceptor.GetQueryHintNoLock(tableName));
    }

    public static ICriteria QueryHintNoLock(this ICriteria query, string tableName)
    {
        return query.SetComment(QueryHintInterceptor.GetQueryHintNoLock(tableName));
    }
}
Run Code Online (Sandbox Code Playgroud)

接下来,告诉NHibernate使用你的拦截器:

config.SetInterceptor(new QueryHintInterceptor());
Run Code Online (Sandbox Code Playgroud)

最后,在NHibernate配置中启用use_sql_comments属性.

而且你已经完成了!现在你可以添加这样的nolock提示:

var criteria = Session.CreateCriteria<Foo>()
    .QueryHintNoLock("tableFoo")
    .List<Foo>();
Run Code Online (Sandbox Code Playgroud)

我的工作基于这里描述的技术:http://www.codewrecks.com/blog/index.php/2011/07/23/use-sql-server-query-hints-with-nhibernate-hql-and-的ICriteria /

NHibernate Showstopping Bugs:

首先,您需要修复NHibernate的这个错误.(您可以通过直接修复NHibernate源,或者通过执行我所做的操作并创建自己的Dialect修复此问题来修复此错误.

其次,当您在第一页之后的任何页面上执行分页查询并且您正在使用投影时,似乎会出现另一个错误.NHibernate生成的SQL在"OVER"子句中是完全错误的.在这个阶段我不知道如何修复这个bug,但我正在努力.更新:在这里详细介绍了如何解决这个问题.与其他bug一样,这个也可以通过修复NHibernate源代码或创建自己的Dialect类来修复.


Roh*_*wal 8

如果要在大量查询中使用它,可以通过配置属性将其设置为默认值connection.isolation.

<property name="connection.isolation">ReadUncommitted</property> 
Run Code Online (Sandbox Code Playgroud)

查看有关此属性文档.


gok*_*ter 6

这不会将NOLOCK添加到我可以告诉的查询中,但它应该提供相同的功能 - 即仅在事务内执行脏读.

Session.BeginTransaction(IsolationLevel.ReadUncommitted);
Run Code Online (Sandbox Code Playgroud)

我使用Sql Profiler来查看上面的命令会做什么,但它没有改变任何关于查询或向他们添加NOLOCK(nhibernate对我的大多数查询使用sp_executesql).无论如何我还是跑了,似乎所有的僵局都消失了.我们的软件已经运行了3天,现在没有死锁.在此更改之前,我通常可以在15分钟内重现死锁.我并不是100%相信它已经解决了这个问题,但经过又一周的测试后,我会知道更多.

这也适用于其他人:http://quomon.com/NHibernate-deadlock-problem-q43633.aspx