我们在使用Entity Framework进行CRUD时,为了提升查询效率,一般均会启动NoTracking,即不追踪变化,设置代码如下:

  1. //这是DB First模式下设置方法:
  2. aTestEntities db = new aTestEntities();
  3. db.Companies.MergeOption = MergeOption.NoTracking;
  4.  
  5. //这是CODE First及Model First模式下设置方法:
  6. aTestEntities db = new aTestEntities();
  7. db.Companies.AsNoTracking();

虽然启动NoTracking,查询效率提高了,但我们在进行CUD时,有时又会出现如下之类的报错:

无法附加此对象,因为它已经在对象上下文中。对象只有在处于未更改状态时才能重新附加。

因为查询时启用了NoTracking,即表明查询的实体对象是处于Detached,我们再进行CRD时,必须先进行Attach操作,然后才能执行相应的增加、更新、删除操作,但由于在有些情况下我们并不能保证需要进行CRD的实体为Detached,所以易造成重复Attach,从而导致报上面的错误或其它错误。

若要避免重复Attach,我们则必需要有一个能够判断实体的状态是否为Attach,如果已Attached,我们就不需要再进行Attach操作,EF中并没有这类的方法,所以我这里总结了如下几个方案(IsAttached方法),可以避免此类问题的发生:

方案一:采用DB First时,由于实体类均继承自EntityObject,所以我们可以通过EntityObject.EntityKey属性来进行判断

  1. /// <summary>
  2. /// 判断entity是否已经Attached
  3. /// </summary>
  4. /// <param name="entity"></param>
  5. /// <returns></returns>
  6. public bool IsAttached<TEntity>(TEntity entity) where TEntity : EntityObject
  7. {
  8. ObjectStateEntry entry = null;
  9. if (dbContext.ObjectStateManager.TryGetObjectStateEntry(entity.EntityKey, out entry))
  10. {
  11. if (entry.State != EntityState.Detached)
  12. {
  13. return true;
  14. }
  15. }
  16. return false;
  17. }

方案二:采用Model First或Code First时,由于实体类为我们自己设计的,默认并没有继承自EntityObject,所以就不能使用上面的方法,但我们可以以方案一中的思想,来设计实体类,我们可以定义一个接口IEntityWithId,然后让所有的实体类均实现该接口,最后再改写方案一的方法即可完成

  1. public interface IEntityWithId
  2. {
  3. Guid Id { get; set; }
  4. }
  5.  
  6. public class EntityClass : IEntityWithId
  7. {
  8. Guid Id { get; set; }
  9.  
  10. //...其它属性
  11. }
  12.  
  13. /// <summary>
  14. /// 判断entity是否已经Attached
  15. /// </summary>
  16. /// <typeparam name="TEntity"></typeparam>
  17. /// <param name="entity"></param>
  18. /// <returns></returns>
  19. public bool IsAttached<TEntity>(TEntity entity) where TEntity : IEntityWithId
  20. {
  21. TEntity localEntity = dbContext.Set<TEntity>().Local.Where(t => t.Id == entity.Id).FirstOrDefault();
  22. if (localEntity != null)
  23. {
  24. if (dbContext.Entry(localEntity).State != EntityState.Detached)
  25. {
  26. return true;
  27. }
  28. }
  29. return false;
  30. }

