如何NOLOCK
在实体框架上使用该功能?XML是做到这一点的唯一方法吗?
如何NOLOCK
在实体框架上使用该功能?XML是做到这一点的唯一方法吗?
Answers:
否,但是您可以启动事务并将隔离级别设置为uncommited。这本质上与NOLOCK相同,但是它不是针对每个表执行此操作,而是针对事务范围内的所有操作执行此操作。
如果这听起来像您想要的,那么您可以按照以下方法进行操作...
//declare the transaction options
var transactionOptions = new System.Transactions.TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new System.Transactions.TransactionScope(
System.Transactions.TransactionScopeOption.Required,
transactionOptions)
)
//declare our context
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
//don't forget to complete the transaction scope
transactionScope.Complete();
}
扩展方法可以使此操作更容易
public static List<T> ToListReadUncommitted<T>(this IQueryable<T> query)
{
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
List<T> toReturn = query.ToList();
scope.Complete();
return toReturn;
}
}
public static int CountReadUncommitted<T>(this IQueryable<T> query)
{
using (var scope = new TransactionScope(
TransactionScopeOption.Required,
new TransactionOptions() {
IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted }))
{
int toReturn = query.Count();
scope.Complete();
return toReturn;
}
}
如果您需要大量的东西,最好的方法是,在每次创建对象上下文之后,通过运行以下简单命令,简单地在连接上设置默认的事务隔离级别:
this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");
http://msdn.microsoft.com/zh-CN/library/aa259216(v=sql.80).aspx
使用这种技术,我们能够创建一个简单的EF提供程序,该提供程序为我们创建上下文,并为所有上下文每次实际运行此命令,因此默认情况下我们始终处于“未提交读”状态。
Transactions running at the READ UNCOMMITTED level do not issue shared locks
。这意味着您必须在事务内运行才能获得好处。(摘自msdn.microsoft.com/en-gb/library/ms173763.aspx)。您的方法可能不会那么麻烦,但是如果您不使用事务,它将无法实现任何目标。
SET TRANSACTION ISOLATION LEVEL...
命令会影响连接级别的属性,因此会影响从该点开始(对于THAT连接)所做的所有SQL语句,除非被查询提示覆盖。至少从SQL Server 2000开始,并且可能在更早之前,这种现象就已经存在。
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
。打开另一个查询(#2)并运行:SELECT * FROM ##Test;
。由于使用排他锁的选项卡#1中仍处于打开状态的事务阻止了SELECT,因此SELECT不会返回。取消#2中的SELECT。SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
在标签#2中运行一次。再次在选项卡#2中再次运行SELECT,它将返回。确保ROLLBACK
在选项卡#1中运行。
尽管我绝对同意使用“读取未提交的事务隔离级别”是最佳选择,但是一段时间后,您不得不根据经理或客户的请求而强制使用NOLOCK提示,并且没有理由反对接受此请求。
使用Entity Framework 6,您可以像这样实现自己的DbCommandInterceptor:
public class NoLockInterceptor : DbCommandInterceptor
{
private static readonly Regex _tableAliasRegex =
new Regex(@"(?<tableAlias>AS \[Extent\d+\](?! WITH \(NOLOCK\)))",
RegexOptions.Multiline | RegexOptions.IgnoreCase);
[ThreadStatic]
public static bool SuppressNoLock;
public override void ScalarExecuting(DbCommand command,
DbCommandInterceptionContext<object> interceptionContext)
{
if (!SuppressNoLock)
{
command.CommandText =
_tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
}
}
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext)
{
if (!SuppressNoLock)
{
command.CommandText =
_tableAliasRegex.Replace(command.CommandText, "${tableAlias} WITH (NOLOCK)");
}
}
}
使用此类,您可以在应用程序启动时应用它:
DbInterception.Add(new NoLockInterceptor());
并有条件地关闭NOLOCK
在当前线程的查询中添加提示的操作:
NoLockInterceptor.SuppressNoLock = true;
public override void ReaderExecuting(DbCommand command, DbCommandInterceptionContext<DbDataReader> interceptionContext) { if (!SuppressNoLock) command.CommandText = $"SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;{Environment.NewLine}{command.CommandText}"; base.ReaderExecuting(command, interceptionContext); }
第一个“ ReadUncommitedTransactionScopeAttribute ”
[Serializable]
public class ReadUncommitedTransactionScopeAttribute : MethodInterceptionAspect
{
public override void OnInvoke(MethodInterceptionArgs args)
{
//declare the transaction options
var transactionOptions = new TransactionOptions();
//set it to read uncommited
transactionOptions.IsolationLevel = IsolationLevel.ReadUncommitted;
//create the transaction scope, passing our options in
using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, transactionOptions))
{
//declare our context
using (var scope = new TransactionScope())
{
args.Proceed();
scope.Complete();
}
}
}
}
那只要你需要
[ReadUncommitedTransactionScope()]
public static SomeEntities[] GetSomeEntities()
{
using (var context = new MyEntityConnection())
{
//any reads we do here will also read uncomitted data
//...
//...
}
}
能够使用拦截器添加“ NOLOCK”也很好,但是在连接到像Oracle这样的其他数据库系统时将不起作用。
为了解决这个问题,我在数据库上创建了一个视图,并将NOLOCK应用于该视图的查询。然后,我将视图视为EF中的表格。
随着EF6的引入,Microsoft建议使用BeginTransaction()方法。
您可以在EF6 +和EF Core中使用BeginTransaction代替TransactionScope
using (var ctx = new ContractDbContext())
using (var transaction = ctx.Database.BeginTransaction(System.Data.IsolationLevel.ReadUncommitted))
{
//any reads we do here will also read uncommitted data
}
不,不是真的-实体框架基本上是您实际数据库之上的一个相当严格的层。您的查询以ESQL-实体SQL编写,它首先针对您的实体模型,并且由于EF支持多个数据库后端,因此您无法真正将“本机” SQL直接发送到后端。
NOLOCK查询提示是SQL Server特有的,不能在任何其他受支持的数据库上运行(除非它们也实现了相同的提示-我对此表示强烈怀疑)。
马克
Database.ExecuteSqlCommand()
或执行“本机” SQL DbSet<T>.SqlQuery()
。
(NOLOCK)
-见坏习惯-将NOLOCK放到任何地方 - 不建议到处使用它-恰恰相反!