感谢您抽出宝贵的时间!
作为学习实体框架的一部分,我目前正在对智能手机网上商店进行原型制作,我偶然发现了一个问题,即数据库中的新记录会在相关表中添加其他记录。我通过关注此帖子Prevent Adding New Record on Related Table Entity in Entity Framework来解决此问题。
但是,每当我想添加记录时为每个实体添加EntityState.Unchanged
似乎很繁琐,所以我开始认为自己可能出错了。
请采用以下用于签出的代码:
public ActionResult CheckOut()
{
// Save cart
Order order = new Order() { Payed = false, Canceled = false };
db.Orders.Add(order);
for (int i = 0; i < cartItems.Count; i++)
{
cartItems[i].Order = order;
cartItems[i].OrderId = order.OrderId;
db.CartItems.Add(cartItems[i]);
db.Entry(cartItems[i].Item).State = EntityState.Unchanged;
db.Entry(cartItems[i].Item.Memory).State = EntityState.Unchanged;
db.Entry(cartItems[i].Item.ScreenSize).State = EntityState.Unchanged;
db.Entry(cartItems[i].Item.OperatingSystem).State = EntityState.Unchanged;
}
db.SaveChanges();
cartItems.Clear();
return View();
}
该方法接收用户放入购物车中的所有物品并将它们添加到数据库中。如果我未将State
设置为EntityState.Unchanged
,则引用的实体将作为新记录添加到其对应的表中。
为了更好地理解,这是Item
实体和CartItem
类:
public class Item
{
public int ItemId { get; set; }
public int MemoryId { get; set; }
public int OperatingSystemId { get; set; }
public int BrandId { get; set; }
public int ScreenSizeId { get; set; }
public string Name { get; set; }
public float Price { get; set; }
public Memory Memory { get; set; }
public OperatingSystem OperatingSystem { get; set; }
public ScreenSize ScreenSize { get; set; }
}
public class CartItem
{
public int CartItemId { get; set; }
public int OrderId { get; set; }
public int ItemId { get; set; }
public int Quantity { get; set; }
public Order Order { get; set; }
public Item Item{ get; set; }
}
真正困扰我的是,如果我将用于将商品添加到购物车的代码和结帐方法进行组合,那么我根本不会遇到这个问题(注意缺少{{1 }})。
state = EntityState.Unchanged
总结并阐明我要做什么:我希望能够将上面的代码分为两部分:一个 public ActionResult ProblemFreeCheckOut()
{
// Add Items to cart...
var item1= db.Items.Include(i => i.Memory).Include(i => i.OperatingSystem).Include(i => i.ScreenSize).Where(i => i.ItemId == 1);
var item2= db.Items.Include(i => i.Memory).Include(i => i.OperatingSystem).Include(i => i.ScreenSize).Where(i => i.ItemId== 2);
CartItems List<CartItems> = new List<CartItem>();
cartItems.Add(new CartItem { Quantity = 1, ItemId = item1.First().ItemId, Item = item1.First() });
cartItems.Add(new CartItem { Quantity = 1, ItemId= item2.First().ItemId, Item = item2.First() });
// Save cart
Order order = new Order() { Payed = false, Canceled = false };
db.Orders.Add(order);
for (int i = 0; i < cartItems.Count; i++)
{
cartItems[i].Order = order;
cartItems[i].OrderId = order.OrderId;
db.CartItems.Add(cartItems[i]);
}
db.SaveChanges();
cartItems.Clear();
return View();
}
方法和一个Add
方法。 Checkout
方法会将数据库中的项目添加到将由Add
方法使用的项目列表中。我通过过度使用Checkout
来解决这个问题-真的需要吗?
非常感谢,如果您在帖子中提到了这么远,我向您致敬!
答案 0 :(得分:3)
看看Add
开始跟踪给定实体和任何其他可达实体 在“添加”状态下尚未被跟踪的 会在调用SaveChanges()时将其插入数据库。
发生的是,通过调用Add
方法,您告诉EF:“将我刚刚附加到上下文的所有内容(通过关系图)作为一个新项进行处理”。
在第二个示例中,您没有遇到问题,因为在您Include
时已经在跟踪相关实体。在那种情况下,EF仅保留Unchanged
的声明,这导致它们在SaveChanges()
期间被忽略。
纯粹从技术角度来看,您有两个选择。要么:
1)做第二个示例中的操作-在调用Add()
方法之前跟踪相关实体,以使EF知道将它们视为Unchanged
。
或
2)而不是直接添加实体图
db.CartItems.Add(cartItem);
只需附加它(默认情况下,所有实体都将被跟踪为Unchanged
),然后将您的cartItem
标记为已添加:
db.CartItems.Attach(cartItem).State = EntityState.Added;
关于最后一点-可能甚至只有db.CartItems.Attach(cartItem);
就足够了,因为EF对于没有设置主键字段的实体将状态设置为Added
。我的记忆有点模糊。无论如何,这都是放置断点并进行调试以了解EF如何跟踪魔术的好地方。关于这个主题,有一篇很棒的博客文章,我建议:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/april/data-points-why-does-entity-framework-reinsert-existing-objects-into-my-database