方案三:采用Model First或Code First时,若没有定义统一的接口,那么我们就不能使用方案二中的IsAttached方法,这时该怎么办呢?通过VS Debug时浏览实体对象发现,实体的类型并不是我们所定义的类型,而是变成了EntityWrapperWithoutRelationships<TEntity>,该类中有一个公共字段属性:_entityWrapper,然后继续查看该字段的类型,又发现了EntityWrapper类,该类中就有了EntityKey属性,该属性与方案一中的EntityObject.EntityKey属性类型相同,如果我们能够获取到该EntityKey属性,那么就可以使用方案一中的方法进行判断了,但高兴之余又发现,EntityWrapperWithoutRelationships及EntityWrapper类的访问修饰符为internal,意味着我们并不能在自己的项目中直接使用,唯一的办法就是采用反射来动态获取该属性,所以整个的实现方法如下:

  1. /// <summary>
  2. /// 判断entity是否已经Attached
  3. /// </summary>
  4. /// <param name="entity"></param>
  5. /// <returns></returns>
  6. private bool IsAttached(TEntity entity)
  7. {
  8. var objectContext = ((IObjectContextAdapter)this.baseContext).ObjectContext;
  9. ObjectStateEntry entry = null;
  10. if (objectContext.ObjectStateManager.TryGetObjectStateEntry(GetEntityKey(entity), out entry))
  11. {
  12. if (entry.State != EntityState.Detached)
  13. {
  14. return true;
  15. }
  16. }
  17. return false;
  18. }
  19.  
  20. /// <summary>
  21. /// 通过反射获取实体对象的EntityKey
  22. /// </summary>
  23. /// <param name="entity"></param>
  24. /// <returns></returns>
  25. private EntityKey GetEntityKey(TEntity entity)
  26. {
  27. var entityWrapper = entity.GetType().GetField("_entityWrapper").GetValue(entity);//获取字段_entityWrapper的值
  28. var entityWrapperType = entityWrapper.GetType();//获取字段的类型
  29.  
  30. var entityKey = entityWrapperType.GetProperty("EntityKey").GetValue(entityWrapper, null);//获取EntityKey属性的值
  31.  
  32. return (EntityKey)entityKey;
  33. }

实现了IsAttached方法后,那么我们就再也不用担心出现重复Attach的情况,使用方法很简单,只需要在需要进行更新、删除操作时前调用IsAttached方法判断一下实体是否为Attached,若不是才Attach,否则忽略,代码示例如下:

  1. public virtual void Update(TEntity entity, bool autoCommit = false)
  2. {
  3. this.ValidateEntity(entity, false);
  4. if (!this.IsAttached(entity))
  5. {
  6. this.objectSet.Attach(entity);
  7. this.baseContext.Entry(entity).State = EntityState.Modified;
  8. }
  9. if (autoCommit)
  10. {
  11. this.Commit();
  12. }
  13. }
  14.  
  15. public virtual void Remove(TEntity entity, bool autoCommit = false)
  16. {
  17. if (!this.IsAttached(entity))
  18. {
  19. this.objectSet.Attach(entity);
  20. }
  21. this.objectSet.Remove(entity);
  22. if (autoCommit)
  23. {
  24. this.Commit();
  25. }
  26. }

