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

时间:2013-04-02 12:08:39

标签: linq ef-code-first entity-framework-5 code-first

我有一个包含核心成员和多个Widget子级的父实体WidgetTranslation,其中包含语言翻译成员,即Description文本,提供英语,法语,德语等。

e.g。

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&amp;投射到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上的更多属性。

由于

2 个答案:

答案 0 :(得分:3)

使用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
                  });

我假设您在给定语言中每个小部件只有一个翻译。

答案 1 :(得分:0)

我会将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()
});

您必须在此使用FirstOrDefault,LINQ-to-Entities投影中不支持First

如果你不想要那个嵌套类型,你可以首先投射到匿名类型然后转换成你的最终类,但代码会更长一些:

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
});