Comment ajouter NOLOCK avec nHibernate?

comment ajouter NOLOCK en utilisant nhibernate? (recherche de critères)

22
demandé sur mrblah 2009-08-20 01:33:25

5 réponses

SetLockMode(LockMode.None) ou connection.isolation ReadUncomitted n'ajoute pas de NOLOCK à vos requêtes.

Ayende va dans le bonne réponse sur son blog :

si vous utilisez <sql-query> vous pouvez faire ce qui suit:

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

Note la WTIH(nolock) ajouté à la FROM clause.

19
répondu AntonioR 2012-11-16 13:28:36

je vais vous expliquer comment faire pour que vous puissiez ajouter NOLOCK (ou toute autre indication de requête), tout en utilisant ICriteria ou HQL, et sans avoir à coller la connaissance de vos requêtes dans les mappings ou la configuration de session factory.

j'ai écrit ceci pour NHibernate 2.1. Il y a un certain nombre de mises en garde majeures, principalement en raison de bogues dans NHibernate lorsque "use_sql_comments" est activé (voir ci-dessous). Je ne sais pas si ces bugs ont été corrigés dans NH 3, mais essayez-le. mise à jour: les bogues n'ont pas été corrigés depuis NH 3.3. La technique et les solutions de rechange que je décris ici fonctionnent toujours.

tout d'Abord, créer un intercepteur, comme ceci:

[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);
    }
}

puis créer quelques méthodes d'extension agréable quelque part:

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));
    }
}

ensuite, dites à NHibernate d'utiliser votre intercepteur:

config.SetInterceptor(new QueryHintInterceptor());

enfin, activez la propriété use_sql_comments dans votre configuration NHibernate.

et c'est fini! Maintenant, vous pouvez ajouter des conseils de nolock comme ceci:

var criteria = Session.CreateCriteria<Foo>()
    .QueryHintNoLock("tableFoo")
    .List<Foo>();

j'ai basé ce travail sur la technique décrite ici: http://www.codewrecks.com/blog/index.php/2011/07/23/use-sql-server-query-hints-with-nhibernate-hql-and-icriteria /

NHibernate Showstopping Bugs:

tout D'abord, il y a ce bug avec NHibernate que vous devrez corriger. (Vous pouvez soit corriger ce bug en réparant la source NHibernate directement, ou en faisant ce que j'ai fait et en créant votre propre dialecte qui répare le problème).

deuxièmement, il y a un autre bug qui semble se produire lorsque vous faites une requête en pagination, sur n'importe quelle page après la première page, et vous utilisez des projections. Le sql généré par NHibernate est complètement erroné autour de la clause "OVER". À Je ne sais pas comment réparer ce bug, mais j'y travaille. mise à jour: j'ai détaillé comment corriger ce bug ici . Comme l'autre bug, celui-ci peut aussi être corrigé en réparant le code source de NHibernate ou en créant votre propre classe de dialecte.

15
répondu cbp 2017-05-23 11:45:39

si vous allez l'utiliser dans beaucoup de vos requêtes, Vous pouvez le définir par défaut via la propriété de configuration connection.isolation .

<property name="connection.isolation">ReadUncommitted</property> 

consultez la documentation sur cette propriété .

8
répondu Rohit Agarwal 2009-11-23 21:41:24

cela n'ajoute pas NOLOCK à vos requêtes que je peux dire, mais il devrait fournir la même fonctionnalité - qui est d'effectuer des lectures sales seulement à l'intérieur d'une transaction.

Session.BeginTransaction(IsolationLevel.ReadUncommitted);

j'ai utilisé Sql Profiler pour voir ce que ferait la commande ci-dessus mais elle n'a rien changé à la requête ou n'y a pas ajouté de NOLOCK (nhibernate utilise sp_executesql pour la plupart de mes requêtes). J'ai quand même couru avec, et il semble que tous les blocages ont disparu. Notre logiciel a fonctionné comme ça depuis 3 jours sans blocages. Avant ce changement, je pouvais généralement reproduire les blocages en moins de 15 minutes. Je ne suis pas convaincu à 100% que cela a réparé, mais après encore quelques semaines de tests, j'en saurai plus.

cela a fonctionné pour d'autres aussi bien: http://quomon.com/NHibernate-deadlock-problem-q43633.aspx

6
répondu goku_da_master 2013-01-21 16:54:28

vous pouvez le résoudre en utilisant Interceptor.

var session = SessionFactory.OpenSession(new NoLockInterceptor());

Voici l'implémentation pour la classe NoLockInterceptor. Fondamentalement, la classe NoLockInterceptor insérera" WITH (NOLOCK) " hit après chaque nom de table dans la requête select, générée par 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;
        }
}
1
répondu Tola Ch. 2016-09-22 16:50:38