sqlserver執行sql的時候是帶有lock的,這樣可能會造成死鎖現象
在使用ef/efcore時,生成的sql都是帶有lock的,所以當使用ef組件時,需要單獨設置一下with nolock
1. net fx集成
我的項目基於net fx4.6.1,ef版本是6.1.3版本
首先在項目中增加WithNoLockInterceptor
/// <summary>
/// ef實現withnolock,給表名后面添加with(nolock),不適用.net core
/// </summary>
public class WithNoLockInterceptor : DbCommandInterceptor
{
private static readonly Regex TableAliasRegex = new Regex(@"(?<tableAlias>\[dbo\].\[\w+\] AS \[Extent\d+\](?! WITH\(NOLOCK\)))", RegexOptions.Multiline | RegexOptions.IgnoreCase);
/// <summary>
/// https://www.bbsmax.com/A/8Bz8V6V65x/
/// 建議不要為標記為 ThreadStaticAttribute 的字段指定初始值,因為這樣的初始化只會發生一次,因此在類構造函數執行時只會影響一個線程。
/// 在不指定初始值的情況下,如果它是值類型,可依賴初始化為其默認值的字段,如果它是引用類型,則可依賴初始化為空引用的字段。
/// </summary>
[ThreadStatic]
public static bool Uselocking;
[ThreadStatic]
public static string CommandText;
public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
if (!Uselocking)
{
command.CommandText = TableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH(NOLOCK) ");
CommandText = command.CommandText;
}
}
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!Uselocking)
{
command.CommandText = TableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH(NOLOCK) ");
CommandText = command.CommandText;
}
}
}
然后在Global.asax.cs里面添加引用
DbInterception.Add(new WithNoLockInterceptor());

這樣全局設置,可能有些sql想帶lock,所以新建一個擴展類:
WithNoLockUtility
public static class WithNoLockUtility
{
/// <summary>
/// 部分查詢需要使用鎖查詢的,可以調用此擴展(默認全局查詢使用with(nolock))
/// 參考:https://github.com/aspnetboilerplate/aspnetboilerplate/issues/1637/
/// 示例:
/// 1、query.OrderByCustom(filters.orderFields).Select({...}).UseLocking(querable => querable.PagingAsync(filters.page, filters.rows));
/// 2、repository.EntitiesAsNoTracking.Select(...).UseLocking(item=>item.FirstOrDefaultAsync());
/// </summary>
/// <typeparam name="T"></typeparam>
/// <typeparam name="TResult"></typeparam>
/// <param name="query"></param>
/// <param name="queryAction"></param>
/// <returns></returns>
public static TResult UseLocking<T, TResult>(this IQueryable<T> query, Func<IQueryable<T>, TResult> queryAction)
{
WithNoLockInterceptor.Uselocking = true;
TResult queryableResult = default(TResult);
try
{
queryableResult = queryAction(query);
}
finally
{
WithNoLockInterceptor.Uselocking = false;
}
return queryableResult;
}
}
使用方式參考示例,不過這個方法我沒有測試使用
2. netcore集成
這個直接參考這一篇文章:https://www.cnblogs.com/weihanli/p/12623934.html
使用攔截器:
public class QueryWithNoLockDbCommandInterceptor : DbCommandInterceptor
{
private static readonly Regex TableAliasRegex =
new Regex(@"(?<tableAlias>AS \[[a-zA-Z]\w*\](?! WITH \(NOLOCK\)))",
RegexOptions.Multiline | RegexOptions.Compiled | RegexOptions.IgnoreCase);
public override InterceptionResult<object> ScalarExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<object> result)
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ScalarExecuting(command, eventData, result);
}
public override Task<InterceptionResult<object>> ScalarExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<object> result,
CancellationToken cancellationToken = new CancellationToken())
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ScalarExecutingAsync(command, eventData, result, cancellationToken);
}
public override InterceptionResult<DbDataReader> ReaderExecuting(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result)
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return result;
}
public override Task<InterceptionResult<DbDataReader>> ReaderExecutingAsync(DbCommand command, CommandEventData eventData, InterceptionResult<DbDataReader> result,
CancellationToken cancellationToken = new CancellationToken())
{
command.CommandText = TableAliasRegex.Replace(
command.CommandText,
"${tableAlias} WITH (NOLOCK)"
);
return base.ReaderExecutingAsync(command, eventData, result, cancellationToken);
}
}
然后在configservice里面添加
var services = new ServiceCollection();
services.AddDbContext<TestDbContext>(options =>
{
options
.UseLoggerFactory(loggerFactory)
.UseSqlServer(DbConnectionString)
.AddInterceptors(new QueryWithNoLockDbCommandInterceptor())
;
});
這樣就可以全局開啟with nolock了
以上均是參考網上資料寫的,mark一下防止丟失
參考:
1.https://www.cnblogs.com/weihanli/p/12623934.html
2.https://www.cnblogs.com/slyzly/articles/11452341.html
