【问题标题】: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,但我只想要idrecipeName 字段(而不是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。

【问题讨论】:

标签: entity-framework entity-framework-core


【解决方案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 确保传递的类型适合用途,并且很清楚预期是什么。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-01-15
    • 2014-10-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多