最有效的实体框架代码第一方法展平/投影具有特定子实体的父实体
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】:
我会将 Name
、Description
和 Summary
移动到 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
,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
);
【讨论】:
以上是关于最有效的实体框架代码第一方法展平/投影具有特定子实体的父实体的主要内容,如果未能解决你的问题,请参考以下文章
我需要帮助理解与实体框架上下文的第一种方法相关的 C# 语法 [重复]