NOLOCK
Entity Frameworkで関数を使用するにはどうすればよいですか?これを行う唯一の方法はXMLですか?
NOLOCK
Entity Frameworkで関数を使用するにはどうすればよいですか?これを行う唯一の方法はXMLですか?
回答:
いいえ。ただし、トランザクションを開始し、分離レベルをコミットされていない読み取りに設定できます。これは基本的に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/en-us/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...
コマンドは、接続レベルのプロパティに影響を及ぼし、したがってクエリヒントによって上書きされない限り、前方に(即ち接続)、その時点から作られたすべてのSQL文に影響を与えます。この動作は、少なくともSQL Server 2000以降、およびおそらく以前から存在しています。
CREATE TABLE ##Test(Col1 INT); BEGIN TRAN; SELECT * FROM ##Test WITH (TABLOCK, XLOCK);
。別のクエリを開き(#2)、次を実行しますSELECT * FROM ##Test;
。SELECTは、排他ロックを使用しているタブ#1でまだ開いているトランザクションによってブロックされているため、戻りません。#2でSELECTをキャンセルします。SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
タブ#2で1回実行します。タブ#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); }
強化ドクター・ジョーンズの受け入れ答えと使用してPostSharpを。
最初の「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の導入により、BeginTransaction()メソッドを使用することをお勧めします。
EF6 +およびEF Coreでは、TransactionScopeの代わりにBeginTransactionを使用できます
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
}
いいえ、本当ではありません-Entity Frameworkは基本的に、実際のデータベースの上にあるかなり厳密なレイヤーです。クエリはESQL-エンティティSQL-で最初に作成され、エンティティモデルを対象としています。EFは複数のデータベースバックエンドをサポートしているため、「ネイティブ」SQLをバックエンドに直接送信することはできません。
NOLOCKクエリヒントはSQL Server固有のものであり、他のサポートされているデータベースでは動作しません(同じヒントが実装されていない限り、これは疑わしいです)。
マーク
Database.ExecuteSqlCommand()
またはを使用して「ネイティブ」SQLを実行できますDbSet<T>.SqlQuery()
。
(NOLOCK)
とにかく-を参照してキックに悪い習慣を-どこでもNOLOCKを置く -されて推奨しませんどこでもこれを使用するために-かなり反対!