您的位置:首页 > 其它

Repository模式中,Update总是失败及其解析

2014-04-25 15:20 330 查看
在Repository模式中,我的Update方法总是无法更新实体,这个非常郁闷,Update方法如下:

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]

看上去是没有任何问题的代码,一旦有实体更新的时候,总会出现如下的错误提示:

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),其中有一段话,提出了问题的所在:



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;
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