1

我有一个这样的表结构:

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

这里 ID 和 Code 形成复合主键,即在上表中,您不能有 ID = 2 和 Code = 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();
}

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

4

1 回答 1

5

现在我想做两件事:

首先进行一次数据库调用并获取整个分支列表的完整对象(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();
于 2018-05-11T19:16:51.420 回答