自动映射和实体框架

Bon*_*eil 6 entity-framework automapper

我在Automapper和Entity Framework中遇到了两个问题,我想知道我的解决方案是否是最好的解决方案.

背景:

我有ObjectA一个列表,ObjectB其中依次有一个ObjectC.

ObjectC是数据库中的列表,如国家/地区.我可以改变ObjectC,ObjectB但我不想添加ObjectC.

我使用MVVM ObjectB并列在数据网格中.有组合框可供选择ObjectC.

我想保存ObjectAObjectB同时使用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数据库和更新ObjectCIDObjectB...)

注意:它也适用于我的第二个问题的第一点的解决方案.

第二个问题:当我在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)

所以,它有效,但我想知道你会怎么做,一个更简单的方法.

Joã*_*lva 0

好吧,在深入探讨您的问题之前,让我先引用 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()