代码之家  ›  专栏  ›  技术社区  ›  Varun Sharma

实体框架核心读取和更新多行-复合主键

  •  1
  • Varun Sharma  · 技术社区  · 6 年前

    我有这样一个表结构:

    ID Code  Value
    1  2     text1
    2  3     text3
    2  4     text4
    

    此处ID和代码构成复合主键,即在上表中,您不能有ID=2、代码=4的另一行。

    现在我们使用的是实体框架核心,我有一个名为Branch的类(表示复合键),它如下所示:

    public class Branch
    {
     public int ID {get; set;}
     public int Code {get; set;}
    }
    

    其次,我有一个 List<Branch> 也现在我想做两件事:

    首先进行一次数据库调用,并获取整个分支列表的完整对象(ID、代码、值)。

    之后,我将更改每个对象的“值”。

    然后我想进行一次数据库调用,并为每行保存更新的“值”。

    目前我在循环中这样做,所以效率很低。

    foreach(var x in listOfBranch)
    {
      var record = context.Table.Find(x.ID, x.Code);
      record.Value = "new value";
      context.SaveChanges();
    }
    

    我们如何在一次通话中做到这一点?谢谢

    1 回复  |  直到 6 年前
        1
  •  5
  •   Ivan Stoev    6 年前

    现在我想做两件事:

    首先进行一次数据库调用,并获取整个分支列表的完整对象(ID、代码、值)。

    之后,我将更改每个对象的“值”。

    然后我想进行一次数据库调用,并为每行保存更新的“值”。

    第二部分很简单-只需移动 SaveChanges() 在循环外部调用。

    第一部分很棘手。在撰写本文时(EF Core 2.0.2), Contains 只能与单个属性一起使用,并连接到内存中的集合, Concat 基于查询或类似筛选器

    .Where(e => listOfBranch.Any(b => b.ID == e.ID && b.Code == e.Code))
    

    所有这些都不会转换为SQL,而是在本地进行计算。

    使用单个数据库查询获得所需结果的唯一方法是动态构建如下筛选条件:

    .Where(e =>
        (e.ID == listOfBranch[0].ID && e.Code == listOfBranch[0].Code)
        ||
        (e.ID == listOfBranch[1].ID && e.Code == listOfBranch[1].Code)
        // ...
        ||
        (e.ID == listOfBranch[N-1].ID && e.Code == listOfBranch[N-1].Code)
    )
    

    您可以使用 Expression 按如下方式对方法进行分类(只需替换 Record 实体类型):

    var parameter = Expression.Parameter(typeof(Record));
    
    var body = listOfBranch
        .Select(b => Expression.AndAlso(
            Expression.Equal(Expression.Property(parameter, "ID"), Expression.Constant(b.ID)),
            Expression.Equal(Expression.Property(parameter, "Code"), Expression.Constant(b.Code))))
        .Aggregate(Expression.OrElse);
    
    var predicate = Expression.Lambda<Func<Record, bool>>(body, parameter);
    

    以及用法:

    foreach (var record in context.Table.Where(predicate))
    {
        record.Value = "new value";
    }
    context.SaveChanges();