2012-07-12 66 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