在我的 EF4 程序中,我有一个申请人和申请表。该程序的多个实例会定期运行,以根据某些业务逻辑为申请人创建应用程序。在申请表中,我不能为申请人拥有多个已提交/正在提交的记录。
所以这是一段代码,它检查是否有一个已提交/正在提交的应用程序并插入它。它在申请者列表的 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;
}
谢谢。