6

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

语境 :

我有一个ObjectAwhich 有一个列表,ObjectB其中又有一个ObjectC.

ObjectC是数据库中的一个列表,就像国家一样。我可以更改但我不添加想要添加ObjectCObjectBObjectC

我使用 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();
    }

实体框架类:

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; }
}

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; }
} 

第一个问题:当我保存时ObjectB,实体框架会尝试插入ObjectC。但ObjectC已经存在。我不想要插入但要更新。

我的解决方案(见论坛):

Mapper.CreateMap<ObjectB, DB.ObjectB>()
      .ForMember(pro=>pro.ObjectC, opt=>opt.Ignore());

但我不明白,因为如果我忽略ObjectCObjectC不应该更新。但是,它可以工作(即:更新正常,他不会尝试在数据库中添加一行,Automapper/EF 可以在数据库中找到并ObjectC更新...)ObjectCIDObjectB

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

第二个问题:当我在数据网格上更新、添加或删除一行时,我想将更改保存在数据库中。

  • 更新:与第一个问题相同:该行已经存在。

    Mapper.CreateMap<ObjectB, DB.ObjectB>()
          .ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID));
    

    我认为,解决方案是从数据库中附加正确的行,然后填充属性。

    所以我用ConstructUsing查找行。

  • 添加:然后,ID 为空,我无法保存新行

    Mapper.CreateMap<ObjectB, DB.ObjectB>()
          .ConstructUsing(s => Context.Set<DB.ObjectB>().Find(s.ID) ?? 
                                   Context.Set<DB.ObjectB>().Create())
    

    在该行不存在的情况下,我必须创建(并附加)一个对象到上下文。

  • 删除:在数据网格上删除的行不会在数据库中删除:

    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)))
          );
    

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

4

1 回答 1

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()

于 2015-03-09T00:17:31.717 回答