2

我有一个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并投影到TranslatedWidgetDTO

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.

谢谢

4

2 回答 2

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

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

于 2013-04-02T16:03:33.633 回答
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只需要一次,这将导致TOP(1)SQL 中只有一个子查询,而不是三个:

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在此处使用,First在 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
});
于 2013-04-02T15:21:17.920 回答