Entity Framework - 真的需要过度使用 "EntityState.Unchanged" 吗?
Entity Framework - is excessive use of "EntityState.Unchanged" really needed?
感谢您抽出宝贵时间!
作为学习的一部分,我目前正在制作智能手机网上商店的原型 Entity Framework,我偶然发现了一个问题,即数据库中的新记录会在相关表中添加额外的记录。我按照这个 post .
设法解决了这个问题
但是,每当我想添加一条记录时,为每个实体添加 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
class:
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; }
}
真正让我烦恼的是,如果我将用于将商品添加到购物车的代码和结帐方法结合起来,我根本不会遇到这个问题(注意缺少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
方法使用的项目列表中。我已经通过过度使用 EntityState.Unchanged
实现了这个功能 - 这真的需要吗?
非常感谢你在 post 中走到这一步,我向你致敬!
查看 Add(TEntity) 方法的文档:
Begins tracking the given entity, and any other reachable entities
that are not already being tracked, in the Added state such that they
will be inserted into the database when SaveChanges() is called.
那么发生的事情是,通过调用 Add
方法,您告诉 EF:"treat everything I just attached to the context (via the relation graph) as a new item".
在第二个示例中,您不会遇到问题,因为当您 Include
相关实体时,它们已经被跟踪。在那种情况下,EF 只是保持 Unchanged
声明,这导致它们在 SaveChanges()
.
期间被忽略
纯粹从技术角度来看,您有两种选择。或者:
1) 执行第二个示例中的操作 - 在调用 Add()
方法之前跟踪相关实体,以便 EF 知道将它们视为 Unchanged
.
或
2) 而不是直接添加实体图
db.CartItems.Add(cartItem);
只需附上它(默认情况下所有实体都将被跟踪为 Unchanged
),然后将您的 cartItem
标记为已添加:
db.CartItems.Attach(cartItem).State = EntityState.Added;
关于最后一点 - 对于未设置主键字段的实体,EF 将状态设置为 Added
甚至可能仅 db.CartItems.Attach(cartItem);
就足够了。我对那件事的记忆有点模糊。在任何情况下,这是放置断点和调试以查看 EF 如何发挥其跟踪魔力的好地方。关于这个主题有一篇很棒的博客post,我推荐:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/april/data-points-why-does-entity-framework-reinsert-existing-objects-into-my-database
感谢您抽出宝贵时间!
作为学习的一部分,我目前正在制作智能手机网上商店的原型 Entity Framework,我偶然发现了一个问题,即数据库中的新记录会在相关表中添加额外的记录。我按照这个 post
但是,每当我想添加一条记录时,为每个实体添加 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
class:
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; }
}
真正让我烦恼的是,如果我将用于将商品添加到购物车的代码和结帐方法结合起来,我根本不会遇到这个问题(注意缺少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
方法使用的项目列表中。我已经通过过度使用 EntityState.Unchanged
实现了这个功能 - 这真的需要吗?
非常感谢你在 post 中走到这一步,我向你致敬!
查看 Add
Begins tracking the given entity, and any other reachable entities that are not already being tracked, in the Added state such that they will be inserted into the database when SaveChanges() is called.
那么发生的事情是,通过调用 Add
方法,您告诉 EF:"treat everything I just attached to the context (via the relation graph) as a new item".
在第二个示例中,您不会遇到问题,因为当您 Include
相关实体时,它们已经被跟踪。在那种情况下,EF 只是保持 Unchanged
声明,这导致它们在 SaveChanges()
.
纯粹从技术角度来看,您有两种选择。或者:
1) 执行第二个示例中的操作 - 在调用 Add()
方法之前跟踪相关实体,以便 EF 知道将它们视为 Unchanged
.
或
2) 而不是直接添加实体图
db.CartItems.Add(cartItem);
只需附上它(默认情况下所有实体都将被跟踪为 Unchanged
),然后将您的 cartItem
标记为已添加:
db.CartItems.Attach(cartItem).State = EntityState.Added;
关于最后一点 - 对于未设置主键字段的实体,EF 将状态设置为 Added
甚至可能仅 db.CartItems.Attach(cartItem);
就足够了。我对那件事的记忆有点模糊。在任何情况下,这是放置断点和调试以查看 EF 如何发挥其跟踪魔力的好地方。关于这个主题有一篇很棒的博客post,我推荐:
https://docs.microsoft.com/en-us/archive/msdn-magazine/2013/april/data-points-why-does-entity-framework-reinsert-existing-objects-into-my-database