Entity Framework Core:高效的数据库更新

Posted

技术标签:

【中文标题】Entity Framework Core:高效的数据库更新【英文标题】:Entity Framework Core: efficient database update 【发布时间】:2021-10-11 12:31:25 【问题描述】:

我有一个使用 Entity Framework Core 来更新数据库的 ASP.NET Core 3.1 应用程序。但我需要一些建议来了解我是否使用了更新给定表的好方法。

假设我有一个表TableState,其中包含三列:IDDESCRIPTIONSTATEID 是主键),下面是它的关联实体类TableState:

public class TableState

    public int ID  get; set; 
    public string Description  get; set; 
    public string State  get; set; 

鉴于TableState 对象的列表,我需要有效地更新表。

更新是特殊​​的,因为对于列表中的每个 TableState 对象:

如果我在表中找到关联记录(使用主键ID),我需要更新记录 如果在表中没有找到,需要插入到数据库中 最后,如果它存在于数据库中,但不在列表中,我需要将State 列更新为INACTIVE

这是我实现的算法,我想要一些反馈来了解它是否可以改进:

public void UpdateTableState (List<TableState> inputs)

      var currentTableStates = Context.Set<TableSet>().ToList();            

      // I retrieve all records in the database
      // One way to detect records to add or update
      foreach (TableState input in inputs)
      
            var foundTableState = currentTableState.FirstOrDefault(ts => ts.ID == input.Id);

            // The input does not exist in DB, so it must be added
            if (foundTableState == null) 
            
                Context.Set<TableSet>().Add(foundTableState);
            
            else
            
                foundTableState.Description = input.Description;
                foundTableState.State = "Active";
            
      

      // Second way to detect records to delete (set its State field to INACTIVE)
      foreach (TableState currentTableState in currentTableStates )
      
            var foundInList = inputs.FirstOrDefault(i => i.Id == currentTableState.Id);

            // The input does not exist in DB, so it must be added
            if (foundTableState == null) 
            
                  Context.Set<TableState>.Remove(currentTableState);
            
      

如果有人找到更新表格的更好方法,请告诉我。

谢谢

【问题讨论】:

使用 vanilla EF Core 这样的操作效率很低。如果您有兴趣,我可以准备带有 EF Core 扩展的示例,它使用 MERGE 运算符来处理这种情况。 如果输入不存在,它不会在属性中包含任何 id?那么你为什么不直接插入没有id。仅供参考,如果您有成千上万的数据,将所有数据都存储到内存中将是一个问题 您好 @SvyatoslavDanyliv,感谢您抽出宝贵时间,如果可能的话,我对您的 EF Core 示例感兴趣。非常感谢 【参考方案1】:

您好,感谢您的回复。

@Eldho,您的代码可能是正确的,但我还需要检测 DB 中输入集合中不存在的记录。但无论如何,谢谢你,我会尽量避免在比较之前将整个数据库表放在内存中。但我认为这是不可能的,因为:

    如果输入中不存在 DB 中的记录:我需要将其 State 列更新为 INACTIVE 否则我需要根据其关联的输入来更新它 DB 中不存在的每个输入(它们的 ID = 0)我需要将它们添加到 DB 中。

谢谢

【讨论】:

删除此评论。这不是答案。【参考方案2】:

与其将整个集合存储到内存中,不如尝试通过输入数据进行限制。

var ids = inputs.Select(x => x.Id);
var insert = inputs.Where(x=>Id==null || Id == 0).ToList() //This are data need be inserted

//This will fetch the items which exist in the table so this can be updated
var foundinTable = Context.Set<TableSet>()
                                .Where(x=> ids.Contains(x.Id)
                                .ToList();

var tobeInserted = foundIntable.Where(x> !ids.Contains(x.Id).ToList()

这减少了我们的 Dbset 以仅返回相关数据。现在你迭代它并更新它。

【讨论】:

您好,感谢您的回答,事实上,如果 ID 属性是由第三方程序预先生成的。 @J.Doe 在这种情况下,获取数据库中的记录,就像答案中的其余项目是您需要插入的一样【参考方案3】:

可以通过 EF Core 扩展 linq2db.EntityFrameworkCore 完成(免责声明:我是创建者之一)

public void UpdateTableState (List<TableState> inputs)

      Context.Set<TableSet>()
        .Merge()
        .Using(inputs)
        .OnTargetKey()
        .UpdateWhenMatched((t, s) => new TableState  Description = s.Description, State = "Active" )
        .InsertWhenNotMatched()
        .UpdateWhenNotMatchedBySource(s => new TableState  State = "Inactive" )
        .Merge();

【讨论】:

以上是关于Entity Framework Core:高效的数据库更新的主要内容,如果未能解决你的问题,请参考以下文章

Entity Framework 7 支持批量操作和 JSON 列

Entity Framework Core 6.0 预览4 性能改进

Entity Framework Core快速开始

Entity Framework Core 快速开始

EF6 System.Data.Entity.Core.EntityKey 在 Entity Framework Core 中的等价物是啥?

张高兴的 Entity Framework Core 即学即用:创建第一个 EF Core 应用