Я объясню, как это сделать, чтобы вы могли добавить 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);
}
}
Затем создайте несколько хороших методов расширения:
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:
Во-первых, есть эта ошибка с NHibernate, которую вам нужно будет исправить.
(Вы можете исправить эту ошибку, исправив источник NHibernate напрямую, или , выполнив то, что я сделал и создав собственный диалект, который исправит проблему).
Во-вторых, есть другая ошибка, которая возникает, когда вы выполняете постраничный запрос на любой странице после первой страницы и используете проекции. SQL, сгенерированный NHibernate, полностью неверен в предложении «OVER». На данном этапе я не знаю, как исправить эту ошибку, но я работаю над этим. ОБНОВЛЕНИЕ: Я подробно описал, как исправить эту ошибку здесь . Как и в случае с другой ошибкой, эту ошибку также можно исправить, исправив исходный код NHibernate или создав собственный класс Dialect.