【问题标题】:Adding items to a collection using Entity Framework使用实体框架将项目添加到集合
【发布时间】:2012-10-28 06:28:39
【问题描述】:

我正在尝试使用 Entity Framework 4 遵循 DDD 存储库模式。但是我在保存对聚合根的集合属性的更改时遇到了问题。考虑下面我的课程。 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);
}

现在这是设置 EF 映射的 DbContext 类。

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 的更改?

【问题讨论】:

    标签: c# .net entity-framework-4 domain-driven-design ddd-repositories


    【解决方案1】:

    型号

    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>
    

    【讨论】:

    • 这并不能说明什么问题
    【解决方案2】:

    您需要通过设置一些属性来帮助 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 是正确的,从而产生错误。

    【讨论】:

      猜你喜欢
      • 2013-06-03
      • 1970-01-01
      • 2010-09-29
      • 2021-05-18
      • 2020-01-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-10-20
      相关资源
      最近更新 更多