EF在NTier架构下的保存注意事项

EF在N-Tier架构下的保存注意事项

                          szjay


前提
一个实体对象必须先进入ObjectContext中才能进行增、删、改的操作。

新增
新增的过程很简单,调用AddObject方法即可。

修改
如果要修改的实体对象是Detached状态,那么必须先调用AttachTo方法。
AttachTo方法告诉ObjectContext,虽然当前实体对象没有在ObjectContext中存在,但它并不是一个新的(Added状态)实体对象,而是在数据库中存在,但ObjectContext中未装载而已。
如果不采用AttachTo方法,那么就需要从数据库中查询一次到ObjectContext中,否则ObjectContext不“认识”此实体对象。

Detached状态产生的原因有两种:
1.手工增加了实体对象,但未调用AddObject或Attach方法;
2.实体对象是从ObjectContext获取的,但随后脱离了ObjectContext;

一个经典的保存实体对象修改的步骤是:
//首先查询出实体对象。
using(Entities ctx = new Entities) //Entities是ObjectContext的派生类。
{
Product product = ctx.Product.First();
}

//然后实体对象脱离ObjectContext,变为Detached状态,传送到客户端做处理。
product.Name = "abc"; //客户端修改实体对象的属性值。

//客户端返回实体对象。
using(Entities ctx = new Entities) //注意,我们开了另一个ObjectContext。
{
ctx.Product.AttachTo(product); //必须先附加,否则当前ObjectContext不认识。AttachTo会把实体对象设置为Unchanged状态。
ctx.ObjectStateManager.ChangeObjectState(product, EntityState.Modified); //ObjectContext并不知道实体对象已经修改过了,因此需要手工修改实体对象的状态。
ctx.SaveChanges();
}

以上方法适用于单个对象或简单的聚合根,如果遇到复杂的对象图(Object Graph),那么手工修改状态非常麻烦,也容易出错。
因此我们通常会从ObjectContext中再取出一次对象(PO),然后把客户端传过来的聚合对象当作DTO,再赋值给PO。
这样方法遇到大数据量的更新时,效率很低(又取了一次数据,并逐个赋值,并且要逐个对比是否被删除)。
考虑到DTO与PO的结构一样,我们可以用AutoMapping之类的工具简化赋值。

删除
ObjectContext删除实体对象时,首先会判断实体对象是否在本身的上下文中,因此在删除前,我们要么先从数据库中Load一次,要么调用AttachTo方法,然后再调用DeleteObject方法。
实际上,EF删除一个实体对象,只会使用到它的主键字段和时间戳字段,因此我们可以“伪造”一个要删除的实体对象(Stub,不完整实体对象):
Product product = new Product(){Id=1,Version=2};
然后调用AttachTo和DeleteObject即可。

注意
当一个对象图(Object Graph)中的一个对象附加到了ObjectContext,那么整个对象图的所有对象,都自动附加到了这个ObjectContext。
一个实体对象只能同时被附加到一个ObjectContext中。

如果要强制从数据库获取实体对象,可以设置MergeOption属性为NoTracking。
MergeOption的策略:
AppendOnly (default): 将新获取的实体对象添加到缓存,忽略那些缓存中已有的实体(注意区别于对象引用)。
OverwriteChanges: 用从 Store 中新获取的属性值替换那些缓存中被修改的实体属性。
PreserveChanges: 和 OverwriteChanges 相反,用缓存中修改的属性值替换从 Store 获取的原始值。
NoTracking: 禁用缓存和状态跟踪,通常用于那些只读环境。

原文地址:https://www.cnblogs.com/ego/p/2445457.html