在我的EF4程序中,我有一个申请人和申请表。该程序的多个实例定期运行,以基于某些业务逻辑为申请人创建应用程序。在Application表中,我不能为申请人提供多个Submitted / BeingSubmitted记录。
所以这里是检查是否有Submitted / 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的ctor是
public SqlUnitOfWork()
{
_context = new MyEntities();
}
MyEntities的ctor由EF生成 -
public const string ConnectionString = "name=Entities";
public const string ContainerName = "Entities";
public TPIEntities() : base(ConnectionString, ContainerName)
{
this.ContextOptions.LazyLoadingEnabled = true;
}
感谢。
答案 0 :(得分:0)
这是一个经典的竞争条件。两个事务都检查是否存在预先存在的Application
。两者都发现没有。然后,两者都试图插入。
您需要通过锁定应用程序或在检查UPDLOCK, HOLDLOCK
是否存在时使用SQL Server Application
来同步您的检查。