如何使用 Entity Framework Core 在一种方法中保存两次更改
Posted
技术标签:
【中文标题】如何使用 Entity Framework Core 在一种方法中保存两次更改【英文标题】:How to save changes twice in one method using Entity Framework Core 【发布时间】:2017-08-25 00:57:45 【问题描述】:使用 Entity Framework Core 我试图为同一个实体集合保存两次更改。
public async Task Save(IEnumerable<Request> request)
var entities = request.Select(x => new MyEntity()
FileName = x.FileName,
Status = Status.Downloading
);
// save as downloading
await _dbContext.MyFiles.AddRangeAsync(entities).ConfigureAwait(false);
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
// start downloading
await _storage.DownloadFilesAsync(request).ConfigureAwait(false);
// save as downloaded
foreach (var entity in entities)
entity.Status = Status.Downloaded;
// this save DOES NOT update the entities with status Downloaded
await _dbContext.SaveChangesAsync().ConfigureAwait(false);
第一次调用SaveChanges()
方法会在数据库中创建状态为Downloading
的实体。
然后我将实体的状态修改为Downloaded
并再次调用SaveChanges()
。但是,这一次实体不会在 DB 中更新。
我认为实体已经加载到 Db 上下文中,所以我不必在第二个 SaveChanges
之前重新加载它们。只需修改属性就会将它们标记为已修改。但它不起作用。
更新 1 我更新了如下代码并将状态显式设置为已修改
// save as downloaded
foreach (var entity in entities)
entity.Status = Status.Downloaded;
// The exception occurs at line below on 2nd loop
_dbContext.Entry(entity).State = EntityState.Modified;
现在我遇到异常
System.InvalidOperationException:实体类型的实例 无法跟踪“MyEntity”,因为此类型的另一个实例 已使用相同的密钥进行跟踪。添加新实体时, 对于大多数键类型,如果没有,将创建一个唯一的临时键值 键已设置(即,如果为键属性分配了默认值 它的类型)。如果您要为新实体显式设置键值, 确保它们不会与现有实体或临时值发生冲突 为其他新实体生成。附加现有实体时, 确保只有一个具有给定键值的实体实例 依附于上下文。在 Microsoft.EntityFrameworkCore.ChangeTracking.Internal.IdentityMap`1.Add(TKey 键,InternalEntityEntry 条目)在 Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.StartTracking(InternalEntityEntry 条目)在 Microsoft.EntityFrameworkCore.ChangeTracking.Internal.InternalEntityEntry.SetEntityState(EntityState oldState, EntityState newState, Boolean acceptChanges)
实体具有int
类型的Id
属性,并且Id
属性也具有[Key]
属性。
public class MyEntity
[System.ComponentModel.DataAnnotations.Key]
public int Id get;set;
public string FileName get;set;
public string Status get;set;
更新 2
所以在做了一点调试之后,我发现第一个SaveChanges()
操作是在数据库中创建记录,并按预期为每条记录分配唯一的 ID。 Id
是数据库中自动递增的标识列。
但是,实体不会使用新 ID 刷新。所以在第一次保存更改后所有实体的值仍然为 0。
如何使用新创建的 ID 刷新这些实体?
在foreach
循环内我尝试重新加载为
_dbContext.Entry<OcrFile>(entity).ReloadAsync().ConfigureAwait(false);
但这并没有刷新。
【问题讨论】:
您是否尝试过将实体状态标记为已修改?_dbContext.Entry(entity).State = EntityState.Modified;
@Steve _dbContext 如何匹配实例,_dbContext 如何知道将哪个实体标记为已修改?
【参考方案1】:
解决了!!在将实体传递给AddRange
方法之前,我必须使用ToList()
或ToArray()
急切地加载实体
var entities = request.Select(x => new MyEntity()
FileName = x.FileName,
Status = Status.Downloading
).ToList();
我不确定这是 EF 中的错误,但我为该问题创建了一个单独的 SO thread
【讨论】:
以上是关于如何使用 Entity Framework Core 在一种方法中保存两次更改的主要内容,如果未能解决你的问题,请参考以下文章
Entity Framework 学习系列 - 认识理解Entity Framework
如何使用 Entity Framework Core 模拟异步存储库
如何使用 Entity Framework 生成和自动递增 Id
如何使用 Entity Framework Core 正确保存 DateTime?