关于Entity Framework中的Attached报错的完美解决方案
我们在使用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报错的完美解决方案的更多相关文章
- 关于Entity Framework中的Attached报错的完美解决方案终极版
之前发表过一篇文章题为<关于Entity Framework中的Attached报错的完美解决方案>,那篇文章确实能解决单个实体在进行更新.删除时Attached的报错,注意我这里说的单个 ...
- 关于Entity Framework中的Attached报错相关解决方案的总结
关于Entity Framework中的Attached报错的问题,我这里分为以下几种类型,每种类型我都给出相应的解决方案,希望能给大家带来一些的帮助,当然作为读者的您如果觉得有不同的意见或更好的方法 ...
- PyCharm 中文 字符 python 报错 的 完美 解决方案!
PyCharm 中文 字符 python 报错 的 完美 解决方案! #_*_ coding:utf-8_*_ https://www.python.org/dev/peps/pep-0263/ 到p ...
- Entity framework 中Where、First、Count等查询函数使用时要注意
在.Net开发中,Entity framework是微软ORM架构的最佳官方工具.我们可以使用Lambda表达式在Entity framework中DbSet<T>类上直接做查询(比如使用 ...
- Entity Framework 教程——Entity Framework中的实体类型
Entity Framework中的实体类型 : 在之前的章节中我们介绍过从已有的数据库中创建EDM,它包含数据库中每个表所对应的实体.在EF 5.0/6.0中,存在POCO 实体和动态代理实体两种. ...
- 解决MyEclipse中的js报错的小方法
今天,下了个模版,但是导进去的时候发现js会报错.看了下其他都没有错误.而有一个js报错误,请原谅我有点红色强迫症,不能留一点红色 . 错误如下:Syntax error on token " ...
- Eclipse中启动tomcat报错:A child container failed during start
我真的很崩溃,先是workspace崩了,费了好久重建的workspace,然后建立了一个小demo项目,tomcat中启动却报错,挑选其中比较重要的2条信息如下: A child container ...
- MyEclipse8.6中提交SVN报错
上周五(11月27日)的时候,从TortoiseSVN提交项目报错,然后直接从MyEclipse中检出来,修改后提交同样报错. MyEclipse8.6中提交SVN报错,错误提示如下: commit ...
- [转]在Entity Framework中使用LINQ语句分页
本文转自:http://diaosbook.com/Post/2012/9/21/linq-paging-in-entity-framework 我们知道,内存分页效率很低.并且,如果是WebForm ...
随机推荐
- 视频教程--ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库
说好的给园子里的朋友们录制与<ASP.NET MVC 使用 Petapoco 微型ORM框架+NpgSql驱动连接 PostgreSQL数据库> 这篇博客相对应的视频,由于一个月一来没有时 ...
- Zookeeper--Zookeeper是什么
Google的三篇论文影响了很多很多人,也影响了很多很多系统.这三篇论文一直是分布式领域传阅的经典.根据MapReduce,于是我们有了Hadoop:根据GFS,于是我们有了HDFS:根据BigTab ...
- OpenSSL密码算法库: MD5示例小程序
OpenSSL http://www.openssl.org/ OpenSSL整个软件包大概可以分成三个主要的功能部分:密码算法库.SSL协议库以及应用程序.OpenSSL 的密码算法库包含多种加密算 ...
- [nRF51822] 6、基于nRF51822平台的flash读写研究
前言 本文重点介绍flash的数据存取特性.flash的内存划分.一个简单的存取图片的内存管理方式,以及对flash写前删的时间.删后读的时间.写后读的时间进行测量的一个小实验.目的在于更全面了解fl ...
- 使用后缀数组寻找最长公共子字符串JavaScript版
后缀数组很久很久以前就出现了,具体的概念读者自行搜索,小菜仅略知一二,不便讨论. 本文通过寻找两个字符串的最长公共子字符串,演示了后缀数组的经典应用. 首先需要说明,小菜实现的这个后缀数组算法,并非标 ...
- Microsoft Dynamics CRM 2013 and 2011 Update Rollups and Service Packs
Microsoft Dynamics CRM 2013 BTW: RC stands for Release for Candidate, and RTM stands for Release ...
- WebApi系列~实际项目中如何使用HttpClient向web api发异步Get和Post请求并且参数于具体实体类型
回到目录 本讲比较实际,在WEB端有一个Index和Create方法,用来从web api显示实体列表数据和向api插入实体对象,这就是以往的网站,只不过是把数据持久化过程放到了web pai上面,它 ...
- Java程序员的日常—— IOUtils总结
以前写文件的复制很麻烦,需要各种输入流,然后读取line,输出到输出流...其实apache.commons.io里面提供了输入流输出流的常用工具方法,非常方便.下面就结合源码,看看IOUTils都有 ...
- ASP.NET MVC的Action拦截器(过滤器)ActionFilter
有时项目要进行客户端请求(action)进行拦截(过滤)验证等业务,可以使用拦截器进行实现,所谓的action拦截器也没有什么的,只是写一个类,继承另一个类(System.Web.Mvc.Filter ...
- 请求一个action,将图片的二进制字节字符串在视图页面以图片形式输出
有些时候需要将二进制图片字节在发送浏览器以图片形式显示: 下面是一些示例代码: 控制器: /// <summary> /// 将图片的二进制字节字符串在视图页面以图片形式输出 /// &l ...