1

私のEF4プログラムには、申請者と申請者のテーブルがあります。プログラムの複数のインスタンスが定期的に実行され、いくつかのビジネスロジックに基づいて申請者向けのアプリケーションが作成されます。アプリケーションテーブルに、申請者の送信済み/送信済みレコードを複数含めることはできません。

これが、Submited/BeingSubmittedアプリケーションがあるかどうかをチェックして挿入するコードです。これは、申請者のリストのforeachループ内で実行されます。

public Application SaveApplication(Int32 applicantId)
    {
        using (TransactionScope txScope = new TransactionScope(TransactionScopeOption.RequiresNew))
        {
            if (ApplicantHasPendingApplication(applicantId))
                return null;

            Application app = null;
            try
            {
                app = new Application()
                {
                    // Create the object...
                };

                _unitOfWork.DisclosureApplications.Add(app);
                _unitOfWork.Commit();
                _unitOfWork.Refresh(app); // We save and refresh it to get the Id.

                txScope.Complete();
            }
            catch (UpdateException ex)
            {
                // We get an Update exception here when multiple instances tries to insert Application.
            }

            return app;
        }
    }

上記のコードは、プログラムの複数のインスタンスの実行中にUpdateExceptionをスローするという事実を除けば、重複レコードの挿入を防ぎます。その例外を飲み込んで続行すれば、すべて問題ありません。

ただし、上記のコードを並行してテスト/実行しようとしましたが、データベースに重複するレコードが挿入されます。

Parallel.Invoke(
            () => CreateApplications("Parallel Instance 1"),
            () => CreateApplications("Parallel Instance 2"));

private void CreateApplications(String dummyInstanceName)
{
   var unitOfWork = new SqlUnitOfWork();
   var applicants = unitOfWork.Applicants.FindAll().Take(100).ToList();

   var facade = new ProviderFacade(unitOfWork, new Log4NetLogger(dummyInstanceName));

   foreach (Applicant applicant in applicants)
            {
                facade.ApplicationProvider.SaveApplication(applicant.applicantID);
            }
}

上記のコードでは、UpdateExceptionをスローし、申請者に複数のアプリケーション行を挿入します。

テーブルには代理主キーのみがあり、他の一意の制約はないことに注意してください。

私の質問は、TransactionScopeがParallel.Invokeで実行して重複行を挿入するのに、プログラムの複数のインスタンスを起動したときに挿入しないのはなぜですか?それを達成するための健全なアプローチは何でしょうか?

更新:SqlUnitOfWorkのコンストラクターは

public SqlUnitOfWork()
    {
        _context = new MyEntities();
    }

MyEntitiesのコンストラクターはEFによって生成されます-

    public const string ConnectionString = "name=Entities";
    public const string ContainerName = "Entities";

    public TPIEntities() : base(ConnectionString, ContainerName)
    {
        this.ContextOptions.LazyLoadingEnabled = true;
    }

ありがとう。

4

1 に答える 1

0

これは古典的な競合状態です。両方のトランザクションは、既存のものApplicationが存在するかどうかを確認します。どちらも存在しないことがわかります。次に、両方が挿入を試みます。

UPDLOCK, HOLDLOCKアプリケーションをロックするか、存在するかどうかを確認するときにSQL Serverを使用して、確認を同期する必要がありますApplication

于 2012-07-12T10:31:03.317 に答える