繁体   English   中英

Entity Framework Core 中的批量更新

[英]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

如果TimeEntryInvoice有关联(检查导航属性),您可能可以执行以下操作:

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

免责声明:我是ELinq项目的维护者

该库允许在 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM