最有效的实体框架代码第一方法展平/投影具有特定子实体的父实体

Posted

技术标签:

【中文标题】最有效的实体框架代码第一方法展平/投影具有特定子实体的父实体【英文标题】:most efficient Entity Framework Code First method of flattening / projecting parent entity with specific child 【发布时间】:2013-04-02 12:08:38 【问题描述】:

我有一个父实体 Widget 有核心成员和多个 WidgetTranslation 子实体有语言翻译成员,即Description 文本可用英语、法语、德语等。

例如

public class Widget

  public int Id  get; set; 
  public string Code  get; set; 
  public virtual ICollection<WidgetTranslation> WidgetTranslations  get; set; 


public class WidgetTranslation

  public int WidgetId  get; set;  
  public virtual Widget Widget  get; set; 

  public int LanguageId  get; set; 
  public virtual Language Language  get; set; 

  public string Name  get; set; 
  public string Description  get; set; 
  public string Summary  get; set; 
 

查询小部件集合的最有效方法是什么,将给定的LanguageId 展平并投影到TranslatedWidget DTO

public class TranslatedWidget

  public int Id  get; set; 
  public string Code  get; set; 
  public int LanguageId  get; set; 
  public virtual Language Language  get; set; 
  public string Name  get; set; 
  public string Description  get; set; 
  public string Summary  get; set; 
 

鉴于languageId我已经开始了

DbSet.Select(w => new TranslatedWidget
  
    Id = w.Id,
    Code = w.Code,
    LanguageId = w.LanguageId,
    Name = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Name,
    Description = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Description,
    Summary = w.WidgetTranslations.First(wt=>wt.LanguageId == languageId).Summary
  );

但我觉得这效率低下,并且无法扩展 WidgetTranslation 上的更多属性。

谢谢

【问题讨论】:

【参考方案1】:

使用 SelectMany 通过单个连接来展平结构:

var widgetQuery = from w in dbSet.Widgets
                  from wt in w.WidgetTranslations
                  where wt.Language == languageId
                  select new TranslatedWidget
                  
                     Id = w.Id,
                     Code = w.Code,
                     LanguageId = w.LanguageId,
                     Name = wt.Name,
                     Description = wt.Description,
                     Summary = wt.Summary
                  );

我在这里假设您对于给定语言的每个小部件只有一个翻译。

【讨论】:

谢谢。我很少看到回答者投票支持其他人的答案。 感谢 Jim,生成的 EF SQL 更清晰。 @JimWooley 您提到使用 SelectMany 但您的示例仅适用于 Select - 我错过了什么吗? 在幕后from ... from语法被翻译成.SelectMany【参考方案2】:

我会将 NameDescriptionSummary 移动到 DTO 的嵌套类中...

public class TranslatedWidgetTranslation

    public string Name  get; set; 
    public string Description  get; set; 
    public string Summary  get; set; 


public class TranslatedWidget

    public int Id  get; set; 
    public string Code  get; set; 
    public int LanguageId  get; set; 
    public TranslatedWidgetTranslation Translation  get; set; 

然后您可以投影到该类中并且只需要一次 First,这将导致 SQL 中只有一个 TOP(1) 子查询而不是三个:

DbSet.Select(w => new TranslatedWidget

    Id = w.Id,
    Code = w.Code,
    LanguageId = languageId,
    Translation = w.WidgetTranslations
        .Where(wt => wt.LanguageId == languageId)
        .Select(wt => new TranslatedWidgetTranslation
        
            Name = wt.Name,
            Description = wt.Description,
            Summary = wt.Summary
        )
        .FirstOrDefault()
);

您必须在此处使用FirstOrDefaultFirst 在 LINQ-to-Entities 投影中不受支持。

如果你不想要那种嵌套类型,你可以先投影成匿名类型,然后再转换成你的最终类,但是代码会有点长:

DbSet.Select(w => new

    Id = w.Id,
    Code = w.Code,
    LanguageId = languageId,
    Translation = w.WidgetTranslations
        .Where(wt => wt.LanguageId == languageId)
        .Select(wt => new
        
            Name = wt.Name,
            Description = wt.Description,
            Summary = wt.Summary
        )
        .FirstOrDefault()
)
.AsEnumerable()
.Select(x => new TranslatedWidget

    Id = x.Id,
    Code = x.Code,
    LanguageId = x.LanguageId,
    Name = x.Translation != null ? x.Translation.Name : null,
    Description = x.Translation != null ? x.Translation.Description : null,
    Summary = x.Translation != null ? x.Translation.Summary : null
);

【讨论】:

以上是关于最有效的实体框架代码第一方法展平/投影具有特定子实体的父实体的主要内容,如果未能解决你的问题,请参考以下文章

使用实体框架代码最有效地处理创建、更新、删除

我需要帮助理解与实体框架上下文的第一种方法相关的 C# 语法 [重复]

如何使用未绑定到具有实体框架代码优先的特定表的序列?

如何创建可重用的实体框架投影表达式?

该属性必须是有效的实体类型,并且该属性应该具有非抽象的 getter 和 setter。实体框架 [重复]

在d3js中寻找具有特定投影的世界地图