使用Entity Framework向集合中添加项目

14

我正在尝试使用Entity Framework 4遵循DDD Repository模式。但是我在保存聚合根的集合属性更改时遇到了问题。请参考我的下面的类。Item是我的聚合根,其中包含SubItem实体的集合。

public class Item
{
    public int ItemId { get; set; }
    public string Name { get; set; }
    public ICollection<SubItem> SubItems { get; private set; }

    public Item()
    {
        this.SubItems = new HashSet<SubItem>();
    }
}

public class SubItem
{
    public int ItemId { get; set; }
    public int SubItemId { get; set; }
    public string Name { get; set; }
}

接下来,我为我的聚合根类定义了一个仓储接口。

public interface IItemRespository
{
    Item Get(int id);
    void Add(Item i);
    void Save(Item i);
}

现在这是我的DbContext类,它设置了EF映射。

public class ItemContext : System.Data.Entity.DbContext
{
    protected override void OnModelCreating(System.Data.Entity.DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Item>().HasKey(i => i.ItemId);
        modelBuilder.Entity<Item>().Property(i => i.Name);

        modelBuilder.Entity<Item>().HasMany(i => i.SubItems)
            .WithRequired()
            .HasForeignKey(si => si.ItemId);

        modelBuilder.Entity<SubItem>().HasKey(i => i.SubItemId);
        modelBuilder.Entity<SubItem>().Property(i => i.Name);
    }
}

最终这是我使用DBContext实现的IRepository

public class Repository : IItemRespository
{
    public void Save(Item i)
    {
        using (var context = new ItemContext())
        {
            context.Set<Item>().Attach(i);
            context.SaveChanges();
        }
    }

    public Item Get(int id)
    {
        using (var context = new ItemContext())
        {
            var result = (from x in context.Set<Item>() where x.ItemId == id select x).FirstOrDefault();
            return result;
        }
    }

    public void Add(Item i)
    {
        using (var context = new ItemContext())
        {
            context.Set<Item>().Add(i);
            context.SaveChanges();
        }
    }
}
以下代码创建一个新项,将其添加到存储库中,添加一些新的子项,然后保存更改。
IItemRespository repo = new Repository();

//Create a new Item
Item parent = new Item() { Name = "Parent" };
repo.Add(parent);

//A long period of time may pass .. . .

//later add sub items
parent.SubItems.Add(new SubItem() { Name = "Child 1" });
parent.SubItems.Add(new SubItem() { Name = "Child 2" });   
parent.SubItems.Add(new SubItem() { Name = "Child 3" });

//save the added sub items
repo.Save(parent);

当Save()方法尝试将该项附加到上下文时,我遇到了以下异常:

  

引用完整性约束冲突发生:定义参考约束的属性值在关系中的主体和从属对象之间不一致。

我意识到在存储库中的每个方法中都为其创建了一个新的上下文。这是故意的。添加项目和稍后编辑之间可能会有很长一段时间,我不想为整个时间保持上下文或数据库连接打开。

现在,如果我在添加子项之前将newItem附加到第二个上下文中,如下所示的代码,则可以正常工作。

//Create a new item
Item newItem = new Item() { Name = "Parent" };

using (ItemContext context1 = new ItemContext())
{
    //Create a new aggrgate
    context1.Set<Item>().Add(newItem);
    context1.SaveChanges();
}

//Long period of time may pass

using (ItemContext context2 = new ItemContext())
{
    context2.Set<Item>().Attach(newItem);

    newItem.Name = "Edited Name";
    newItem.SubItems.Add(new SubItem() { Name = "Child 1" });
    newItem.SubItems.Add(new SubItem() { Name = "Child 2" });
    newItem.SubItems.Add(new SubItem() { Name = "Child 3" });

    context2.SaveChanges();
}

然而,如果我想真正遵循存储库模式,那么编辑Item的代码不应该知道存储库如何工作或ItemContext类的任何信息。它应该只能够对聚合根实体进行更改,然后通过存储库的Save()方法保存这些更改。

那么,我该如何修改我的Save()方法,以便正确保存对Item.SubItems的更改?

2个回答

19

为了使此功能正常工作,您需要通过设置一些属性来帮助EF。

创建新子项时,您需要自己设置FK:

   parent.SubItems.Add(new SubItem() { Name = "Child 1", ItemId = parent.ItemId});
   parent.SubItems.Add(new SubItem() { Name = "Child 2", ItemId = parent.ItemId });
   parent.SubItems.Add(new SubItem() { Name = "Child 3", ItemId = parent.ItemId });

然后在您的保存函数中,将项目添加或附加到您的上下文:

    public void Save(Item i)
    {
        using (var context = new ItemContext())
        {
            foreach (var subitem in i.SubItems)
            {
                if (subitem.SubItemId == 0)
                    context.Set<SubItem>().Add(subitem);
                else
                    context.Set<SubItem>().Attach(subitem);
            }
            context.Set<Item>().Attach(i);
            context.SaveChanges();
        }
    }

问题的原因是,在执行附加操作时,实体没有与上下文关联,EF 实际上不知道实体来自哪里 - 它认为 FK 没有设置(可能为 0)是有效的状态 - 这就是你遇到错误的原因。你需要先附加子对象,这样你才能添加而不是附加。同样,由于子项在附加时上下文不活动,EF 不确定实体来自哪里,并且假定 0 PK 是正确的,从而创建了错误。


-5

模型

public virtual ICollection<CostCenter> CostCenters { get; set; }

[NotMapped]
public int CostCenterId { get; set; }//Just to get item in view

创建

public ActionResult Create()
{
    ViewBag.CostCenterId = new SelectList(db.CostCenters, "Id", "Name");
    return View();
}

创建帖子

public ActionResult Create(Campaign campaign)
{
    ...
    campaign.CostCenters.Add(db.CostCenters.FirstOrDefault(f => f.Id == campaign.CostCenterId);
    return RedirectToAction("Index");
}

视图

<%: Html.LabelFor(model => model.CostCenterId ) %>
<div class="input-control">
    <%: Html.DropDownList("CostCenterId ", String.Empty) %>
    <%: Html.ValidationMessageFor(model => model.CostCenterId ) %>
</div>

4
这并没有解释任何东西。 - lc.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接