今天在改造以前旧项目时出现了一项BUG,是由于以前不规范的EF写法所导致。异常信息如下:

"An entity object cannot be referenced by multiple instances of IEntityChangeTracker(一个实体对象不能由多个 IEntityChangeTracker 实例引用)"

这个问题其实很容易定位,是因为在程序中

使用了不同的DbContext来追踪同一个实体

以下的Demo代码可以轻松地引发该异常:

            using (var dbContext = new ADbContext())
{var aa = dbContext.ClassA.Where(p => p.Id == ).FirstOrDefault();
dbContext.Entry<ClassA>(aa).State = System.Data.Entity.EntityState.Modified;
using (var db2 = new ADbContext())
{
db2.Entry<ClassA>(aa).State = System.Data.Entity.EntityState.Modified;
dbContext.SaveChanges();
}
dbContext.SaveChanges();
}

注意ClassA一定要具有  导航属性  ,如下:

    public class ClassA

    {
public int Id { get; set; }
public string guid { get; set; }
public int? child_id { get; set; } [ForeignKey("child_id")]
public virtual ClassB child { get; set; }
}

多数场景下,一个设计好的系统中都会使用Uow(工作单元)来保证每一次请求使用同一个DbContext(也会有一些系统会需要MSDTC,不在讨论范围内),任意新建DbContext不但容易引发异常,还有可能因为释放不及时而导致内存问题。

要解决上面的这个异常很简单——使用同一个DbContext就行了。

可问题是:

为什么这个项目在以前的运行过程中没有引发错误?

探究


还是上面那个项目,我们把导航属性的 Virtual 去掉,如下:

    public class ClassA

    {
public int Id { get; set; }
public string guid { get; set; }
public int? child_id { get; set; } [ForeignKey("child_id")]
public ClassB child { get; set; }
}

运行项目,你会发现异常不见了。

究竟是什么原因?

对EF稍微了解的同学都知道,EF的导航属性默认是开启延迟加载的。

