11

だから私は、一種のDALとして使用されるこのEntity Frameworkプロジェクトに取り組んでおり、ストレステストを実行するとき(Thread()を介してエンティティのいくつかの更新を開始する)、これらを取得しています:

_innerException = {"トランザクション (プロセス ID 94) は、別のプロセスとのロック リソースでデッドロックされ、デッドロックの犠牲者として選択されました。トランザクションを再実行してください。"}

クラスのメソッドをどのように実装したかの例を次に示します。

public class OrderController
{

    public Order Select(long orderID)
    {
        using (var ctx = new BackEndEntities())
        {

            try
            {
                var res = from n in ctx.Orders
                                       .Include("OrderedServices.Professional")
                                       .Include("Agency")
                                       .Include("Agent")
                          where n.OrderID == orderID
                          select n;
                return res.FirstOrDefault();
            }
            catch (Exception ex)
            {
                throw ex;
            }
         }
    }

    public bool Update(Order order)
    {
        using (var ctx = new BackEndEntities())
        {
            try
            {
                order.ModificationDate = DateTime.Now;
                ctx.Orders.Attach(order);
                ctx.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}

と:

public class AgentController
{

    public Agent Select(long agentID)
    {
        using (var ctx = new BackEndEntities())
        {
            try
            {
                var res = from n in ctx.Agents.Include("Orders")
                          where n.AgentID == agentID
                          select n;
                return res.FirstOrDefault();
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }

    }

    public bool Update(Agent agent)
    {
        using (var ctx = new BackEndEntities())
        {
            try
            {
                agent.ModificationDate = DateTime.Now;
                ctx.Agents.Attach(agent);
                ctx.ObjectStateManager.ChangeObjectState(agent, System.Data.EntityState.Modified);
                ctx.SaveChanges();
                return true;
            }
            catch (Exception ex)
            {
                throw ex;
            }
        }
    }
}

明らかに、ここのコードはおそらくより良いものになる可能性がありますが、私はどちらかというと EF の初心者です。しかし、私の問題はむしろコンテキストの設計上の問題だと思います。

ここで誰かが、コンテキストが共有されていなければ、これらのデッドロックの問題に遭遇しないと述べたことを覚えています。

各メソッドでnew BackEndEntities() を使用しているため、これは「共有」されているようには見えません。より堅牢にするために何を変更する必要がありますか?

この DAL は、インターネット上で公開される Web サービスで使用されるため (クールなコード レビューの後)、どれだけストレスがかかるかを制御できず、多くの異なるインスタンスが同じエンティティを更新する可能性があります。

ありがとう!

4

3 に答える 3

9

これらのデッドロックの理由はコードではなく、デフォルトの TransactionScope 分離レベルに SERIALIZABLE を使用している EF によるものです。

SERIALIZABLE は可能な限り最も制限されたロックです。これは、デフォルトで最も制限的な分離レベルを選択していることを意味し、多くのロックが期待できます!

解決策は、実行するアクションに応じて別の TransactionScope を指定することです。EF アクションを次のように囲むことができます。

using (var scope = new TransactionScope(TransactionScopeOption.Required, new 
        TransactionOptions { IsolationLevel= IsolationLevel.Snapshot }))
{
    // do something with EF here
    scope.Complete();
}

この問題の詳細を読む:

http://blogs.msdn.com/b/diego/archive/2012/04/01/tips-to-avoid-deadlocks-in-entity-framework-applications.aspx

http://blogs.u2u.be/diederik/post/2010/06/29/Transactions-and-Connections-in-Entity-Framework-40.aspx

http://blog.aggregatedintelligence.com/2012/04/sql-server-transaction-isolation-and.html

https://serverfault.com/questions/319373/sql-deadlocking-and-timing-out-almost-constantly

于 2012-09-03T08:30:25.737 に答える
6

デッドロックの自由は、大きなシステムではかなり難しい問題です。EF 自体とは何の関係もありません。

トランザクションの有効期間を短くすると、デッドロックは減少しますが、データの不整合が発生します。以前はデッドロックしていた場所で、(通知なしに) データを破壊しています。

したがって、物理的な考慮事項ではなく、論理的なトランザクションに従って、コンテキストの有効期間とトランザクションの有効期間を選択してください。

スナップショット分離を有効にします。これにより、トランザクションの読み取りが方程式から完全に外れます。

トランザクションを書き込むには、ロックの順序を見つける必要があります。多くの場合、悲観的により高いレベルでロックする最も簡単な方法です。例: 常に顧客のコンテキストでデータを変更していますか? トランザクションの最初のステートメントとして、その顧客の更新ロックを取得します。これにより、その顧客へのアクセスをシリアル化することで、完全なデッドロックの自由が提供されます。

于 2012-08-28T20:36:57.847 に答える
2

コンテキストは、エンティティにデータベースと対話する能力を与えるものであり、コンテキストがなければ、何がどこにあるのかという概念はありません。したがって、コンテキストのスピンアップは一種の重要な作業であり、データベースなどの外部リソースを含む多くのリソースを占有します。複数のスレッドがスピンアップして同じデータベースリソースを取得しようとすると、間違いなくデッドロックになるため、問題は「新しい」コマンドだと思います。

あなたが投稿したコードは、アンチパターンのようです。ご覧のとおり、エンティティ コンテキストがスピンアップし、比較的迅速に範囲外になりますが、リポジトリ CRUD オブジェクトははるかに長い間持続しているように見えます。

私がエンティティを実装した企業が伝統的にそれを行ってきた方法は、正反対の方法です。コンテキストが作成され、アセンブリがデータベースを必要とする限り保持され、リポジトリ CRUD オブジェクトが作成され、マイクロ秒で消滅します。

コンテキストが共有されていないというあなたの主張をどこから得たのかはわかりませんので、どのような状況で言われたのかわかりませんが、アセンブリ間でコンテキストを共有してはならないことは絶対に真実です。 同じアセンブリの中で、コンテキストを起動するのに必要なリソースの数と、起動にかかる時間がない理由がわかりません。エンティティ コンテキストは非常に重いので、現在のコードをシングル スレッド化して機能させるとしたら、まったくひどいパフォーマンスが見られると思います。

したがって、代わりにこれをリファクタリングしてCreate(BackEndEntites context)andUpdate(BackEndEntities context)を作成し、マスター スレッド (これらすべての子スレッドを作成するスレッド) に BackEndEntities コンテキストを作成して維持させ、その子に渡すことをお勧めします。また、AgentControllers とOrderControllers を使い終わったらすぐに削除し、メソッドの外で決して再利用しないようにしてください。Ninject や StructureMap などの制御フレームワークの適切な反転を実装すると、これがはるかに簡単になります。

于 2012-08-28T20:30:28.777 に答える