我有一个业务案例,对于给定源表中的每个记录,需要对不同表中的每个记录进行一些更改,并且每个sourceTable记录都需要单独处理。
所以我有以下pseodocode:
MyEntityFrameworkContext ctx;
foreach (sourceRecord sr in ctx.sourceTable)
{
try
{
using (MyEntityFrameworkContext tctx = new MyEntityFramworkContext)
{
string result1 = MakeUpdatesToSomeOtherTable1(tctx);
sr.Result1 = result1;
string result2 = MakeUpdatesToSomeOtherTable2(tctx);
sr.Result2 = result2;
// will be more tables here.
using (TransactionScope ts = new TransactionScope)
{
tctx.SaveChanges; // to save changes made to OtherTable1 and OtherTable2
tctx.ExecuteStoreCommand("SQL that makes a few other changes related to sourceRecord to tables that are NOT in the EF context");
ts.Complete();
}
}
}
catch (Exception ex)
{
sr.ExceptionResult = ex.Message();
}
});
ctx.SaveChanges(); // to save all changes made to sourceTable.
循环中tctx和TransactionScope的原因是我需要对一个事务中保存对OtherTables1& 2的更改,并为每个正在处理的sourceRecord调用tctx.ExecuteStoreCommand()。
另请注意,我需要保存写入sourceTable的结果,而不是对TransactionScope中更新的表所做的更改。因此,我不能在同一个TransactionScope中包含sourceTable的更新,因为如果txn回滚,我将不会有异常记录。这样,在整个过程结束时,我可以看到哪些sourceRecords失败,哪些成功。
上述伪代码效果很好。
但是,我想在这里利用并行性,并将foreach转换为Parallel.ForEach()。但后来我遇到了非常意外的错误(比如在调用ts.Complete()之后的TransactionAbortedException,调用ctx.SaveChanges()时的NullReferenceException,或者在设置sourceRecord的Result属性之一时,我有时会得到InvalidOperationException:EntityMemberChanged或EntityComplexMemberChanged被调用而没有首先使用相同的属性名称在同一个更改跟踪器上调用EntityMemberChanging或EntityComplexMemberChanging。
所以我认为并行性虽然对于QUERIES来说是完美的,但它并不适合EntityFramwork中的数据更新?我错过了什么,或者不了解并行性?我不明白为什么我的上述方法在转换为使用并行性时会中断。任何建议将不胜感激。
答案 0 :(得分:1)
EF对象上下文不是线程安全的,因此当多个线程在同一个上下文中嗡嗡作响时,很有可能发生灾难性错误。
看起来你在foreach循环之外至少有一个上下文对象,并在线程之间共享。
根据您的描述,我猜测来自多个线程的sourceRecord实体的更新属性正在破坏上下文中的某些内部状态 - 可能是它为变更跟踪维护的数据集合。
答案 1 :(得分:0)
并行性只会为那些受CPU限制的操作带来好处,在你的情况下,你所做的一切似乎都是IO操作(数据库更新等),所以我认为你不能从中获得任何好处平行。但是如果此代码中的操作受CPU限制,则可以使它们与每次更新并行,但更新将是顺序的。