不了解的同学请搜索关键字补充一下关于EF延迟加载的基础知识,英语能力不错的同学请浏览官方文档( 关联与导航属性加载关联实体

去掉了virtual后,关闭了该导航属性的延迟加载功能,然后异常消失了。

是因为延迟加载导致的这个异常吗?

延迟加载又和IEntityChangeTracker有什么关系呢?

原因


顾名思义,其实 IEntityChangeTracker 就是用来追踪实体信息的,但令人不解的是,为什么关闭延迟加载之后,就算实体同时被两个DbContext追踪也不会报错。

来考虑一下EF的延迟加载是如何实现的,

EF使用了与Castle类似的动态代理技术,同时也存在着相同的缺陷(无法拦截没有被标识为virtual的成员)。

由于没看过EF的源码,官方文档也没有详细的说明,所以我只能推测,IEntityChangeTracker其实发挥类似拦截器的功能,

调用DbContext时,由EF产生实体的动态代理,在访问导航属性时,拦截请求访问数据库并填充导航属性。

而动态代理在产生之后,就无法在Attach到其他的DbContext中。

基于这个推测,我们可以使用一下的代码进行测试,关闭DbContext动态代理。

            using (var dbContext = new ADbContext())
{
dbContext.Configuration.ProxyCreationEnabled = false;
var aa = dbContext.ClassA.Where(p => p.Id == ).FirstOrDefault();
dbContext.Entry<ClassA>(aa).State = System.Data.Entity.EntityState.Modified;
using (var db2 = new ADbContext())
{
db2.Entry<ClassA>(aa).State = System.Data.Entity.EntityState.Modified;
dbContext.SaveChanges();
}
dbContext.SaveChanges();
}

结果通过。

为了证明该异常其实跟延迟加载没有关系,我们可以开启动态代理,然后关闭延迟加载。

            using (var dbContext = new ADbContext())
{
dbContext.Configuration.ProxyCreationEnabled = true;
dbContext.Configuration.LazyLoadingEnabled = false;
var aa = dbContext.ClassA.Where(p => p.Id == ).FirstOrDefault();
dbContext.Entry<ClassA>(aa).State = System.Data.Entity.EntityState.Modified;
using (var db2 = new ADbContext())
{
db2.Entry<ClassA>(aa).State = System.Data.Entity.EntityState.Modified;
dbContext.SaveChanges();
}
dbContext.SaveChanges();
}

异常依旧发生。

证明引发该异常的并不是延迟加载功能,而在于EF动态代理的对象只能由一个DbContext追踪。

还有一点值得一提的是:

如果一个实体没有导航属性的话,EF也不会生成它的动态代理。

EF异常探究(An entity object cannot be referenced by multiple instances of IEntityChangeTracker.)的更多相关文章

  1. EntityFramework 异常 -- An entity object cannot be referenced by multiple instances of IEntityChangeTracker

    问题      在调用 DbSet 的 Attach()  方法时(与将 Entity 设置为 EntityState.Unchanged 状态等价)报告以下错误:      An entity ob ...

  2. An entity object cannot be referenced by multiple instances of IEntityChangeTracker 的解决方案

    使用EF对建立了关系的表新增记录时出现: An entity object cannot be referenced by multiple instances of IEntityChangeTra ...

  3. An entity object cannot be referenced by multiple instances of IEntityChangeTracker.

    如果你和我一样遇到了这个问题,那么你就要检查你要操作的Model对象查询,更新操作的数据库上下文也就是DBContext是否一致.如果不一致也就是说你用AContext去查如AContext.SET& ...

  4. EBS OAF开发中的Java 实体对象(Entity Object)验证功能补充

    EBS OAF开发中的Java 实体对象(Entity Object)验证功能补充 (版权声明,本人原创或者翻译的文章如需转载,如转载用于个人学习,请注明出处:否则请与本人联系,违者必究) EO理论上 ...

  5. 记录一个EF连接查询的异常:the entity or complex type 'x' cannot be constructed in a linq to entities query

    问题解决连接:https://stackoverflow.com/questions/5325797/the-entity-cannot-be-constructed-in-a-linq-to-ent ...

  6. EF中System.Data.Entity.Internal.AppConfig的类型初始值设定项引发异常

    使用Entity的时候遇到的一个错 问题出在项目的App.config中 解决: 1.configSections要写在最顶端 2. 当中的incariantName会变成incariantNodeN ...

  7. APS.NET MVC + EF (02)---ADO.NET Entity FrameWork

    2.1 Entity Framework简介 Ado.net Entity Framework 是Microsoft推出的ORM框架. 2.1.1 什么是ORM 对象关系映射(Object Relat ...

  8. 怎么搭建EF的环境?(Entity Framework)

    1.EF是什么? EF是.net封装的一个用于数据库交互的实体层框架,它的全称是Entity Framework. 2.EF搭建: 新建之后,我们就可以看到里面的内容: 我们可以分别看一下它里面有些什 ...

  9. ASP.NET Core ActionFilter引发的一个EF异常

    最近在使用ASP.NET Core的时候出现了一个奇怪的问题.在一个Controller上使用了一个ActionFilter之后经常出现EF报错. InvalidOperationException: ...

随机推荐

  1. 201521123009 《Java程序设计》第11周学习总结

    1. 本周学习总结 2. 书面作业 本次PTA作业题集多线程 Q1:互斥访问与同步访问 完成题集4-4(互斥访问)与4-5(同步访问) 1.1 除了使用synchronized修饰方法实现互斥同步访问 ...

  2. java 课程设计 购物车系统 个人

    Q1.团队课程设计博客链接 团队博客 Q2.个人负责模块或任务说明 我主要负责main函数的编写和系统中瞎看功能代码的编写. Q3.自己的代码提交记录截图 main函数代码如下: public sta ...

  3. MyBatis学习(六)MyBatis关联映射之一对多映射

    数据库中一对多通常使用主外键关联,外键应该在多方,即多方维护关系. 下面举一个简单实例来看看MyBatis怎么处理一对多的关系. 1.创建一个项目,导入所需jar包,导入db.properties配置 ...

  4. JVM菜鸟进阶高手之路七(tomcat调优以及tomcat7、8性能对比)

    转载请注明原创出处,谢谢! 因为每个链路都会对其性能造成影响,应该是全链路的修改压测(ak大神经常说全链路!).本次基本就是局域网,所以并没有怎么优化,其实也应该考虑进去的. Linux系统参数层面的 ...

  5. PHP垃圾回收机制理解

    使用的是"引用计数"方式进行回收.简单地理解的话,就是每个分配的内存区域都有一个计数器,记录有多少个变量指针指向这片内存.当指向该片内存的指针数量为0,那么该片内存区域就可以被回收 ...

  6. node.js express mvc轻量级框架实践

    本文记录的是笔者最近抽私下时间给朋友做的一个时时彩自动下注系统,比较简单,主要也是为了学习一下node.js. 其实逻辑没什么可以深谈的,主要是想说说这套代码结构.结构如下图: js的代码比较难以维护 ...

  7. Postman高级应用——流程控制、调试、公共函数、外部数据文件

    postman客户端下载地址:https://www.getpostman.com/apps 目录 流程控制 调试 公共函数 外部数据文件 流程控制 流程控制简言之就是设置接口的执行顺序,流程控制只有 ...

  8. React——高阶组件

    1.在React中higher-order component (HOC)是一种重用组件逻辑的高级技术.HOC不是React API中的一部分.HOC是一个函数,该函数接收一个组件并且返回一个新组件. ...

  9. jenkins~集群分发功能和职责处理

    jenkins的多节点集群 在进行自动化部署时,你可以按着它们的项目类型去进行分别部署,这样即可以达到负载均衡,又可以达到一种职责的明确,比如像java的项目你可以使用linux服务来进行部署(拉代码 ...

  10. append、extend与insert的区别

    最近在自学Python语言,看到向列表增加更多数据时被append(),extend(),insert()方法绕晕了. 作为编程0基础的小白,觉得有必要自己再梳理一遍: 1.append()方法是指在 ...