如何使用 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?

如何使用 Entity Framework Core 获取主键值

如何在 Entity Framework 6 中使用 DbDataReader 获取表名?