C# 使用实体框架将项目添加到集合
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/13282610/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Adding items to a collection using Entity Framework
提问by Eric Anastas
I'm trying to follow the DDD Repository pattern with Entity Framework 4. But I'm having problems saving changes to collection properties of my aggregate roots. Consider my classes below. Item is my aggregate root which contains a collection of SubItem entities.
我正在尝试使用实体框架 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; }
}
Next I defined a repository interface for my aggregate root class
接下来我为我的聚合根类定义了一个存储库接口
public interface IItemRespository
{
Item Get(int id);
void Add(Item i);
void Save(Item i);
}
Now here is my DbContext class that sets up the EF mapping.
现在这是我的 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);
}
}
Finally here's my implementation of IRepository using the DBContext
最后,这是我使用 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();
}
}
}
The following code creates a new Item, adds it to the repository, adds some new SubItems, and then saves the changes.
以下代码创建一个新项目,将其添加到存储库中,添加一些新的子项目,然后保存更改。
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);
I get the following exception when the Save() method tries to attach the item to the context.
当 Save() 方法尝试将项目附加到上下文时,我收到以下异常。
A referential integrity constraint violation occurred: The property values that define the referential constraints are not consistent between principal and dependent objects in the relationship.
发生参照完整性约束冲突:定义参照约束的属性值在关系中的主体和从属对象之间不一致。
I realized I'm creating a new context for each method in the repository. This is intentional. A long period of time may pass between when an Item is added and then later edited, and I don't want to keep the context or database connection open for the entire time.
我意识到我正在为存储库中的每个方法创建一个新的上下文。这是故意的。在添加 Item 和稍后编辑之间可能会经过很长一段时间,我不想让上下文或数据库连接一直保持打开状态。
Now if I attach the newItem to the second context before adding the sub items as in the code below it works.
现在,如果我在添加子项之前将 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();
}
However, if I want to be true to the repository pattern the code that edits Item shouldn't know anything about how the repository works or the ItemContext class. It should simply be able to make changes to an aggregate root entity, and then save those changes through a repository Save() method.
但是,如果我想忠实于存储库模式,编辑 Item 的代码不应该知道存储库如何工作或 ItemContext 类。它应该能够对聚合根实体进行更改,然后通过存储库 Save() 方法保存这些更改。
So how do I modify my Save() method so that changes to Item.SubItems are saved correctly?
那么如何修改我的 Save() 方法,以便正确保存对 Item.SubItems 的更改?
回答by Mark Oreta
You'll need to help EF by setting some properties to get this to work.
您需要通过设置一些属性来帮助 EF 使其工作。
When you create a new subitem, you'll need to set the FK yourself:
创建新子项时,您需要自己设置 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 });
And then in your save function, add or attach the items to your context:
然后在您的保存功能中,将项目添加或附加到您的上下文中:
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();
}
}
The reason is, because your entity isn't attached to a context when you're doing the attach, EF doesn't actually know where the entities came from - it thought that the FK not being set (probably 0) was a valid state - which is where your error was coming from. The reason you need to attach the child objects first, is so that you can actually add rather than attach. Again, since your context wasn't alive when the subitem was attached, EF isn't sure where the entity came from, and assumes the 0 PK is correct, creating your error.
原因是,因为您在执行附加操作时实体未附加到上下文,EF 实际上并不知道实体来自何处 - 它认为未设置 FK(可能为 0)是有效状态- 这就是您的错误的来源。您需要先附加子对象的原因是,您可以实际添加而不是附加。同样,由于附加子项时您的上下文不存在,因此 EF 不确定实体来自何处,并假设 0 PK 是正确的,从而造成您的错误。
回答by Gustavo
Model
模型
public virtual ICollection<CostCenter> CostCenters { get; set; }
[NotMapped]
public int CostCenterId { get; set; }//Just to get item in view
Create
创建
public ActionResult Create()
{
ViewBag.CostCenterId = new SelectList(db.CostCenters, "Id", "Name");
return View();
}
Create Post
创建帖子
public ActionResult Create(Campaign campaign)
{
...
campaign.CostCenters.Add(db.CostCenters.FirstOrDefault(f => f.Id == campaign.CostCenterId);
return RedirectToAction("Index");
}
View
看法
<%: Html.LabelFor(model => model.CostCenterId ) %>
<div class="input-control">
<%: Html.DropDownList("CostCenterId ", String.Empty) %>
<%: Html.ValidationMessageFor(model => model.CostCenterId ) %>
</div>

