删除实体而不将其加载到内存中

Mig*_*ura 10 linq-to-entities entity-framework-core

在Entity Framework Core中,我有以下实体:

public class File {
  public Int32 Id { get; set; }
  public Byte[] Content { get; set; }
  public String Name { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

我有一个我需要删除的文件ID列表:

List<Int32> ids = new List<Int32> { 4, 6, 8 }; // Ids example
Run Code Online (Sandbox Code Playgroud)

如何在不加载每个文件内容属性的情况下删除3个文件?

_context.Files.Remove(??);
Run Code Online (Sandbox Code Playgroud)

我不想加载每个文件的Content属性,因为它的大小很大.

Luk*_* Vo 31

EF Core 7 现在支持ExecuteUpdate 和 ExecuteDelete(批量更新)

// Delete all Tags (BE CAREFUL!)
await context.Tags.ExecuteDeleteAsync();

// Delete Tags with a condition
await context.Tags.Where(t => t.Text.Contains(".NET")).ExecuteDeleteAsync();
Run Code Online (Sandbox Code Playgroud)

等效的 SQL 查询是:

DELETE FROM [t]
FROM [Tags] AS [t]

DELETE FROM [t]
FROM [Tags] AS [t]
WHERE [t].[Text] LIKE N'%.NET%'
Run Code Online (Sandbox Code Playgroud)


Iva*_*oev 18

如果您确定数据库中存在所有 Id 并且上下文不包含(不跟踪)具有相同键的其他实体,则可以使用简单的假(存根)实体:

_context.RemoveRange(ids.Select(id => new File { Id = id }));
Run Code Online (Sandbox Code Playgroud)

为避免不存在的 id 出现问题,您可以从数据库中获取现有的 id:

var existingIds = _context.Files.Where(f => ids.Contains(f.Id)).Select(f => f.Id).ToList();

_context.RemoveRange(existingIds.Select(id => new File { Id = id }));
Run Code Online (Sandbox Code Playgroud)

为避免跟踪实体问题,您可以使用FindTracked在 EntityFrameworkCore 中按 ID 删除加载和卸载的对象的答案中的自定义扩展方法,并将其与上述任何方法结合使用。

var existingIds = _context.Files.Where(f => ids.Contains(f.Id)).Select(f => f.Id).ToList();

_context.RemoveRange(
    existingIds.Select(id => _context.FindTracked(id) ?? new File { Id = id }));
Run Code Online (Sandbox Code Playgroud)

  • @Toolkit (1) 加载 PK 与加载完整记录不同。(2) EF确实不利于大规模修改 (8认同)
  • `.ToList();` != "没有将它们加载到内存中" (7认同)
  • @Toolkit 不是“正确的”?我不需要检查你的方法。我知道 **3rd party** 库,但它不是 EF **开箱即用**解决方案(实际上它绕过了 EF)。可以使用具有相同效果的普通 SQL。这并不意味着库不好 - 只是它不是“标准”解决方案,有些人不想/不允许使用 3rd 方库。不管怎样,有不同意见是你的权利,只是不要告诉我什么是“正确”。 (4认同)

Abl*_*lue 12

实体跟踪可以手动工作,无需任何数据库调用,只要您可以唯一标识实体。

您所追求的内容记录在此处

var entity = new EntityModel {
   Id = yourId
};

var entry = context.Entry(entity);
entry.State = EntityState.Deleted;
context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

这与...相同

var entity = new EntityModel {
   Id = yourId
};

var entry = context.Entry(entity);
context.Remove(entry);
context.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

  • @Ablue,我不敢相信我已经使用 EF 这么久了,而且刚刚学会了这个技巧。绝对精彩。如果我知道如何给你汇款的话,我现在就请你喝一杯。 (3认同)
  • 这是我知道的唯一方法,但我真的很希望它可以通过 Queryable 来完成。 (2认同)

M K*_*aei 6

根据您的dotnet 版本安装Z.EntityFramework.ExtensionsZ.EntityFramework.Extensions.EFCore包。

然后在查询后使用DeleteFromQuery() 或DeleteFromQueryAsync() 方法。

await _dbContext.MyTable.Where(w => w.TypeId == 5).DeleteFromQueryAsync();
Run Code Online (Sandbox Code Playgroud)

DeleteFromQuery 使您可以直接在数据库中执行 DELETE 语句,并在无需选择和加载对象的情况下提供巨大的性能改进。

性能比较:

运营:1,000 个实体 - 2,000 个实体 - 5,000 个实体

保存更改:1,000 毫秒 - 2,000 毫秒 - 5,000 毫秒

从查询删除:1 毫秒 - 1 毫秒 - 1 毫秒