【问题标题】:EF Core: Getting table with Include, but Include should not get one columnEF Core:使用 Include 获取表格,但 Include 不应获得一列
【发布时间】:2021-11-06 08:43:55
【问题描述】:

我有那些模型(实体类商店 ID)

    public class TeachingProgramme : Entity
    {
        [Required]
        [MaxLength(100)]
        public string Name { get; set; }

        public virtual ICollection<TeachingProgrammeFile> TeachingProgrammeFiles { get; set; }
    }
    public class TeachingProgrammeFile : Entity
    {
        [Required]
        public byte[] Content { get; set; }

        [Required]
        [MaxLength(100)]
        public string Name { get; set; }

        [Required]
        public int TeachingProgrammeId { get; set; }

        [Required]
        public TeachingProgrammeFileType FileType { get; set; }

        public virtual TeachingProgramme TeachingProgramme { get; set; }
    }

还有一项用于获取带有文件但没有文件数据的 TeachingProgramme 的服务 (TeachingProgrammeFile.Content):

            db.TeachingProgramme teachingProgramme = await _context.TeachingProgrammes
                .Include(x => x.TeachingProgrammeFiles)
                .FirstOrDefaultAsync(x => x.Id == request.TeachingProgrammeId);

如何优化它以防止每次都从数据库中获取内容?它需要大量不必要的数据,因为我只需要 TeachingProgrammeFile Name 和 Id

【问题讨论】:

  • 这不是 Include 本身的东西;那只是JOIN;不要拉取你不想要的数据(写Select(.. props apart from Content here..)),所以它永远不会放入SELECT 并且数据库永远不会发送它
  • 您是否尝试在 LINQ 末尾添加扩展方法 Select?我认为它应该在数据库级别进行过滤。
  • 您需要这些实体进行修改吗?还是开玩笑通过控制器返回?
  • @SvyatoslavDanyliv 进行修改。我尝试了这些选择,但它不起作用
  • 那么你必须重新设计你的实体。并将内容移动到导航属性(其他表)。否则,您将坚持使用分离实体更新。

标签: c# .net-core entity-framework-core


【解决方案1】:

Select()你想要的列或者使用Table Splitting

要使用表拆分,实体类型需要映射到相同的 表,将主键映射到相同的列,并且至少 一种实体类型的主键之间配置的一种关系 和另一个在同一个表中。

表拆分的一个常见场景是仅使用 表中的列以获得更好的性能或封装。

【讨论】:

    【解决方案2】:

    我只需要 TeachingProgrammeFile Name 和 Id

    您的查询将为您提供一个完整填充的 TeachingProgramme,其中包含文件列表(包括内容),因为它实际上是您所要求的。

    不过,EF 确实是 know how to pare down the query to just what you project,例如:

    var q = _context.TeachingProgrammes
        .Include(x => x.TeachingProgrammeFiles)
        .Where(x => x.Id == request.TeachingProgrammeId)
        .Select(tp => new { tp.Name, tp.Files.Select(tpf => new { tpf.Id, tpf.Name }) });
    

    我在这个阶段还没有具体化它,所以你可以在调试器中暂停并查看查询的 DebugView 属性(如果是 EFcore5)

    您应该看到 EF 准备了一个大大简化的 SQL。这是我在这里放在一个数据库上的一个(飞机:航班是 1:M) - 查询 Q 的准备 SQL 显示在文本 vizualizer 中 - 你可以看到尽管实际表更多,但只包含少量列满(在右边)。航班也有同样多的列。

    我在这里使用了一个匿名对象,但如果您想为此目的只使用几个道具制作一个缩减 DTO/POCO,这同样适用;这是我们将数据库使用的数据实体与我们的数据实体分开的原因之一。用户界面使用


    还请考虑您可能根本不需要加入 TeachingProgramme;您可以通过链接回TP的关系ID查询文件;如果您将 id/name 发送到 UI,用户键入一个新名称,然后当它返回时,您可以伪造一个表示文件的新本地实体(id 和新名称),或者直接更新它

    注意:
    斯维亚托斯拉夫的评论值得注意;如果 EF 可以从您在 Select 中编写的内容中计算出您需要连接的内容,则不需要 Include..

    【讨论】:

    • 为什么我不喜欢这样的答案——它们总是包含Include,在Select 之后,EF 完全忽略了它。我想做出类似的答案,但 OP 需要实体进行修改。不跟踪通过Select 投射的实体。
    • 你不必拉下来更新它;你可以伪造一个本地的,附上它并发送更新。 stackoverflow.com/questions/50779937/… - 当然有些人会说“但这太难了..”但实际上更多的是因为它是一个边缘案例。不将字节存储在数据库中将是更好的选择 iMHO;文件系统用于文件
    • 是的,在 cmets 中提到过;) 变通方法。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-08-09
    • 2014-10-19
    相关资源
    最近更新 更多