实体框架-确实需要过度使用“ EntityState.Unchanged”吗?

时间:2019-11-09 12:42:30

标签: c# asp.net entity-framework datatables record

感谢您抽出宝贵的时间!

作为学习实体框架的一部分,我目前正在对智能手机网上商店进行原型制作,我偶然发现了一个问题,即数据库中的新记录会在相关表中添加其他记录。我通过关注此帖子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来解决这个问题-真的需要吗?

非常感谢,如果您在帖子中提到了这么远,我向您致敬!

1 个答案:

答案 0 :(得分:3)

看看Add (TEntity)方法的文档:

  

开始跟踪给定实体和任何其他可达实体   在“添加”状态下尚未被跟踪的   会在调用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