博客
关于我
关于Entity Framework中的Attached报错的完美解决方案
阅读量:463 次
发布时间:2019-03-06

本文共 4449 字,大约阅读时间需要 14 分钟。

我们在使用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属性来进行判断

///         /// 判断entity是否已经Attached        ///         ///         /// 
public bool IsAttached
(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; }        //...其它属性    }///         /// 判断entity是否已经Attached        ///         /// 
/// ///
public bool IsAttached
(TEntity entity) where TEntity : IEntityWithId { TEntity localEntity = dbContext.Set
().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,意味着我们并不能在自己的项目中直接使用,唯一的办法就是采用反射来动态获取该属性,所以整个的实现方法如下:

///         /// 判断entity是否已经Attached        ///         ///         /// 
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; } /// /// 通过反射获取实体对象的EntityKey /// /// ///
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();            }        }

 

转载地址:http://aawdz.baihongyu.com/

你可能感兴趣的文章
Selenium2+python自动化38-显式等待(WebDriverWait)
查看>>
python接口自动化8-参数化
查看>>
appium+python自动化33-解锁九宫格(TouchAction)
查看>>
使用GMap.NET类库,实现地图轨迹回放。(WPF版)
查看>>
对Spring aop的理解
查看>>
highcharts 从后台动态改变数据
查看>>
【Autofac打标签模式】PropertySource和Value
查看>>
有道云笔记 同步到我的博客园
查看>>
阿里云“网红&quot;运维工程师白金:做一个平凡的圆梦人
查看>>
AnalyticDB for PostgreSQL 6.0 新特性介绍
查看>>
Alibaba Cloud Linux 2 LTS 正式发布,提供更高性能和更多保障!
查看>>
从单体迈向 Serverless 的避坑指南
查看>>
卡顿人生,如何拯救?
查看>>
对容器镜像的思考和讨论
查看>>
【OpenYurt 深度解析】边缘网关缓存能力的优雅实现
查看>>
互联网泛娱乐直播安全的解决之道
查看>>
一不小心,它成为了 GitHub Alibaba Group 下 Star 最多的开源项目
查看>>
java实现哈弗曼树
查看>>
李笑来必读书籍整理
查看>>
vue书籍整理
查看>>