从一个案例看MVC中DataContext和UpdateModel的工作原理(详解UpdateModel/SubmitChanges错误)
2011-05-31 23:40
417 查看
昨天遇到一段棘手的程序,尝试了各种方法,忽而在SubmitChanges的时候没反应(无错误,也不更新),忽而发生ChangeConflict,经过几个小时,终于大致理清了思路,也顺便把DataContext/UpdateModel/SubmitChanges给搞得更明白了一些,特此分享。
先大致看看代码:
xxController
{
AgileRepository _repAgile = new AgileRepository(); //这里边是SubmitChanges/DateContext/Tables等属性,可取出下面提到的story
SFCRepository _repSFC = new SFCRepository(); //相同,可取出下面提到的UDCs
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Story story = _repAgile.GetStoryAt(id);
try
{
UpdateModel(story);
foreach (var udc in story.UDCs)
{
UpdateModel(udc, udc.ID.ToString("D6"));
}
_repAgile.Save();
_repSFC.Save(); //这里也是
...
}
}
之所以出现红色的_repSFC.Save(),是因为story的一个属性List<IUDC> UDCs,也是需要在这个页面被更新的内容(在View中有控件与其对应),而它的Get过程最初是:
class Story
{
public List<IUDC> UDCs
{
get
{
SFCRepository rep = new SFCRepository();
return rep.GetUDCs().Where(...);
}
set { }
}
}
这里蓝色的rep和前面红色的rep不是同一个,所以如果从蓝色rep的当中取出UDCs并进行UpdateModel,而对另外一个无关的红色rep保存,什么也不会发生;而如果两者都取出UDCs并都曾经被UpdateModel,在Save(内部执行了SubmitChanges)的时候,会报出confilict changes exception,这个Exception极其麻烦而且不透明Google/MSDN上能找到一些资料但很多不工作。
其实,全部解决方法的秘诀其实就是:让取出数据的那个Repository执行Save操作(或者从内部看,是让取出数据的DataContext执行SubmitChanges操作)。
蓝色rep的是个局部变量,活不到Save的时候了,只能用红色的那个rep了。代码改为下面这个就好了:
xxController
{
SFCRepository _repSFC = new SFCRepository(); //下面取数据/保存的都是它。
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Story story = _repAgile.GetStoryAt(id);
_repSFC.GetUDCsFor(story); //在这里边让_repSFC的DataContext取数据。
try
{
UpdateModel(story);
foreach (var udc in story.UDCs)
{
UpdateModel(udc, udc.ID.ToString("D6"));
}
_repAgile.Save();
_repSFC.Save(); //这里会完成存储。
...
}
尽管能用了,但这段代码很不好,尤其GetUDCsFor,调用的位置很生硬,不好读也很容易出错。
最终还是这样最好:
外面:
Story story = _repAgile.GetStoryAt(id);
try
{
UpdateModel(story);
foreach (var udc in story.UDCs)
{
UpdateModel(udc, udc.ID.ToString("D6")); //Update的
}
story.SaveUDCs(); // 这个调用看着顺眼。
_repAgile.Save();
...
}
里边:
public partial class Story : IUDCable, IItem
{
private SFCRepository _repSFC = new SFCRepository(); //这个方案里取数据/保存的都是它。_repSFC不再是个局部变量,生命周期正好和UDCs相同。
public void SaveUDCs()
{
_repSFC.Save(); // 在这里保存
}
public List<IUDC> UDCs
{
get
{
_repSFC.GetUDCOf(this, ref _udcs); //取数据。
return _udcs;
}
}
后面本来不应该再把_udcs传入GetUDCOf了,直接但因为别的IUDCable也要用到,所以封装了一个函数。
这个方案,取数据/存数据的都是Story内部的_repSFC,而且封装性更好,是最终结果。
最后重复一下在这种场景中发生问题时的解决方法的秘诀其实就是:让取出数据的那个Repository执行Save操作(或者从内部看,是让取出数据的DataContext执行SubmitChanges操作)。
点击下载免费的敏捷开发教材:《火星人敏捷开发手册》
先大致看看代码:
xxController
{
AgileRepository _repAgile = new AgileRepository(); //这里边是SubmitChanges/DateContext/Tables等属性,可取出下面提到的story
SFCRepository _repSFC = new SFCRepository(); //相同,可取出下面提到的UDCs
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Story story = _repAgile.GetStoryAt(id);
try
{
UpdateModel(story);
foreach (var udc in story.UDCs)
{
UpdateModel(udc, udc.ID.ToString("D6"));
}
_repAgile.Save();
_repSFC.Save(); //这里也是
...
}
}
之所以出现红色的_repSFC.Save(),是因为story的一个属性List<IUDC> UDCs,也是需要在这个页面被更新的内容(在View中有控件与其对应),而它的Get过程最初是:
class Story
{
public List<IUDC> UDCs
{
get
{
SFCRepository rep = new SFCRepository();
return rep.GetUDCs().Where(...);
}
set { }
}
}
这里蓝色的rep和前面红色的rep不是同一个,所以如果从蓝色rep的当中取出UDCs并进行UpdateModel,而对另外一个无关的红色rep保存,什么也不会发生;而如果两者都取出UDCs并都曾经被UpdateModel,在Save(内部执行了SubmitChanges)的时候,会报出confilict changes exception,这个Exception极其麻烦而且不透明Google/MSDN上能找到一些资料但很多不工作。
其实,全部解决方法的秘诀其实就是:让取出数据的那个Repository执行Save操作(或者从内部看,是让取出数据的DataContext执行SubmitChanges操作)。
蓝色rep的是个局部变量,活不到Save的时候了,只能用红色的那个rep了。代码改为下面这个就好了:
xxController
{
SFCRepository _repSFC = new SFCRepository(); //下面取数据/保存的都是它。
[HttpPost]
public ActionResult Edit(int id, FormCollection collection)
{
Story story = _repAgile.GetStoryAt(id);
_repSFC.GetUDCsFor(story); //在这里边让_repSFC的DataContext取数据。
try
{
UpdateModel(story);
foreach (var udc in story.UDCs)
{
UpdateModel(udc, udc.ID.ToString("D6"));
}
_repAgile.Save();
_repSFC.Save(); //这里会完成存储。
...
}
尽管能用了,但这段代码很不好,尤其GetUDCsFor,调用的位置很生硬,不好读也很容易出错。
最终还是这样最好:
外面:
Story story = _repAgile.GetStoryAt(id);
try
{
UpdateModel(story);
foreach (var udc in story.UDCs)
{
UpdateModel(udc, udc.ID.ToString("D6")); //Update的
}
story.SaveUDCs(); // 这个调用看着顺眼。
_repAgile.Save();
...
}
里边:
public partial class Story : IUDCable, IItem
{
private SFCRepository _repSFC = new SFCRepository(); //这个方案里取数据/保存的都是它。_repSFC不再是个局部变量,生命周期正好和UDCs相同。
public void SaveUDCs()
{
_repSFC.Save(); // 在这里保存
}
public List<IUDC> UDCs
{
get
{
_repSFC.GetUDCOf(this, ref _udcs); //取数据。
return _udcs;
}
}
后面本来不应该再把_udcs传入GetUDCOf了,直接但因为别的IUDCable也要用到,所以封装了一个函数。
这个方案,取数据/存数据的都是Story内部的_repSFC,而且封装性更好,是最终结果。
最后重复一下在这种场景中发生问题时的解决方法的秘诀其实就是:让取出数据的那个Repository执行Save操作(或者从内部看,是让取出数据的DataContext执行SubmitChanges操作)。
点击下载免费的敏捷开发教材:《火星人敏捷开发手册》
相关文章推荐
- 从一个案例看MVC中DataContext和UpdateModel的工作原理(详解UpdateModel/SubmitChanges错误)
- 从一个案例看MVC中DataContext和UpdateModel的工作原理(详解UpdateModel/SubmitChanges错误)
- 从一个案例看MVC中DataContext和UpdateModel的工作原理(详解UpdateModel/SubmitChanges错误)
- 从一个案例看MVC中DataContext和UpdateModel的工作原理(详解UpdateModel/SubmitChanges错误)
- 从一个案例看MVC中DataContext和UpdateModel的工作原理(详解UpdateModel/SubmitChanges错误)
- 从一个案例看MVC中DataContext和UpdateModel的工作原理(详解UpdateModel/SubmitChanges错误)
- LINQ to SQL: DataContext.SubmitChanges() 失效问题
- [Angular2 Form] Reactive form: valueChanges, update data model only when form is valid
- Oracle数据泵(Data Dump)使用过程当中经常会遇到一些奇奇怪怪的错误案例
- 使用EF6.0出现:CS0029 无法将类型“System.Data.Entity.Core.Objects.ObjectContext”隐式转换为“System.Data.Objects.ObjectContext”错误
- ASP.NET MVC TryUpdateModel 更新model
- MetadataType的使用,MVC的Model层数据验证
- MVC到底使用哪种方式传递Model,在ViewData、ViewBag、PartialView、TempData、ViewModel、Tuple之间取舍
- Spring MVC @ModelAttribute 详解
- _ASSERTE(_CrtIsValidHeapPointer(pUserData))错误详解
- MVC网站开发:自定义扩展方法ModelStateExtension获取ModelState中的错误信息
- 深入浅出 spring-data-elasticsearch - 基本案例详解(三
- 详解ASP.NET MVC Model验证
- 解决asp.net mvc UpdateModel更新对象后出现null问题的方法
- 转:ASP.NET MVC利用TryUpdateModel来做资料更新 (一)