Repository模式中,Update总是失败及其解析
2014-04-25 15:20
330 查看
在Repository模式中,我的Update方法总是无法更新实体,这个非常郁闷,Update方法如下:
[code]{
[/code]
看上去是没有任何问题的代码,一旦有实体更新的时候,总会出现如下的错误提示:
Attachinganentityoftype'TinyFrame.Data.DomainModel.t_user_application'failedbecauseanotherentityofthesametypealreadyhasthesameprimarykeyvalue.Thiscanhappenwhenusingthe'Attach'methodorsettingthestateofanentityto'Unchanged'or'Modified'ifanyentitiesinthegraphhaveconflictingkeyvalues.Thismaybebecausesomeentitiesarenewandhavenotyetreceiveddatabase-generatedkeyvalues.Inthiscaseusethe'Add'methodorthe'Added'entitystatetotrackthegraphandthensetthestateofnon-newentitiesto'Unchanged'or'Modified'asappropriate.
看字面意思,好像是我的EntityState设置不正确导致的,虽然我尝试过重新设置几次EntityState,但是仍旧无法解决我的问题。
然后实在找不出原因,就利用关键字“EFRepositoryUpdate”在Google上面搜集,果然找到一篇文章:AdvancedEntityFramework6ScenariosforanMVC5WebApplication(12of12),其中有一段话,提出了问题的所在:
[code]
问题的原因如下:
在Context对象中,已经hold住了一个需要操作的对象,当我们把EntityState修改成modified的时候,Context会再次去加载那个操作对象,但是这样加载是无法成功的,因为当前已经存在一个对象了,再加载会导致重复,然后抛出失败的错误。
解决方法很简单,就是在展示列表的时候,利用AsNoTracking将Hold住的对象释放掉即可。我们修改代码如下:
[/code]
然后提交,OK,问题解决。
=============================Update2014.09.19======================
看到评论中有朋友虽然按照上述方法,但是仍然无法解决这一问题。原因是在Context中还保留有当前实体的副本所致,这里只要我们将实体副本从内存中完全移除,就可以了。
然后在Repository中,在进行更新和删除之前,运行一下即可:
publicvirtualvoidUpdate(Tentity)
[code]{
try
{
if(entity==null)thrownewArgumentNullException("实体类为空");
Context.Entry(entity).State=EntityState.Modified;
//Context.SaveChanges();
}
catch(DbEntityValidationExceptiondbex)
{
varmsg=string.Empty;
foreach(varvalidationErrorsindbex.EntityValidationErrors)
foreach(varvalidateionErrorinvalidationErrors.ValidationErrors)
msg+=string.Format("Property:{0}Error:{1}",validateionError.PropertyName,validateionError.ErrorMessage);
varfail=newException(msg,dbex);
throwfail;
}
}
[/code]
看上去是没有任何问题的代码,一旦有实体更新的时候,总会出现如下的错误提示:
看字面意思,好像是我的EntityState设置不正确导致的,虽然我尝试过重新设置几次EntityState,但是仍旧无法解决我的问题。
然后实在找不出原因,就利用关键字“EFRepositoryUpdate”在Google上面搜集,果然找到一篇文章:
Thishappenedbecauseofthefollowingsequenceofevents:
[code]
TheEditmethodcallstheValidateOneAdministratorAssignmentPerInstructormethod,whichretrievesalldepartmentsthathaveKimAbercrombieastheiradministrator.ThatcausestheEnglishdepartmenttoberead.Asaresultofthisreadoperation,theEnglishdepartmententitythatwasreadfromthedatabaseisnowbeingtrackedbythedatabasecontext.
TheEditmethodtriestosettheModifiedflagontheEnglishdepartmententitycreatedbytheMVCmodelbinder,whichimplicitlycausesthecontexttotrytoattachthatentity.Butthecontextcan'tattachtheentrycreatedbythemodelbinderbecausethecontextisalreadytrackinganentityfortheEnglishdepartment.
Onesolutiontothisproblemistokeepthecontextfromtrackingin-memorydepartmententitiesretrievedbythevalidationquery.There'snodisadvantagetodoingthis,becauseyouwon'tbeupdatingthisentityorreadingitagaininawaythatwouldbenefitfromitbeingcachedinmemory.
问题的原因如下:
在Context对象中,已经hold住了一个需要操作的对象,当我们把EntityState修改成modified的时候,Context会再次去加载那个操作对象,但是这样加载是无法成功的,因为当前已经存在一个对象了,再加载会导致重复,然后抛出失败的错误。
解决方法很简单,就是在展示列表的时候,利用AsNoTracking将Hold住的对象释放掉即可。我们修改代码如下:
publicvirtualTGet(Expression<Func<T,bool>>where)
{
returnDbset.Where(where).AsNoTracking().FirstOrDefault<T>();
}
publicvirtualIQueryable<T>GetMany(Expression<Func<T,bool>>where)
{
returnDbset.Where(where).AsNoTracking();
}
[/code]
然后提交,OK,问题解决。
=============================Update2014.09.19======================
看到评论中有朋友虽然按照上述方法,但是仍然无法解决这一问题。原因是在Context中还保留有当前实体的副本所致,这里只要我们将实体副本从内存中完全移除,就可以了。
//用于监测Context中的Entity是否存在,如果存在,将其Detach,防止出现问题。
privateBooleanRemoveHoldingEntityInContext(Tentity)
{
varobjContext=((IObjectContextAdapter)_context).ObjectContext;
varobjSet=objContext.CreateObjectSet<T>();
varentityKey=objContext.CreateEntityKey(objSet.EntitySet.Name,entity);
ObjectfoundEntity;
varexists=objContext.TryGetObjectByKey(entityKey,outfoundEntity);
if(exists)
{
objContext.Detach(foundEntity);
}
return(exists);
}
然后在Repository中,在进行更新和删除之前,运行一下即可:
publicTRemove(Tentity)
{
try
{
RemoveHoldingEntityInContext(entity);
_context.DbSet<T>().Attach(entity);
return_context.DbSet<T>().Remove(entity);
}
catch(DbEntityValidationExceptiondbex)
{
varmsg=string.Empty;
foreach(varvalidationErrorsindbex.EntityValidationErrors)
foreach(varvalidateionErrorinvalidationErrors.ValidationErrors)
msg+=string.Format("属性:{0}错误:{1}",validateionError.PropertyName,validateionError.ErrorMessage);
varfail=newException(msg,dbex);
throwfail;
}
}
publicTUpdate(Tentity)
{
try
{
RemoveHoldingEntityInContext(entity);
varupdated=_context.DbSet<T>().Attach(entity);
_context.DbContext.Entry(entity).State=EntityState.Modified;
returnupdated;
}
catch(DbEntityValidationExceptiondbex)
{
varmsg=string.Empty;
foreach(varvalidationErrorsindbex.EntityValidationErrors)
foreach(varvalidateionErrorinvalidationErrors.ValidationErrors)
msg+=string.Format("属性:{0}错误:{1}",validateionError.PropertyName,validateionError.ErrorMessage);
varfail=newException(msg,dbex);
throwfail;
}
}
相关文章推荐
- Repository模式中,Update总是失败及其解析
- Repository模式中,Update和Delete总是失败及其解析(报错“another entity of the same type already has the same primar”)
- Repository模式中,Update总是失败及其解析(转)
- 微信开发者模式和新浪SAE之间 Token总是验证失败
- (转)看到网上有很多关于这个的帖子,但在执行“设置test数据库为紧急修复模式”时,SQL Server 2008总是失败。哪位大侠给个SQLServer 2008的有效方法。
- 关于fopen函数中的打开模式(以写的方式总是打开失败)
- KandQ:单例模式的七种写法及其相关问题解析
- EventBus 3.0进阶:源码及其设计模式 完全解析
- 关于window update 检查更新总是失败的解决方案
- 实例解析观察者模式及其在Java设计模式开发中的运用
- KandQ:单例模式的七种写法及其相关问题解析
- DNS原理及其解析过程 精彩剖析
- 虚拟机几种联网模式详解 桥接 nat 模式解析
- Java事务处理全解析(五)—— Template模式
- Mysql InnoDB中的查询事务模式与锁定select ..for update
- PHP设计模式之代理模式的深入解析
- 高性能应用构建模式解析(性能解析)
- Oracle DB 12.2(12cR2)的一个新特性:硬解析失败的SQL语句(需要符合一定条件)打印到alert_sid.log中.
- Scala 深入浅出实战经典 第61讲:Scala中隐式参数与隐式转换的联合使用实战详解及其在Spark中的应用源码解析
- 【设计模式】 适配器模式(Adapter Pattern)- 最易懂的设计模式解析