我正在使用 Entity Framework Core 7,但我对此很陌生。
我的场景如下。用户通过 UI 输入编辑的信息,该信息存储在名为
Upload
的实体类实例中。
我可能有许多已编辑的实体,因此我很高兴找到
ExecuteUpdate()
方法,该方法只会进行一次数据库调用,而不是使用 ChangeTracking
和 SaveChanges
会对每个实体进行一次调用。
但是,我似乎无法在代码中找到一种方法来设置每个实体的每个已编辑属性的值。
这是我到目前为止的代码(不起作用):
var editedUploads = editViewModel.SelectedIncomingStatements
.Select((d,Index) => new Upload() { Id = d.Id , BankRoyaltiesReceived = d.BankRoyaltiesReceived, ExchangeRate = d.ExchangeRate, Tag = Index});
var editedUploadIds = editedUploads.Select(u => u.Id).ToHashSet();
var erColl = new Collection<double>(editedUploads.Select(u => u.ExchangeRate).ToList());
var rrColl = new Collection<double>(editedUploads.Select(u => u.BankRoyaltiesReceived).ToList());
using (var trans = await ctx.Database.BeginTransactionAsync(cancellationToken))
{
try
{
var q = from u in ctx.Uploads
let c = editedUploadIds.Contains(u.Id)
where c == true
select u;
await q.ExecuteUpdateAsync(setters => setters.SetProperty(p => p.BankRoyaltiesReceived, u => rrColl[(int)u.Tag]).SetProperty(p => p.ExchangeRate, u => erColl[(int)u.Tag]));
await trans.CommitAsync(cancellationToken);
}
catch (Exception)
{
await trans.RollbackAsync(cancellationToken);
throw;
}
}
主要问题是我能够从
exColl
中的 2 个集合 rrColl
和 SetProperty
中“索引”出值,以便我为相应的 Upload
应用正确的编辑值.
幸运的是,集合似乎是由 EF Core 翻译的,因此使用似乎还可以。我用有效的集合和硬编码索引对此进行了测试。我最初尝试使用字典,但根据文档,这是不允许的。
所以我现在要做的就是为集合提供可转换为 EF Core 的索引。
正如您在代码中看到的,我最后一次尝试是将
[NotMapped]
属性添加到 .Tag
实体上的 Upload
属性,并使用 2 个集合中所需的索引来设置它。遗憾的是,这不起作用,因为 EF 翻译抱怨 .Tag
是 [NotMapped]
!
因此,我对如何使用
ExecuteUpdate()
为多个实体设置多个属性感到困惑。
我觉得我一定错过了一些明显的东西,因为我认为这种情况正是
ExecuteUpdate()
的用途?
有什么建议吗?
预先感谢您的帮助。
您应该为此使用表值参数。
首先定义一个表类型。
CREATE TYPE dbo.Upload AS TABLE (
Id bigint PRIMARY KEY,
BankRoyaltiesReceived decimal(19,9),
ExchangeRate decimal(19,9)
);
在 EF Core 8+ 中,您可以对此使用
ctx.Database.SqlQuery<Upload>
。但对于 EF Core 7,您需要在模型中定义它
modelBuilder.Entity<Upload>().HasNoKey.ToView(null);
现在您可以将其作为参数传递
var table = new DataTable { Columns = {
{ "Id", typeof(long) },
{ "BankRoyaltiesReceived", typeof(decimal) },
{ "ExchangeRate", typeof(decimal) },
} };
foreach (var d in editViewModel.SelectedIncomingStatements)
{
table.Add(d.Id, d.BankRoyaltiesReceived, d.ExchangeRate,);
}
// must do this in a separate step from the rest of the query
var tvp = ctx.Set<Upload>.FromSqlRaw(
"SELECT * FROM @tmp",
new SqlParameter("@tmp", table) { TypeName = "dbo.Upload" }
);
//
var q =
from u in ctx.Uploads
join t in tvp on u.Id = t.Id
select new { u, t };
await using var trans = await ctx.Database.BeginTransactionAsync(cancellationToken);
await q.ExecuteUpdateAsync(setters => setters
.SetProperty(ut => ut.u.BankRoyaltiesReceived, ut => u.t.BankRoyaltiesReceived)
.SetProperty(ut => ut.u.ExchangeRate, ut => ut.t.ExchangeRate);
await trans.CommitAsync(cancellationToken);
请注意,如果您有
using
,则无需在错误时手动回滚。如果您所做的只是一笔大交易,那么您就不需要进行交易 UPDATE
。