提问人:AZ Chad 提问时间:11/17/2023 最后编辑:AZ Chad 更新时间:11/17/2023 访问量:53
Entity Framework Core,如何不保存子记录?
Entity Framework Core, how do I NOT save children records?
问:
使用如下所示的模式,当我保存 时,它会影响我不希望它做的表。Vehicle
VehicleOrders
我正在使用但不起作用,其他想法?[NotMapped]
从哪个执行更新:Vehicle
UpdateVehicle
[NotMapped]
public ICollection<VehicleOrder> VehicleOrders { get; set; }
[NotMapped]
public ICollection<VehicleOrder> VehicleOrders1 { get; set; }
public async Task<RadzenBlazorServerApp.Models.sql_database.Vehicle> UpdateVehicle(int id, RadzenBlazorServerApp.Models.sql_database.Vehicle vehicle)
{
OnVehicleUpdated(vehicle);
var itemToUpdate = Context.Vehicles
.Where(i => i.Id == vehicle.Id)
.FirstOrDefault();
if (itemToUpdate == null)
{
throw new Exception("Item no longer available");
}
var entryToUpdate = Context.Entry(itemToUpdate);
entryToUpdate.CurrentValues.SetValues(vehicle);
entryToUpdate.State = EntityState.Modified;
Context.SaveChanges();
OnAfterVehicleUpdated(vehicle);
return vehicle;
}
更新:
史蒂夫·派,谢谢你。这是从 Radzen Blazor Studio 自动生成的代码(平均而言,这真是太棒了),因此可以理解。它是一个 Blazor Server 应用。 什么都不做,它们只是存根。OnVehicleUpdated
OnAfterVehicleUpdated
var isSameReference = object.ReferenceEquals(itemToUpdate, vehicle);
确实和代码一样,所以整个事情可以简单地归结为true
public async Task<RadzenBlazorServerApp.Models.sql_database.Vehicle> UpdateVehicle(int id, RadzenBlazorServerApp.Models.sql_database.Vehicle vehicle)
{
Context.SaveChanges();
return vehicle;
}
所以它一定是,就像你说的,已经加载或其他地方了。Attached
代码中有一个可用的方法,可以分离所有内容:Reset()
public void Reset() => Context.ChangeTracker.Entries().Where(e => e.Entity != null).ToList().ForEach(e => e.State = EntityState.Detached);
现在您的支票是确认的。isSameReference
false
虽然我可能会精简一些东西,但为了与自动生成的代码保持连续性,我只是将它们放在方法的顶部:Reset()
Update Vechile
public async Task<RadzenBlazorServerApp.Models.sql_database.Vehicle> UpdateVehicle(int id, RadzenBlazorServerApp.Models.sql_database.Vehicle vehicle)
{
OnVehicleUpdated(vehicle);
**Reset();**
var itemToUpdate = Context.Vehicles
.Where(i => i.Id == vehicle.Id)
.FirstOrDefault();
if (itemToUpdate == null)
{
throw new Exception("Item no longer available");
}
var entryToUpdate = Context.Entry(itemToUpdate);
entryToUpdate.CurrentValues.SetValues(vehicle);
entryToUpdate.State = EntityState.Modified;
Context.SaveChanges();
OnAfterVehicleUpdated(vehicle);
return vehicle;
}
至于正在发生的事情,由于缺乏更好的术语,表中的数据被“打乱”了,我敢肯定它被系统地“打乱”了,但我没有解开这个谜语。我的感觉是因为表中多次引用了相同的内容。继续测试,该页面是一个网格,每个网格都有一个向下钻取和一个弹出窗口的编辑链接。我认为,如果向下钻取是打开的,那么问题就会发生,这可能是有道理的,因为向下钻取会获取数据,并可能将一些东西附加到在表中创建“错误”更新的 DbContext。VehicleOrders
Vehicle
VehicleOrders
Vehicles
VehicleOrders
答:
SetValues
不深入研究导航属性,并且不需要更改 EntityState,只需设置值并调用 .因此,您的代码可以简化为:SaveChanges
public async Task<RadzenBlazorServerApp.Models.sql_database.Vehicle> UpdateVehicle(int id, RadzenBlazorServerApp.Models.sql_database.Vehicle vehicle)
{
OnVehicleUpdated(vehicle); // What code is listening to this?
var itemToUpdate = await Context.Vehicles
.Where(i => i.Id == vehicle.Id)
.FirstOrDefaultAsync();
if (itemToUpdate == null)
{
throw new Exception("Item no longer available");
}
itemToUpdate.CurrentValues.SetValues(vehicle); // Change tracking will do it's job here.
await Context.SaveChangesAsync();
OnAfterVehicleUpdated(vehicle); // and what code is listening to this?
return vehicle;
}
请注意,使用“itemToUpdate”时,我们并不急于加载 VehicleOrders,因此在从传入的车辆复制值时,实际更新的实体甚至不会跟踪订单,除非在此调用之前加载了这些订单。
那么接下来的问题是,你说的“影响VehicleOrders”到底是什么意思,更新车辆后你看到了什么意想不到的效果?集合是否被映射并不重要,如果你更新了一辆车,并且相关数据发生了变化,那么你要么有其他代码潜伏在某个地方进行更改,要么在数据库级别上有一些东西,比如触发器在做一些事情。这是传递实体的一个问题,因为如果您有任何代码将这辆分离的车辆传递到其中,然后执行类似操作并将其发送到 Modified,那么可以肯定的是,您最终可能会覆盖或复制数据库中的数据。传递实体似乎很方便,但容易出现混淆行为。Attach
EntityState
是什么提供/填充了被传入的车辆?它是从 Web 控制器方法中传入的参数构造的吗?它是一个未跟踪的实体吗?还是被跟踪的实体?
一个很好的测试:
var itemToUpdate = await Context.Vehicles
.Where(i => i.Id == vehicle.Id)
.FirstOrDefaultAsync();
var isSameReference = object.ReferenceEquals(itemToUpdate, vehicle);
那应该回来.如果是这样,则表示传入的车辆已经是被跟踪的车辆引用,并且此方法中的所有代码都完全没有意义。这可能意味着车辆是从 DbContext 加载的,或者在某个时间点加载到 DbContext,因此该车辆实例中的任何内容都是 EF 打算保存的内容。false
true
Attached
我要看的下一个地方是当您向 VehicleUpdated 和 AfterVehicleUpdated 发出信号时正在执行的代码。
评论
entryToUpdate.State = EntityState.Modified
不需要 EF Core 应自动检测更改。Async
await ... FirstOrDefaultAsync()
await Context.SaveChangesAsync();
CurrentValues.SetValues(vehicle)