实体框架:如何从相关实体中选择特定列
Posted
技术标签:
【中文标题】实体框架:如何从相关实体中选择特定列【英文标题】:Entity Framework: How to select specific columns from a related entity 【发布时间】:2022-01-23 23:08:08 【问题描述】:我有一个 PlayerContext
模型,其中包含许多 BlueprintContext
模型。像这样:
public class PlayerContext
public PlayerContext()
[Key]
public int id get; set;
...
public List<BlueprintContext> Blueprints get; set;
...
public class BlueprintContext
[Key]
public int id get; set;
public Dictionary<Coord3, Chonxels> BigData = new Dictionary<Coord3, Chonxels>() ;
public Dictionary<BlueprintIngredient, int> recipe = new Dictionary<BlueprintIngredient, int>();
public string Name get; set;
public int CreatorId get; set;
public PlayerContext Creator get; set;
在BlueprintContext
中,BigData
字段可以变得非常大。因此,当我加载PlayerContext
时,我想要Blueprints
,但我只想要id
、recipe
和Name
字段(而不是BigData
)。
有没有办法加载PlayerContext
并在没有BigData
字段的情况下包含我需要的BlueprintContext
字段?
这是我尝试过的
using (var db = new EfContext())
PlayerContext playerContext = db.Players
.Where(p => p.id == playerId)
...
.Include(p => p.Blueprints.Select(b => new b.id, b.recipe, b.Name))
.AsNoTracking() // disables change tracking
.FirstOrDefault();
我得到了这个异常:
System.InvalidOperationException: The expression 'p.Blueprints.AsQueryable().Select(b => new <>f__AnonymousType43`3(id = b.id, recipe = b.recipe, Name = b.Name))' is invalid inside an 'Include' operation, since it does not represent a property access: 't => t.MyProperty'. To target navigations declared on derived types, use casting ('t => ((Derived)t).MyProperty') or the 'as' operator ('t => (t as Derived).MyProperty'). Collection navigation access can be filtered by composing Where, OrderBy(Descending), ThenBy(Descending), Skip or Take operations. For more information on including related data, see http://go.microsoft.com/fwlink/?LinkID=746393.
我正在使用 Entity Framework Core。
【问题讨论】:
这能回答你的问题吗? select specific columns when using include statement with entity framework 【参考方案1】:这是考虑投影***实体的原因之一。例如,如果您想显示玩家及其蓝图的摘要详细信息:
[Serializable]
public class PlayerSummaryViewModel
public int Id get; set;
public string Name get; set;
// Any other fields the view actually needs.
public IList<BlueprintSummaryViewModel> Blueprints get; set; = new List<BlueprintSummaryViewModel>();
public class BlueprintSummaryViewModel
public int Id get; set;
public string Name get; set;
public string Recipe get; set;
然后通过Select
或Automapper的ProjectTo
进行投影:
using (var db = new EfContext())
PlayerViewModel player = db.Players
.Where(p => p.id == playerId)
.Select(p => new PlayerSummaryViewModel
Id = p.Id,
Name = p.Name,
// ...
Blueprints = p.Blueprints.Select(b => new BlueprintSummaryViewModel
Id = b.Id,
Name = b.Name,
Recipe = b.Recipe
).ToList()
).Single();
使用 automapper,因为属性本质上是具有一致命名的 1 对 1 映射,映射本质上看起来像:
var config = new MapperConfiguration(cfg =>
cfg.CreateMap<Player, PlayerSummaryViewModel>();
cfg.CreateMap<Blueprint, BlueprintSummaryViewModel>();
;
using (var db = new EfContext())
PlayerViewModel player = db.Players
.Where(p => p.id == playerId)
.ProjectTo<PlayerSummaryViewModel>(config)
.Single();
可以为所有视图模型集中配置或按需创建配置。
为什么像您建议的那样执行选择性包含这样的事情会很糟糕的一个原因是,任何接受实体的方法都应该能够预期传递的实体是域状态的完整表示,或者至少是完整的 -能代表。如果您有一个采用 Player 实体的方法,则该方法不需要开销来检查该播放器是否包含蓝图,或者是否可以包含蓝图,或者可能只获得一些精简版本的蓝图。视图模型或 DTO 确保传递的类型适合用途,并且很清楚预期是什么。
【讨论】:
以上是关于实体框架:如何从相关实体中选择特定列的主要内容,如果未能解决你的问题,请参考以下文章