关于Entity Framework中的Attached报错的完美解决方案的更多相关文章

  1. 关于Entity Framework中的Attached报错的完美解决方案终极版

    之前发表过一篇文章题为<关于Entity Framework中的Attached报错的完美解决方案>,那篇文章确实能解决单个实体在进行更新.删除时Attached的报错,注意我这里说的单个 ...

  2. 关于Entity Framework中的Attached报错相关解决方案的总结

    关于Entity Framework中的Attached报错的问题,我这里分为以下几种类型,每种类型我都给出相应的解决方案,希望能给大家带来一些的帮助,当然作为读者的您如果觉得有不同的意见或更好的方法 ...

  3. PyCharm 中文 字符 python 报错 的 完美 解决方案!

    PyCharm 中文 字符 python 报错 的 完美 解决方案! #_*_ coding:utf-8_*_ https://www.python.org/dev/peps/pep-0263/ 到p ...

  4. Entity framework 中Where、First、Count等查询函数使用时要注意

    在.Net开发中,Entity framework是微软ORM架构的最佳官方工具.我们可以使用Lambda表达式在Entity framework中DbSet<T>类上直接做查询(比如使用 ...

  5. Entity Framework 教程——Entity Framework中的实体类型

    Entity Framework中的实体类型 : 在之前的章节中我们介绍过从已有的数据库中创建EDM,它包含数据库中每个表所对应的实体.在EF 5.0/6.0中,存在POCO 实体和动态代理实体两种. ...

  6. 解决MyEclipse中的js报错的小方法

    今天,下了个模版,但是导进去的时候发现js会报错.看了下其他都没有错误.而有一个js报错误,请原谅我有点红色强迫症,不能留一点红色 . 错误如下:Syntax error on token " ...

  7. Eclipse中启动tomcat报错:A child container failed during start

    我真的很崩溃,先是workspace崩了,费了好久重建的workspace,然后建立了一个小demo项目,tomcat中启动却报错,挑选其中比较重要的2条信息如下: A child container ...

  8. MyEclipse8.6中提交SVN报错

    上周五(11月27日)的时候,从TortoiseSVN提交项目报错,然后直接从MyEclipse中检出来,修改后提交同样报错. MyEclipse8.6中提交SVN报错,错误提示如下: commit ...

  9. [转]在Entity Framework中使用LINQ语句分页

    本文转自:http://diaosbook.com/Post/2012/9/21/linq-paging-in-entity-framework 我们知道,内存分页效率很低.并且,如果是WebForm ...

随机推荐

  1. Python札记 -- 装饰器补充

    本随笔是对Python札记 -- 装饰器的一些补充. 使用装饰器的时候,被装饰函数的一些属性会丢失,比如如下代码: #!/usr/bin/env python def deco(func): def ...

  2. 建立自己的Visual Studio工程模板

    如果你需要经常创建自己的特殊工程的话,那么预先建立自定义的工程模块,可能会让你的工作变得更轻松一些. 实现方法很简单,一共只需要六个步骤: 一. 新建工程 * 这里选用空白的Web工程. 二. 建立必 ...

  3. CSS:谈谈栅格布局

    检验前端的一个基本功就是考查他的布局.很久之前圣杯布局风靡一时,这里就由圣杯布局开始,到最流行的bootstrap栅格布局. 圣杯布局 圣杯布局是一种三列布局,两边定宽,中间自适应: * { box- ...

  4. Ngnice-国内ng学习网站

    今天给angular新手介绍一个国内开源的ng学习网站http://www.ngnice.com/这是由一批ng爱好者在雪狼大叔的带领下共同开发完成,致力于帮助更多的ng新人,他们分别是: ckken ...

  5. resumablejs 分块上传 断点续传

    http://www.resumablejs.com/ 官网 upload.html <!DOCTYPE html> <html lang="en"> &l ...

  6. Unity3D热更新全书-下载 唯一的一篇

    下载在这个时代实在是太平常了,每个人都深刻的理解着下载到底是什么. 这一篇文字只是把下载的代码分享并介绍,而已. 首先,下载系统担负着几个使命. 第一.是保持客户端版本库的最新. 第二.是下载要能够比 ...

  7. Unity3D热更新全书-脚本(二) 两级分化

    上篇明确了我们探讨的脚本是什么:是写在文本文件里面的代码,可以作为资源加载,取得字符串再执行. 可是为什么世界上会有那么多的脚本?而其使用方法完全看起来不一样呢?这是因为每种脚本都有自己的定位,在不同 ...

  8. [Unity3D]再次点击以退出程序

    [Unity3D]再次点击以退出程序 本文介绍为Android应用编写点击返回按键时的"再次点击以退出程序"的方法. +BIT祝威+悄悄在此留下版了个权的信息说: 下面是一个测试用 ...

  9. P,NP,NP_hard,NP_complete问题定义

    背景:在看李航的<统计学习方法时>提到了NP完全问题,于是摆之. 问题解答:以下是让我豁然开朗的解答的摘抄: 最简单的解释:P:算起来很快的问题NP:算起来不一定快,但对于任何答案我们都可 ...

  10. php将文件转换成二进制输出[转]

    header( "Content-type: image/jpeg"); $PSize = filesize('1.jpg'); $picturedata = fread(fope ...