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)
我们怎样才能在一次通话中做到这一点?谢谢。
现在我想做两件事:
首先进行一次数据库调用并获取整个分支列表的完整对象(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)