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

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

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

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

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

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

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

        /// <summary>
/// 判断entity是否已经Attached
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
public bool IsAttached<TEntity>(TEntity entity) where TEntity : EntityObject
{
ObjectStateEntry entry = null;
if (dbContext.ObjectStateManager.TryGetObjectStateEntry(entity.EntityKey, out entry))
{
if (entry.State != EntityState.Detached)
{
return true;
}
}
return false;
}

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

 public interface IEntityWithId
{
Guid Id { get; set; }
} public class EntityClass : IEntityWithId
{
Guid Id { get; set; } //...其它属性
} /// <summary>
/// 判断entity是否已经Attached
/// </summary>
/// <typeparam name="TEntity"></typeparam>
/// <param name="entity"></param>
/// <returns></returns>
public bool IsAttached<TEntity>(TEntity entity) where TEntity : IEntityWithId
{
TEntity localEntity = dbContext.Set<TEntity>().Local.Where(t => t.Id == entity.Id).FirstOrDefault();
if (localEntity != null)
{
if (dbContext.Entry(localEntity).State != EntityState.Detached)
{
return true;
}
}
return false;
}

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

        /// <summary>
/// 判断entity是否已经Attached
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
private bool IsAttached(TEntity entity)
{
var objectContext = ((IObjectContextAdapter)this.baseContext).ObjectContext;
ObjectStateEntry entry = null;
if (objectContext.ObjectStateManager.TryGetObjectStateEntry(GetEntityKey(entity), out entry))
{
if (entry.State != EntityState.Detached)
{
return true;
}
}
return false;
} /// <summary>
/// 通过反射获取实体对象的EntityKey
/// </summary>
/// <param name="entity"></param>
/// <returns></returns>
private EntityKey GetEntityKey(TEntity entity)
{
var entityWrapper = entity.GetType().GetField("_entityWrapper").GetValue(entity);//获取字段_entityWrapper的值
var entityWrapperType = entityWrapper.GetType();//获取字段的类型 var entityKey = entityWrapperType.GetProperty("EntityKey").GetValue(entityWrapper, null);//获取EntityKey属性的值 return (EntityKey)entityKey;
}

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

        public virtual void Update(TEntity entity, bool autoCommit = false)
{
this.ValidateEntity(entity, false);
if (!this.IsAttached(entity))
{
this.objectSet.Attach(entity);
this.baseContext.Entry(entity).State = EntityState.Modified;
}
if (autoCommit)
{
this.Commit();
}
} public virtual void Remove(TEntity entity, bool autoCommit = false)
{
if (!this.IsAttached(entity))
{
this.objectSet.Attach(entity);
}
this.objectSet.Remove(entity);
if (autoCommit)
{
this.Commit();
}
}

关于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. 大数据存储:MongoDB实战指南——常见问题解答

    锁粒度与并发性能怎么样? 数据库的读写并发性能与锁的粒度息息相关,不管是读操作还是写操作开始运行时,都会请求相应的锁资源,如果请求不到,操作就会被阻塞.读操作请求的是读锁,能够与其它读操作共享,但是当 ...

  2. 将asp.net webapi的运行时版本由4.0升级到4.5.1时遇到的问题及解决

    更新package 更改.net运行时的版本之后,出现了错误提示,说需要改新以下组件: EntityFramework, EntityFramework.zh-Hans, Microsoft.AspN ...

  3. Dynamic CRM 2013学习笔记 系列汇总

    这里列出所有 Dynamic CRM 2013学习笔记 系列文章,方便大家查阅.有任何建议.意见.需要,欢迎大家提交评论一起讨论. 本文原文地址: Dynamic CRM 2013学习笔记 系列汇总 ...

  4. [.NET领域驱动设计实战系列]专题十一:.NET 领域驱动设计实战系列总结

    一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计 ...

  5. Unable to locate secure storage module异常的解决方案

    org.eclipse.equinox.security.storage.StorageException: Unable to locate secure storage module 该异常同样是 ...

  6. javascript 设计模式-----单例模式

    单例模式的意思是只需要实例化某个类一次,它的方法也比较简单,通过判断某个类是否已经被实例化了,再返回该值.可以通过各种方法来实现单例模式,下面我们采取以下这种实现方式: var single = (f ...

  7. java生成压缩文件

    在工作过程中,需要将一个文件夹生成压缩文件,然后提供给用户下载.所以自己写了一个压缩文件的工具类.该工具类支持单个文件和文件夹压缩.放代码: import java.io.BufferedOutput ...

  8. common-dbcp2数据库连接池参数说明

    参数 默认值 描述 建议值 DefaultAutoCommit  null 通过这个池创建连接的默认自动提交状态.如果不设置,则setAutoCommit 方法将不被调用.  true Default ...

  9. bianma 水平 技巧

    能够写出这样的代码, 其实体现了水平 switch (state) { case 0: break; case 3: return; // already connected case 4: stat ...

  10. .NetCore~框架版本号不同引起dotnet不能run它

    对于.netCore来说,今年已经推出了正式版,这要求使用vs2015的开发者需要升级到beta3版,而如果使用老版VS开始的.netCore应用程序,它的架构版本将为是测试版"versio ...