Entity Framework Core 读取和更新多行 - 复合主键

Var*_*rma 1 c# entity-framework composite-key dbcontext entity-framework-core

我有一个这样的表结构:

ID Code  Value
1  2     text1
2  3     text3
2  4     text4
Run Code Online (Sandbox Code Playgroud)

这里 ID 和 Code 形成复合主键,即在上表中不能有 ID = 2 和 Code = 4 的另一行。

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

public class Branch
{
 public int ID {get; set;}
 public int Code {get; set;}
}
Run Code Online (Sandbox Code Playgroud)

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

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

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

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

目前我正在循环中执行此操作,因此效率低下。

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

我们怎样才能在一次通话中做到这一点?谢谢。

Iva*_*oev 6

现在我想做两件事:

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

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

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

第二部分很简单 - 只需将SaveChanges()调用移到循环之外即可。

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

.Where(e => listOfBranch.Any(b => b.ID == e.ID && b.Code == e.Code))
Run Code Online (Sandbox Code Playgroud)

所有这些都不会转换为 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)
)
Run Code Online (Sandbox Code Playgroud)

您可以使用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);
Run Code Online (Sandbox Code Playgroud)

和用法:

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