2012-07-12 78 views
1

在我的EF4程序中,我有一個申請人和應用程序表。該程序的多個實例會定期運行,以根據某些業務邏輯爲應用程序創建應用程序。在申請表中,我不能爲申請人提供多個提交/正在提交的記錄。parallel.invoke事務範圍實體框架

因此,這裏的一塊,它檢查是否有一個提交/ 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併爲申請人插入多個Application行。

請注意,該表只有一個代理主鍵,沒有其他唯一性約束。

我的問題是:爲什麼TransactionScope的由Parallel.Invoke運行它插入重複行,但不是當我關火程序的多個實例?實現它的方法是什麼?

更新:SqlUnitOfWork的構造函數是

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

由EF產生MyEntities的構造函數 -

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

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

感謝。

+0

當您調用facade.ApplicationProvider.SaveApplication時,正在使用什麼_unitOfWork?我沒有看到在CreateApplications中創建的SqlUnitOfWork如何通過那裏。如果您的兩個CreateApplications實例最終有效使用相同的SqlUnitOfWork,那可能與您的問題有關。 – 2012-07-12 09:50:15

+0

請參閱更新。還添加了創建外觀的線。當我在CreateApplications中創建一個新的SqlUnitOfWork時,在並行方法調用中使用相同UnitOfWork的可能性是什麼? (我是並行編程的新手。) – user1520015 2012-07-12 10:13:39

回答

0

這是一個典型的競賽條件。兩筆交易都會檢查是否存在預先存在的Application。兩者都發現沒有。然後,都嘗試插入。

您需要通過鎖定應用程序或通過在檢查Application是否存在時採用SQL Server UPDLOCK, HOLDLOCK來同步您的檢查。

+0

我在這裏使用EF,那麼如何告訴Sql Server執行UPDLOCK和HOLDLOCK? – user1520015 2012-07-12 11:37:13

+0

您需要使用ExecuteStoreCommand發送一個(簡單)的SQL語句(http://msdn.microsoft.com/en-us/library/system.data.objects.objectcontext.executestorecommand.aspx), – usr 2012-07-12 11:38:24

+0

謝謝,這是我一直在尋找。 – user1520015 2012-07-12 14:27:55