实体框架核心遍历大Blob数据而没有内存溢出,最佳实践

Atl*_*e S 8 c# blob traversal out-of-memory entity-framework-core

我正在编写遍历大量图片数据的代码,并准备了一个大的增量块,其中包含所有压缩后用于发送的数据。

这是关于如何将这些数据作为示例

[MessagePackObject]
public class Blob : VersionEntity
{
    [Key(2)]
    public Guid Id { get; set; }
    [Key(3)]
    public DateTime CreatedAt { get; set; }
    [Key(4)]
    public string Mediatype { get; set; }
    [Key(5)]
    public string Filename { get; set; }
    [Key(6)]
    public string Comment { get; set; }
    [Key(7)]
    public byte[] Data { get; set; }
    [Key(8)]
    public bool IsTemporarySmall { get; set; }
}

public class BlobDbContext : DbContext
{
    public DbSet<Blob> Blob { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blob>().HasKey(o => o.Id);
    }
}
Run Code Online (Sandbox Code Playgroud)

使用此方法时,我会将所有内容处理到一个文件流中,并且希望在任何给定时间将其尽可能少地保留在内存中。

这样做够吗?

foreach(var b in context.Where(o => somefilters).AsNoTracking())
    MessagePackSerializer.Serialize(stream, b);
Run Code Online (Sandbox Code Playgroud)

这是否仍将用所有blob记录填充内存,还是在我对枚举数进行迭代时将它们一一处理。它不使用任何ToList,仅使用枚举器,因此Entity Framework应该可以随时处理它,但是我不确定这是否是它的功能。

这里的任何实体框架专家都可以就如何正确处理提供一些指导。

ste*_*-fu 1

一般来说,当您在实体上创建 LINQ 筛选器时,就像以代码形式编写 SQL 语句一样。它返回一个IQueryable尚未针对数据库实际执行的值。当您使用IQueryableorforeach调用迭代ToList()时,将执行 sql 并返回所有结果,并将其存储在内存中。

https://learn.microsoft.com/en-us/dotnet/framework/data/adonet/ef/language-reference/query-execution

虽然 EF 可能不是纯粹性能的最佳选择,但有一种相对简单的方法可以处理这个问题,而不必过多担心内存使用情况:

考虑以下

var filteredIds = BlobDbContext.Blobs
                      .Where(b => b.SomeProperty == "SomeValue")
                      .Select(x => x.Id)
                      .ToList();
Run Code Online (Sandbox Code Playgroud)

现在,您已根据您的要求过滤了 Blob,并针对数据库执行了此操作,但仅返回了内存中的 Id 值。

然后

foreach (var id in filteredIds)
{
    var blob = BlobDbContext.Blobs.AsNoTracking().Single(x => x.Id == id);
    // Do your work here against a single in-memory blob
}
Run Code Online (Sandbox Code Playgroud)

一旦你完成了大的blob,它就应该可以用于垃圾回收,并且你不应该耗尽内存。

显然,您可以感知检查 id 列表中的记录数量,或者您可以将元数据添加到第一个查询中,以帮助您决定如何处理它(如果您想完善这个想法)。