Bon*_*eil 6 entity-framework automapper
我在Automapper和Entity Framework中遇到了两个问题,我想知道我的解决方案是否是最好的解决方案.
背景:
我有ObjectA
一个列表,ObjectB
其中依次有一个ObjectC
.
ObjectC
是数据库中的列表,如国家/地区.我可以改变ObjectC
,ObjectB
但我不想添加ObjectC
.
我使用MVVM ObjectB
并列在数据网格中.有组合框可供选择ObjectC
.
我想保存ObjectA
并ObjectB
同时使用Automapper和事务.
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();
}
Run Code Online (Sandbox Code Playgroud)
实体框架类:
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; }
}
Run Code Online (Sandbox Code Playgroud)
DTO课程:
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; }
}
Run Code Online (Sandbox Code Playgroud)
第一个问题:当我保存时ObjectB
,Entity Framework尝试插入ObjectC
.但ObjectC
已经存在.我不想要插入但需要更新.
我的解决方案(在论坛上看到):
Mapper.CreateMap<ObjectB, DB.ObjectB>()
.ForMember(pro=>pro.ObjectC, opt=>opt.Ignore());
Run Code Online (Sandbox Code Playgroud)
但我不明白,因为如果我忽略ObjectC
,ObjectC
不应该更新.然而,它的工作原理(即:更新OK,他不尝试在数据库中添加行和Automapper/EF可以找到ObjectC
数据库和更新ObjectCID
上ObjectB
...)
注意:它也适用于我的第二个问题的第一点的解决方案.
第二个问题:当我在datagrid上更新,添加或删除一行时,我想保存数据库中的更改.
更新:与第一个问题相同:该行已存在.
Mapper.CreateMap<ObjectB, DB.ObjectB>()
.ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID));
Run Code Online (Sandbox Code Playgroud)
我认为,解决方案是从数据库中附加正确的行,然后填充属性.
所以我ConstructUsing
用来找到这一行.
添加:然后,ID为空,我无法保存新行
Mapper.CreateMap<ObjectB, DB.ObjectB>()
.ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID) ??
Context.Set<DB.ObjectB>().Create())
Run Code Online (Sandbox Code Playgroud)
在行不存在的情况下,我必须创建(并附加)对象到上下文.
删除:数据网格中删除的行不会在数据库中删除:
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)))
);
Run Code Online (Sandbox Code Playgroud)所以,它有效,但我想知道你会怎么做,一个更简单的方法.
好吧,在深入探讨您的问题之前,让我先引用 Autommaper文档中的一些内容:
目前,AutoMapper面向模型投影场景,将复杂的对象模型扁平化为DTO和其他简单对象,其设计更适合序列化、通信、消息传递,或者只是域和应用层之间的反腐败层
也就是说,如果你仔细阅读,automapper 的设计目的是从复杂的对象转换为更简单的对象,或者读取字里行间的内容,从实体对象到 Pocos、DTO、ViewModel 等……而不是相反。为什么?仅仅是因为它太复杂了。这不是你可以拥有一个的情况new ObjectA()
。
问题 1:这是 Entity Framework(和其他主流 Orms)的预期行为。EF 处理实体对象。如果您指定 ObjectC 属性是一个新实体,EF 将尝试插入它。如果您想使用现有的外键,则需要首先从上下文中加载它,或者简单地使用 ObjectCId 外键。
问题 2:这实际上与问题 1 的答案几乎相同。EF 是为您使用实体对象而设计的。当您修改它们时,EF 会跟踪更改,然后在调用后SaveChanges()
,这些更改将保留在数据库中。
只是关于删除的特别说明。EF 不级联删除,如 NHibernate 上的“cascade=all-delete-orphan”。有一些解决方法,但一般来说,当您想要删除整个关系时,您需要管理子项的删除。
我知道这可能不是您正在寻找的答案,但您正在处理由于没有正确使用工具而出现的问题。
一般来说,使用 EF 很简单:
1 - 从数据库加载实体
2 - 修改它们
3 - 通过调用对数据库应用更改SaveChanges()
归档时间: |
|
查看次数: |
2527 次 |
最近记录: |