[英]Bulk Update in Entity Framework Core
我从数据库中提取了一堆时间表条目并使用它们来创建发票。 保存发票并获得 ID 后,我想使用发票 ID 更新时间表条目。 有没有一种方法可以批量更新实体而不用一次加载一个实体?
void SaveInvoice(Invoice invoice, int[] timeEntryIds) {
context.Invoices.Add(invoice);
context.SaveChanges();
// Is there anything like?
context.TimeEntries
.Where(te => timeEntryIds.Contains(te.Id))
.Update(te => te.InvoiceId = invoice.Id);
}
免责声明:我是Entity Framework Plus项目的所有者
我们的图书馆有一个批量更新功能,我相信这是你正在寻找的
此功能支持 EF Core
// Is there anything like? YES!!!
context.TimeEntries
.Where(te => timeEntryIds.Contains(te.Id))
.Update(te => new TimeEntry() { InvoiceId = invoice.Id });
Wiki: EF 批量更新
编辑:回答评论
它是否像您的示例一样支持包含? 我认为这来自 EF Core,它在 3.1 版本中甚至不受支持
EF Core 3.x 支持包含: https : //dotnetfiddle.net/DAdIO2
编辑:回答评论
这很好,但这需要类的零参数公共构造函数。 这不是一个伟大的。 有什么办法可以解决这个问题?
从 EF Core 3.x 开始支持匿名类型
context.TimeEntries
.Where(te => timeEntryIds.Contains(te.Id))
.Update(te => new { InvoiceId = invoice.Id });
在线示例: https : //dotnetfiddle.net/MAnPvw
如果TimeEntry
与Invoice
有关联(检查导航属性),您可能可以执行以下操作:
var timeEntries = context.TimeEntries.Where(t => timeEntryIds.Contains(te.Id)).ToArray();
foreach(var timeEntry in timeEntries)
invoice.TimeEntries.Add(timeEntry);
context.Invoices.Add(invoice);
//save the entire context and takes care of the ids
context.SaveChanges();
您追求简化语法的性能吗?
我建议使用直接 SQL 查询,
string query = "Update TimeEntries Set InvoiceId = <invoiceId> Where Id in (comma separated ids)";
context.Database.ExecuteSqlCommandAsync(query);
对于逗号分隔的 ID,您可以执行string.Join(',', timeEntryIds)
这取决于您实际需要什么。 如果你想使用 Linq,那么你需要遍历每个对象。
在实体框架核心中,您可以使用更新范围方法。 你可以在这里看到一些示例用法。
using (var context = new YourContext())
{
context.UpdateRange(yourModifiedEntities);
// or the followings are also valid
//context.UpdateRange(yourModifiedEntity1, yourModifiedEntity2, yourModifiedEntity3);
//context.YourEntity.UpdateRange(yourModifiedEntities);
//context.YourEntity.UpdateRange(yourModifiedEntity1, yourModifiedEntity2,yourModifiedEntity3);
context.SaveChanges();
}
Entity Framework Core 5.0 中引入的
IQueryable.ToQueryString<\/code><\/a>方法可能有助于这种情况。
此方法将生成可包含在原始 SQL 查询中的 SQL,以对该查询标识的记录执行批量更新。
例如:
void SaveInvoice(Invoice invoice, int[] timeEntryIds) {
context.Invoices.Add(invoice);
context.SaveChanges();
var query = context.TimeEntries
.Where(te => timeEntryIds.Contains(te.Id))
.Select(te => te.Id);
var sql = $"UPDATE TimeEntries SET InvoiceId = {{0}} WHERE Id IN ({query.ToQueryString()})";
context.Database.ExecuteSqlRaw(sql, invoice.Id);
}
EF 7 支持批量更新:
context
.TimeEntries
.Where(te => timeEntryIds.Contains(te.Id))
.ExecuteUpdate(s => s.SetProperty(
i => te.InvoiceId,
i => invoice.Id));
此方法 ExecuteUpdateAsync 也有异步版本。
在 EF Core 7 中,使用 ExecuteUpdate(), 新功能
var multipleRows = TableA.Where(t=>t.Id < 99);
multipleRows.ExecuteUpdate(t=>
t.SetProperty(
r => r.Salary,
r => r.Salary * 2));
//SQL already sent to database, do not run below
//SaveChanges();
SQL 由 EF 生成
UPDATE [t]
SET [t].[Salary] = [t].[Salary] * 2
FROM [TableA] AS [t]
WHERE [t].[ID] < 99
该库允许在 C# 中编写 SQL,实体定义取自 EF Core 映射。 在可能的情况下,集合方法被映射到相应的 SQL 结构( Contains
包含)。
因此所需的查询将类似于以下内容:
var invoiceId = invoice.Id;
context.Database.Query((TimeEntry te) => {
UPDATE(te).SET(() => {
te.InvoiceId = invoiceId;
});
WHERE(timeEntryIds.Contains(te.Id));
});
类似小提琴: https : //dotnetfiddle.net/LzLhO0
从 EFCore 7.0 开始,您将看到内置的BulkUpdate()
和BulkDelete
方法:
context.Customers.Where(...).BulkDelete();
context.Customers.Where(...).BulkUpdate(c => new Customer { Age = c.Age + 1 });
context.Customers.Where(...).BulkUpdate(c => new { Age = c.Age + 1 });
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.