web-dev-qa-db-ja.com

NOLOCKを使用したエンティティフレームワーク

Entity FrameworkでNOLOCK関数を使用するにはどうすればよいですか? XMLがこれを行う唯一の方法ですか?

134
OneSmartGuy

いいえ。ただし、トランザクションを開始し、 コミットされていない状態を読み取るための分離レベル を設定できます。これは基本的に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();
}
203
Doctor Jones

拡張メソッドはこれを簡単にすることができます

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;
    }
}
81
Alexandre

全般的に何かが必要な場合、毎回実際にトランザクションスコープを開始するよりも邪魔にならないことがわかった最良の方法は、次の簡単なコマンドを実行してオブジェクトコンテキストを作成した後、接続にデフォルトのトランザクション分離レベルを設定することです:

this.context.ExecuteStoreCommand("SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;");

http://msdn.Microsoft.com/en-us/library/aa259216(v = sql.80).aspx

この手法を使用すると、コンテキストを作成し、すべてのコンテキストに対してこのコマンドを実際に実行する単純なEFプロバイダーを作成して、デフォルトで常に「コミットされていない読み取り」を行うことができました。

26
Frank.Germain

Read Uncommittedトランザクション分離レベルを使用することが最良の選択であることに絶対に同意しましたが、マネージャーまたはクライアントの要求によって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;
19

Doctor Jones の受け入れられた回答と 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などの他のデータベースシステムに接続する場合は機能しません。

9
myuce

これを回避するには、データベースにビューを作成し、ビューのクエリにNOLOCKを適用します。次に、ビューをEF内のテーブルとして扱います。

6
Ryan Galloway

いいえ、そうではありません-Entity Frameworkは基本的に、実際のデータベースよりもかなり厳密なレイヤーです。クエリはESQL-Entity SQL-で定式化されます。これはまずエンティティモデルを対象としています。EFは複数のデータベースバックエンドをサポートしているため、「ネイティブ」SQLをバックエンドに直接送信することはできません。

NOLOCKクエリヒントはSQL Server固有のものであり、サポートされている他のデータベースでは機能しません(同じヒントも実装していない限り、これは強く疑います)。

マーク

2
marc_s

EF6の導入により、Microsoftは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
}
2
Ali

1つのオプションは、ストアドプロシージャを使用して(Ryanが提案したビューソリューションと同様)、EFからストアドプロシージャを実行することです。このように、EFは結果をパイプするだけで、ストアドプロシージャはダーティリードを実行します。

0
Rafiki