繁体   English   中英

EF 核心。 如何从深层嵌套实体中仅加载必要的属性

[英]EF Core. How to load only necessary properties from deep nested entity

我开发了一个简单的应用程序,类似于聊天,其中每条消息都可能包含文本和文件。 实体是这样相关的:

消息组 -> 每个消息组都有一个消息集合 -> 每个消息都有一个属性 'FileCollection' -> 'File collection' 有 4 个 collections:图像、视频、音频、文件。 它们在数据库中都有相同的关系。 为了在此处显示此逻辑,我的查询是获取所有消息组及其实体:

var messageGroups = await _db.MessageGroups
    .Where(mg => mg.UserId == id)
    .Include(m => m.Messages).ThenInclude(mes => mes.FileCollection.Images)
    .Include(m => m.Messages).ThenInclude(mes => mes.FileCollection.Video)
    .Include(m => m.Messages).ThenInclude(mes => mes.FileCollection.Audio)
    .Include(m => m.Messages).ThenInclude(mes => mes.FileCollection.Files)
    .ToListAsync();

问题是每种类型的文件(图像、音频等)在 Db(EF Core 中的属性)中都有一个“数据”列,其中包含它们的 blob 数据。 我想从查询中排除所有 blob,因为从 Db 加载所有用户文件的查询变得非常繁重。 像这样的东西(但排除方法不存在):

.Include(m => m.Messages).ThenInclude(mes => mes.FileCollection.Video).exclude(video => video.Data);

有没有办法在查询结束时使用显式加载? 或者也许有像 [JsonIgnore] 这样的属性,它从 Json 序列化中排除了 class 属性? 还是有什么其他方法?

如果有帮助:ImageFile、AudioFile 等继承自 File super class:

public class File
{
    [Column("id")]
    public int Id { get; set; }

    [Column("content_type")]
    public string ContentType { get; set; }

    [Column("file_name")]
    public string FileName { get; set; }

    [Column("length")]
    public long Length { get; set; }

    [Column("related_file_collection_id")]
    public int FileCollectionId { get; set; }

    public FileCollection FileCollection { get; set; }
}


public class ImageFile : File
{
    [Column("data")]
    public byte[] Data { get; set; }

}

我需要来自“文件”class 的所有属性,而没有来自其子类的“数据”属性。

我相信最好的方法是使用表拆分为那些包含 Blob 列的实体配置 DbContext。

不要让名字混淆你。 此技术不是将 Blob 移动到不同的表。 相反,它将允许您在同一行上放置两个“实体”。 在您的情况下,您可以从FileData中拆分File ,这意味着您将为它们中的每一个拥有不同的实体,但两者都将存储在同一张表的同一行中。

通过使用表拆分,您可以.Include您的File并且它不会包含FileData ,除非您明确告诉 EF Core 这样做。

如果您不想走 go 这条路,我相信您需要编写一些自定义选择或自定义 SQL。

您可以使用[NotMapped]属性,但是您将无法从其他查询的数据库中检索该列。

您还可以仅创建所需属性的 DTO 和 select,但考虑到您包含的所有内容,这并不优雅。

正如这里所建议的那样,表拆分可能是答案,但它有点复杂。 我刚刚使用.Select() 修改了我的查询。 不是很优雅,我也有一个循环内循环,但它的工作原理:

List<MessageGroup> messageGroups = await _db.MessageGroups.Where(mg => mg.UserId == id).AsNoTracking().AsSplitQuery().Include(m => m.Messages).ThenInclude(mes => mes.FileCollection.Images)
                                                                                                                             .Include(m => m.Messages).ThenInclude(mes => mes.UrlPreviews).ToListAsync();
foreach (var mg in messageGroups)
{
    foreach (var m in mg.Messages)
    {
         m.FileCollection.Video = await _db.Video.Where(video => video.FileCollectionId == m.FileCollection.Id).Select(v => new VideoFile(v.ContentType, v.FileName, v.Length, v.FileCollectionId, null)).ToListAsync();
         m.FileCollection.Audio = await _db.Audio.Where(audio => audio.FileCollectionId == m.FileCollection.Id).Select(a => new AudioFile(a.ContentType, a.FileName, a.Length, a.FileCollectionId, null)).ToListAsync();
         m.FileCollection.Files = await _db.Files.Where(file => file.FileCollectionId == m.FileCollection.Id).Select(f => new OtherFile(f.ContentType, f.FileName, f.Length, f.FileCollectionId, null)).ToListAsync();
    }
}

文件构造函数中的 null 是 byte[] blob 数据所在的位置。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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