您的位置:首页 > 其它

一例一个实体对象不能由多个 IEntityChangeTracker 实例引用的解决

2017-05-09 10:52 423 查看
前几日在检查日志的时候发现系统偶尔会出现 Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded 的错误,经过查找资料,是因为随着访问量的增多,并发增多造成。解决办法从微软的msdn上找到了想改解决办法。

Entity Framework Optimistic Concurrency Patterns  这里给出了微软这篇文章的链接,我采用的是微软提出的第一种解决办法:Resolving optimistic concurrency exceptions with Reload   其中 update 方法的写法修改成这样:

public bool Update(T entity)
{
using (var dbContext = new F())
{

bool saveFailed;
do
{
saveFailed = false;

try
{

//把entity附加到当前dbContext
dbContext.Set<T>().Attach(entity);
dbContext.Entry<T>(entity).State = EntityState.Modified;

return dbContext.SaveChanges() > 0;
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update the values of the entity that failed to save from the store
ex.Entries.Single().Reload();
}

} while (saveFailed);

return false;
}

}


当然,添加和删除的相应方法也做了同样的修改,修改后对相应的方法做测试,没有发现什么问题。于是就上线了。因为并发效果在本地没法实现,实际效果需要上线后观察。过了个周末,然后周一来检查执行的效果,Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded 的错误没出现,但是发现大量的 一个实体对象不能由多个
IEntityChangeTracker 实例引用的错误。通过定位,是update方法出现了问题。网上搜索这个错误出现的原因,搜索到的大部分是说由于主从表的关系导致此问题,而且说的都云里雾里,看不懂他们说的意思。但是虽然出错的地方刚好有主从表,但我明白,问题恐怕不是这里。因为这个错误大量出现是在我更改了update方法后才出现的。通过分析我更改的update方法前后的变化,更改前update方法传入的entity实例和update方法SaveChanges 共用的是一个dbcontext, 而更改后,由于使用了
using new 了一个新的 dbcontext,这两个 dbcontext 不一样了,怀疑是这里的问题。而通过把 using 去掉,也真解决了这个问题,不出现 一个实体对象不能由多个 IEntityChangeTracker 实例引用的错误了。但是这样的解决不能令人满意。因为没有理论支撑。由于百度和google都没有找到令人满意的解释,于是把目光投向了 stackoverflow 网站,这个网站还是有许多高手,一些解释感觉很深刻。 通过不停的变换关键词搜索,终于找到了两篇文章解释的比较清楚。我总结了下我这个问题出现的原因是这样的,看这两行代码:

var examList = es2.Get(m => m.ExamMainGID == examMain.GID && m.QuestionLibraryGID == gid).FirstOrDefault();

es2.Update(examList); 

第一行代码es2.Get方法使用的数据连接是_dbContext, entity framework 每一个查询方法都会自动创建一个 IEntityChangeTracker 的跟踪,这个跟踪是使用的_dbContext连接创建的,他会跟踪exmalList的实例状态变化,比如状态的修改,删除等。而在执行 es2.Update(examList);  这个方法的时候,由于Update方法的实现是 using 了一个新的dbContext: using (var dbContext = new F()),
在这句:dbContext .Set<T>().Attach(entity); 就是把 entity附加到了新的dbContext对象中。按ef的规则,这里是需要创建一个新的 IEntityChangeTracker  跟踪器,就是这行出现了错误。原因就是一个实例的跟踪器只能有一个,在这里这个实例就是examList 。在stackoverflow 网站的这两篇文章中也给出了解决办法。有两种,第一种是公用一个数据连接dbContext,就是我以前的解决办法,不再重新new 一个新的数据连接上下文,而仍然使用es2.Get方法使用的数据连接。另外一种解决办法就属于场合用法了。即把通过
es2.Get方法获取的实例 examList 同_dbContext 剥离开来,然后再把exmaList对象附加到新的 dbContext 对象中。具体的写法是这样的:

//从_dbContext中分离entity
((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);
//把entity附加到当前dbContext
dbContext.Set<T>().Attach(entity);
dbContext.Entry<T>(entity).State = EntityState.Modified;

return _dbContext.SaveChanges() > 0;


这里的((IObjectContextAdapter)_dbContext).ObjectContext.Detach(entity);就是用 Detach 把 entity从 _dbContext上下文数据连接剥离出来。剥离后再附加到新的数据连接中就不会再出现错误。这个方法是不推荐的。如果每次都这样采用剥离的方法,这是增加代码量,共用Context 数据连接才是正确的方法。

这里给出两篇stakoverflow的连接,有兴趣的可以看下:

http://stackoverflow.com/questions/10191734/entity-object-cannot-be-referenced-by-multiple-instances-of-ientitychangetracker

http://stackoverflow.com/questions/12112360/c-sharp-entity-framework-ientitychangetracker-issue
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