【问题标题】:Entity Framework - is excessive use of "EntityState.Unchanged" really needed?实体框架 - 真的需要过度使用“EntityState.Unchanged”吗?
【发布时间】:2019-11-09 12:42:30
【问题描述】:

感谢您抽出宝贵时间!

我目前正在对智能手机网上商店进行原型设计,作为学习 Entity Framework 的一部分,我偶然发现了一个问题,即我的数据库中的新记录会在相关表中添加额外的记录。我通过关注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; }
}

真正让我烦恼的是,如果我将用于将商品添加到购物车中的代码和结帐方法结合起来,我根本不会遇到这个问题 (请注意缺少 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,我已经完成了这项工作 - 这真的需要吗?

非常感谢,如果您在帖子中走到了这一步,我向您致敬!

【问题讨论】:

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


    【解决方案1】:

    查看 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

    【讨论】:

    • 非常感谢!看来我不太明白 DBContext 是如何工作的,你的帖子把我推向了正确的方向。这篇来自微软的文章很有帮助:link.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-10
    • 1970-01-01
    • 2013-01-09
    • 1970-01-01
    • 2019-07-18
    • 1970-01-01
    相关资源
    最近更新 更多