Ara*_*ash 36 .net c# sql sql-server entity-framework
我在MVC 5项目中使用Entity Framework 6.如您所知,SELECT如果我们使用SQL Server中的查询,它们的执行速度更快,效率更高WITH (NOLOCK).我查看了Entity Framework 6生成的一些SQL SELECT语句,并意识到它们都不包含NOLOCK.
我不想在我的提取操作中使用事务来读取未提交的事务.
如何在下面生成的SELECT语句中强制使用EF 6来使用NOLOCK?
cod*_*orx 50
首先......你永远不应该为每个SQL语句使用NOLOCK.它可能会损害数据的完整性.
这就像任何其他查询提示你应该只在你做一些与众不同的事情时才使用的机制.
无法告诉EF Provider呈现NoLock提示.如果您确实需要读取未提交的数据,则可以使用以下选项.
编写自己的EntityFramework提供程序.
使用Command Interceptor在语句执行之前修改它.http://msdn.microsoft.com/en-us/data/dn469464.aspx
将TransactionScope与IsolationLevel.ReadUncommited一起使用.
我知道你说你不想使用事务,但它是读取未提交数据的唯一开箱即用方式.此外,它不会产生太多开销,因为SQL Server中的每个语句都"隐式"在事务中运行.
using (new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions
{
IsolationLevel = IsolationLevel.ReadUncommitted
}))
{
using (var db = new MyDbContext()) {
// query
}
}
Run Code Online (Sandbox Code Playgroud)
编辑: 重要的是要注意NOLOCK for Updates和Deletes(选择保持不变)已被Microsoft从SQL Server 2016中弃用,并且将在'a'将来的版本中删除.
Cem*_*tlu 10
您可以使用不为每个查询使用事务范围的变通方法.如果运行下面的代码,ef将对同一服务器进程ID使用相同的事务隔离级别.由于服务器进程ID在同一请求中不会更改,因此每个请求只能调用一次就足够了.这也适用于EF Core.
this.Database.ExecuteSqlCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
Run Code Online (Sandbox Code Playgroud)
我同意codeworx所说的Read Uncommitted可能很危险.如果你能忍受脏读,那就去吧.
我找到了一种方法来完成这项工作而不改变当前查询中的任何内容.
你需要像这样创建一个DbCommandInterceptor:
public class IsolationLevelInterceptor : DbCommandInterceptor
{
private IsolationLevel _isolationLevel;
public IsolationLevelInterceptor(IsolationLevel level)
{
_isolationLevel = level;
}
//[ThreadStatic]
//private DbCommand _command;
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
SetTransaction(command);
}
public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
SetTransaction(command);
}
public override void NonQueryExecuting(DbCommand command, DbCommandInterceptionContext<int> interceptionContext)
{
SetTransaction(command);
}
private void SetTransaction(DbCommand command)
{
if (command != null)
{
if (command.Transaction == null)
{
var t = command.Connection.BeginTransaction(_isolationLevel);
command.Transaction = t;
//_command = command;
}
}
}
}
Run Code Online (Sandbox Code Playgroud)
然后在cctor(dbcontext的静态构造函数)中,只需将拦截器添加到实体框架集合的DbInfrastructure中.
DbInterception.Add(new IsolationLevelInterceptor());
Run Code Online (Sandbox Code Playgroud)
对于EF发送到商店的每个命令,这将包含具有该隔离级别的事务.
在我的情况下运作良好,因为我们通过API写入数据,其中数据不是基于数据库的读数.(由于脏读,数据可能会被破坏),所以工作正常.
在我们的项目中,我们结合了@Cem Mutlu 和@anotherNeo 建议的第二个和第三个解决方案。
Sql Profiler 的实验表明我们必须使用一对命令:
因为 NET 通过SqlConnectionPool重用连接
internal class NoLockInterceptor : DbCommandInterceptor
{
public static readonly string SET_READ_UNCOMMITED = "SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED";
public static readonly string SET_READ_COMMITED = "SET TRANSACTION ISOLATION LEVEL READ COMMITTED";
public override void ScalarExecuting(DbCommand command, DbCommandInterceptionContext<object> interceptionContext)
{
if (!interceptionContext.DbContexts.OfType<IFortisDataStoreNoLockContext>().Any())
{
return;
}
ExecutingBase(command);
}
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!interceptionContext.DbContexts.OfType<IFortisDataStoreNoLockContext>().Any())
{
return;
}
ExecutingBase(command);
}
private static void ExecutingBase(DbCommand command)
{
var text = command.CommandText;
command.CommandText = $"{SET_READ_UNCOMMITED} {Environment.NewLine} {text} {Environment.NewLine} {SET_READ_COMMITED}";
}
}
Run Code Online (Sandbox Code Playgroud)