Как вы добавляете NOLOCK при использовании nhibernate? (запрос критериев)
Как добавить NOLOCK с nHibernate?
Ответ 1
 SetLockMode(LockMode.None) или connection.isolation ReadUncomitted НЕ добавьте NOLOCK к вашим запросам.
Айенде переходит в правильный ответ в своем блоге:
Если вы используете <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>
Обратите внимание на WTIH(nolock), добавленный к предложению FROM.
Ответ 2
Я объясню, как это сделать, чтобы вы могли добавлять NOLOCK (или любые другие подсказки для запросов), в то же время используя ICriteria или HQL, и без необходимости хранить информацию о своих запросах в конфигурациях сопоставлений или сеансов factory.
Я написал это для 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);
    }
}
Затем создайте некоторые полезные методы расширения где-нибудь:
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));
    }
}
Затем скажите NHibernate использовать ваш перехватчик:
config.SetInterceptor(new QueryHintInterceptor());
Наконец, включите свойство use_sql_comments в вашей конфигурации NHibernate.
И все готово! Теперь вы можете добавить подсказки nolock следующим образом:
var criteria = Session.CreateCriteria<Foo>()
    .QueryHintNoLock("tableFoo")
    .List<Foo>();
Я основал эту работу вокруг описанной здесь техники: 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 напрямую, или выполнив то, что я сделал, и создав свой собственный диалог, который исправляет проблему).
Во-вторых, есть еще одна ошибка, которая возникает, когда вы выполняете постраничный запрос, на любой странице после первой страницы, и вы используете прогнозы. Sql, сгенерированный NHibernate, полностью ошибочен вокруг предложения OVER. На этом этапе я не знаю, как исправить эту ошибку, но я над этим работаю. ОБНОВЛЕНИЕ: Я подробно описал, как исправить эту ошибку здесь. Как и в случае с другой ошибкой, это также можно устранить либо путем исправления исходного кода NHibernate, либо путем создания собственного класса Dialect.
Ответ 3
Если вы собираетесь использовать его во многих своих запросах, вы можете установить его по умолчанию с помощью свойства конфигурации connection.isolation.
<property name="connection.isolation">ReadUncommitted</property> 
Просмотрите документацию по этому свойству.
Ответ 4
Это не добавляет NOLOCK к вашим запросам, которые я могу сказать, но он должен обеспечивать ту же функциональность, которая должна выполнять грязные чтения только внутри транзакции.
Session.BeginTransaction(IsolationLevel.ReadUncommitted);
Я использовал Sql Profiler, чтобы увидеть, что сделала бы эта команда, но ничего не изменила в запросе или не добавила NOLOCK (nhibernate использует sp_executesql для большинства моих запросов). Я все равно бегал с ним, и, похоже, все тупики исчезли. Наше программное обеспечение работает уже 3 дня без взаимоблокировок. До этого изменения я обычно мог воспроизвести тупики в течение 15 минут. Я не на 100% убежден, что это исправлено, но после проверки на несколько недель я узнаю больше.
Это сработало и для других: http://quomon.com/NHibernate-deadlock-problem-q43633.aspx
Ответ 5
Вы можете решить эту проблему с помощью Interceptor.
var session = SessionFactory.OpenSession(new NoLockInterceptor());
Вот реализация класса NoLockInterceptor. В принципе класс NoLockInterceptor будет вставлять подсказку "WITH (NOLOCK)" после каждого имени таблицы в запросе выбора, сгенерированного nHibernate.
public class NoLockInterceptor : EmptyInterceptor
{
    public override SqlString OnPrepareStatement(SqlString sql)
        {
            //var log = new StringBuilder();
            //log.Append(sql.ToString());
            //log.AppendLine();
            // Modify the sql to add hints
            if (sql.StartsWithCaseInsensitive("select"))
            {
                var parts = sql.ToString().Split().ToList();
                var fromItem = parts.FirstOrDefault(p => p.Trim().Equals("from", StringComparison.OrdinalIgnoreCase));
                int fromIndex = fromItem != null ? parts.IndexOf(fromItem) : -1;
                var whereItem = parts.FirstOrDefault(p => p.Trim().Equals("where", StringComparison.OrdinalIgnoreCase));
                int whereIndex = whereItem != null ? parts.IndexOf(whereItem) : parts.Count;
                if (fromIndex == -1)
                    return sql;
                parts.Insert(parts.IndexOf(fromItem) + 3, "WITH (NOLOCK)");
                for (int i = fromIndex; i < whereIndex; i++)
                {
                    if (parts[i - 1].Equals(","))
                    {
                        parts.Insert(i + 3, "WITH (NOLOCK)");
                        i += 3;
                    }
                    if (parts[i].Trim().Equals("on", StringComparison.OrdinalIgnoreCase))
                    {
                        parts[i] = "WITH (NOLOCK) on";
                    }
                }
                // MUST use SqlString.Parse() method instead of new SqlString()
                sql = SqlString.Parse(string.Join(" ", parts));
            }
            //log.Append(sql);
            return sql;
        }
}
Ответ 6
Вы можете попробовать следующее:
public class NoLockInterceptor : EmptyInterceptor
{
    /// <summary>
    /// OnPrepare.
    /// </summary>
    /// <param name="sql">Query.</param>
    public override SqlString OnPrepareStatement(SqlString sql)
    {
        var begin = SqlString.Parse("with query as (");
        var end = SqlString.Parse(") select * from query with ( nolock )");
        return base.OnPrepareStatement(begin + sql + end);
    }
}
Ответ 7
Я взял @cbp ответ и немного изменил его:
private static SqlString ApplyQueryHintNoLock(SqlString sql)
    {
        var sqlString = sql.ToString();
        if (_cache.Get(sqlString) is SqlString cachedSql)
        {
            //return cachedSql;
        }
        var regex1 = new Regex(@" FROM\s+[a-zA-Z1-9_.]*\s+([a-zA-Z1-9_.]*)", RegexOptions.IgnoreCase);
        var regex2 = new Regex(@"(?: INNER JOIN| LEFT OUTER JOIN)\s+[a-zA-Z1-9_.]*\s+([a-zA-Z1-9_.]*) ON", RegexOptions.IgnoreCase);
        var tableAliasMatches = regex1.Matches(sqlString);
        var joinsAliasMatches = regex2.Matches(sqlString);
        var combined = tableAliasMatches.OfType<Match>()
            .Concat(joinsAliasMatches.OfType<Match>())
            .Where(m => m.Success)
            .OrderBy(m=>m.Index);
        var noLockLength = " WITH (NOLOCK)".Length;
        var q = 0;
        foreach (Match aliasMatch in combined)
        {
            var alias = aliasMatch.Groups[1].Value;
            var aliasIndex = aliasMatch.Groups[1].Index + q + alias.Length;
            sql = sql.Insert(aliasIndex, " WITH (NOLOCK)");
            q += noLockLength;
        }
        _cache.Set(sqlString, sql, DateTimeOffset.Now.AddHours(3));
        return sql;
    }
    internal static string GetQueryHintNoLock()
    {
        return _queryHintNoLockCommentString;
    }
таким образом, он не добавит блокировки ко всем таблицам и внутренним объединениям в запросе.
это хорошо для всех вас, используя единицу шаблона работы.
