自动映射和实体框架

时间:2013-10-22 12:54:43

标签: entity-framework automapper

我在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

但我不明白,因为如果我忽略ObjectCObjectC就不应该更新。但是,它可以工作(即:更新正常,他不会尝试在数据库中添加行,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}}

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

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

对数据库应用更改