如何在EF中通过替换来更新实体?

16

我有一个名为Order的类,其中包含一个List<Item>和一个Customer。我需要更新Order及其子类。一开始我只附加了Order对象,但这样EF不能理解ItemsCustomers的更改。

然后我尝试将子类的状态更改为EntityState.Modified,但也不起作用。在EF中是否有一种方式可以替换现有记录中的父类和子类?

如果没有,请问我该如何解决这个问题?

var temp = db.Orders.Find(order.Id);
temp = order;
temp.Items = order.Items;
temp.Customer = order.Customer; 
db.SaveChanges(); 

在更改用户操作中的顺序后,我也尝试了这个:

db.Orders.Attach(order) ;
db.SaveChanges();

你是想说你正在更改模型,并期望它对数据库产生影响吗?但实际上它对数据库没有影响。这是你的意思吗? - bluetoothfx
是的,从数据库中获取一个“订单”记录到对象中并修改它的子类,但EF不会将此更改保存在数据库中。 - Navid_pdp11
你在执行 你的对象.SaveChanges() 吗?(将“你的对象”替换为你的实际对象名称) - Jaquarh
它是否返回错误?将其包裹在 try{}catch(Exception ex){} 中,你的问题有点令人困惑,因为我尝试的功能运行良好,使用构造函数。 - Jaquarh
1
你没有正确地添加你的对象。 Db.Entry(order).State = EntityState.Modified; Db.SaveChanges(); - bluetoothfx
显示剩余5条评论
4个回答

22

2
这段代码对我有效:context.Update(temp).CurrentValues.SetValues(order);。虽然它不适用于子实体。 - Alex Zhukovskiy
1
@Alex Zhukovskiy和Dale C - 感谢您的帖子,它很好用! Dale - 请更新链接,因为返回404错误... - Matt
这很好!更新让我失望了。 - Yoda

6

如果您想用相关/子级/嵌套引用和集合替换实体,可以使用Tracked Graph自EF Core 2.2以来的版本。所有实体ID应该由数据库生成。

将这个方法添加到您的上下文中

public void Replace<TEntity>(TEntity oldEntity, TEntity newEntity) where TEntity : class
{
    ChangeTracker.TrackGraph(oldEntity, e => e.Entry.State = EntityState.Deleted);
    ChangeTracker.TrackGraph(newEntity, e => e.Entry.State = e.Entry.IsKeySet ? EntityState.Modified : EntityState.Added);
}

用法

var oldOrder = db.Orders
        .AsNoTracking()
        .Include(o => o.Items)
        .Include(o => o.Customer)
        .Find(newOrder.Id);

db.Replace(oldOrder, newOrder);

db.SaveChanges();

注意,旧实体应使用 AsNoTracking 分离或

db.Entry(oldOrder).State = EntityState.Detached;

1
在EF6中,除了Add()(但它只是添加而不是更新),没有内置的可靠方法可以附加完整的图形(包括相关项目)到上下文中。实际上,它应该工作(并附加完整的对象图),但在实践中,它不起作用且非常不一致,因此最好自己附加每个相关项目。
有第三方工具(如GraphDiff)可能有助于完成此任务(检查其扩展方法DbContext.UpdateGraph()),但没有可靠的内置方式。
我不确定EF Core是否已经实现了它,但它正在讨论中(在GitHub上有this issue going on at GitHub-虽然它是为Add而开放的,但你会在讨论中看到提到Update-)。然而,它已经关闭,我没有密切关注以知道是否有相关问题被打开。

-1
无需附加订单进行更新。请按以下方式使用:
Db.Entry(temp).State = EntityState.Modified;
Db.SaveChanges();

Db.Entry(temp).State = EntityState.Modified; - bluetoothfx
2
我在使用时遇到了这个异常:“附加类型为'DataAccess.Order'的实体失败,因为另一个具有相同主键值的实体已经存在。” - Navid_pdp11
这是因为你执行了一个Find(),将记录从数据库加载到实体缓存中。然后你分配了一个新对象并将其附加到上下文中,创建了一个具有相同键(order.ID)的新条目。当你点击SaveChanges()时,EF正确地抱怨了两个具有相同键的条目。 - Suncat2000

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接