我在Automapper和Entity Framework中遇到两个问题,我想知道我的解决方案是否是最好的解决方案。
背景:
我有一个ObjectA
,其中包含ObjectB
的列表,而ObjectC
又有ObjectC
。
ObjectC
是数据库中的列表,就像国家/地区一样。我可以在ObjectB
上更改ObjectC
,但我不想添加ObjectB
。
我使用MVVM,ObjectC
列在数据网格中。有组合框可供选择ObjectA
。
我想同时保存ObjectB
和 public void SaveObjectA(ObjectA p_ObjectA)
{
OpenTransaction();
var l_Provider = new DataProvider<DB.ObjectA>(Context);
var l_ObjectA = l_Provider.FindById(p_ObjectA.ID);
Mapper.Map(p_ObjectA, l_ObjectA);
CloseTransaction();
}
,因此我使用Automapper和交易。
public partial class ObjectA
{
public ObjectA()
{
this.ObjectB = new HashSet<ObjectB>();
}
public System.Guid ID { get; set; }
public virtual ICollection<ObjectB> ObjectB { get; set; }
}
public partial class ObjectB
{
public System.Guid ID { get; set; }
public System.Guid ObjectCID { get; set; }
public virtual ObjectC ObjectC { get; set; }
}
public partial class ObjectC
{
public System.Guid ID { get; set; }
public string Name { get; set; }
}
实体框架类:
public class ObjectA : ObjectBase
{
public ObjectA ()
{
ObjectB = new Collection< ObjectB >();
}
public virtual ICollection<ObjectB> ObjectB { get; set; }
}
public class ObjectB : ObjectBase
{
private ObjectC _ObjectC { get; set; }
public virtual ObjectC ObjectC
{
get
{
return _ObjectC;
}
set
{
_ObjectC = value;
}
}
}
public class ObjectC : ObjectBase
{
public string Name { get; set; }
}
public abstract class ObjectBase
{
public Guid ID { get; set; }
}
DTO课程:
ObjectB
第一个问题:当我保存ObjectC
时,实体框架会尝试插入ObjectC
。但是Mapper.CreateMap<ObjectB, DB.ObjectB>()
.ForMember(pro=>pro.ObjectC, opt=>opt.Ignore());
已经存在。我不想要插入但需要更新。
我的解决方案(在论坛上看到):
ObjectC
但我不明白,因为如果我忽略ObjectC
,ObjectC
就不应该更新。但是,它可以工作(即:更新正常,他不会尝试在数据库中添加行,Automapper / EF可以在数据库中找到ObjectCID
并在ObjectB
上更新Mapper.CreateMap<ObjectB, DB.ObjectB>()
.ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID));
... )
注意:它对我的第二个问题的第一点解决方案也有效。
第二个问题:当我在datagrid上更新,添加或删除一行时,我想保存数据库中的更改。
更新:与第一个问题相同:该行已存在。
ConstructUsing
我认为,解决方案是从数据库中附加正确的行,然后填充属性。
所以我使用Mapper.CreateMap<ObjectB, DB.ObjectB>()
.ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID) ??
Context.Set<DB.ObjectB>().Create())
来查找行。
添加:然后,ID为空,我无法保存新行
Mapper.CreateMap<ObjectA, DB.ObjectA>()
.ConstructUsing(s => Context.Set<DB.ObjectA>().Create())
.AfterMap
(
(bef, aft) => aft.ObjectB.ToList()
.Where(x => !bef.ObjectB.Select(z=>z.ID).Contains(x.ID))
.ToList()
.ForEach(ele => Context.ObjectB
.Remove(Context.ObjectB.Find(ele.ID)))
);
如果行不存在,我必须创建(并附加)对象到上下文。
删除:数据网格中删除的行不会在数据库中删除:
{{1}}
所以,它有效,但我想知道你会怎么做,一种更简单的方法。
答案 0 :(得分:0)
好吧,在我深入研究你的问题之前,让我通过引用Autommaper docs中的某些内容来明星:
目前,AutoMapper面向模型投影场景,将复杂对象模型展平为DTO和其他简单对象,其设计更适合于序列化,通信,消息传递,或者只是域和应用层之间的反腐败层< / p>
也就是说,如果你仔细阅读,那么automapper被设计用于从复杂对象转换为更简单的对象,或者在行之间进行读取,从实体对象到Pocos,DTO,ViewModel等等......不是相反。为什么?仅仅因为它太复杂了。这不是你可以拥有new ObjectA()
的情况。
问题1: 这是实体框架(和其他主流Orms)的预期行为。 EF处理实体对象。如果指定ObjectC属性是新实体,EF将尝试插入它。如果要使用存在的那个,则需要先从上下文加载它,或者只使用ObjectCId外键。
问题2:
这与问题1的答案几乎相同.EF设计为您使用实体对象。当您修改它们时,EF跟踪更改,然后在您调用SaveChanges()
后,这些更改将保留在数据库中。
关于删除的一个特别评论。 EF不会像NHibernate上的“cascade = all-delete-orphan”那样级联删除。有一些workarounds,但一般来说,当你想删除整个关系时,你需要管理孩子的删除。
我知道这可能不是您正在寻找的答案,但是您正在处理由于您没有正确使用这些工具而引发的问题。
一般来说,与EF合作很简单:
1 - 从数据库加载实体
2 - 修改它们
3 - 通过调用SaveChanges()