在实体框架中使用自定义表达式嵌套包含集合

本文关键字:表达式 嵌套 包含 集合 自定义 实体 框架 | 更新日期: 2023-09-27 18:15:21

根据这篇文章,将私有集合属性暴露给实体框架,我试图保持模型中的集合属性不被暴露。这里展示的技术可以很好地用于单个关卡,但我无法将其用于多个关卡。

在我的示例模型中,我有训练器。培训师有培训课程。培训班有培训班。我在Trainer中定义了以下内容:

    public class ORMappings
    {
        public const string TrainingCoursesCollectionName = nameof(Trainer._trainingCourses);
        public static Expression<Func<Trainer, ICollection<TrainingCourse>>> TrainingCourses
        {
            get { return t => t._trainingCourses; }
        }
    }

这很好(包括一个集合深度),我可以像这样引用它:

return _dbContext.Set<Trainer>()
    .Include(Trainer.ORMappings.TrainingCoursesCollectionName);

// using System.Data.Entity extension
return _dbContext.Set<Trainer>()
    .Include(Trainer.ORMappings.TrainingCourses);

我指的是关于如何包含嵌套集合属性的文档。它建议以下操作应该起作用:

先包含一个集合,然后再下一个集合:查询包含(e => e. level1collection)。选择(l1 =>l1.Level2Collection)) .

然而,我似乎找不到一种方法来使用。select()包括使用上面所示的强类型表达式的每个TrainingCourse引用的TrainingClasses (TrainingCourse有一个类似的ORMappings类的TrainingClasses列表)。我是否尝试在ORMappings类之外这样做(例如在存储库或类似的地方),或者在我可以直接访问我的私人_trainingCourses列表的地方,这并不重要。我很高兴有一个单独的ORMappings表达式,除了TrainingCourses,还包括TrainingClasses,如果我能让它工作的话。我的目标是能够指定应该在集合树中填充对象的深度,以便只在给定操作需要时加载数据。

作为额外参考,以下是Trainer如何定义其与TrainingCourse的关系:

private List<TrainingCourse> _trainingCourses { get; } = new List<TrainingCourse>();
public IEnumerable<TrainingCourse> TrainingCourses
{
    get
    {
        return _trainingCourses
            .Where(tc => tc.IsActive)
            .AsEnumerable();
    }
}

在实体框架中使用自定义表达式嵌套包含集合

好吧,恕我冒昧,我认为你正在尝试做一些非常奇怪的事情。如果您想"保护"源代码,您应该尝试探索封装关键字,如internalprotected。如果您希望将域类从基础结构中分离出来,那么您应该创建不同的项目。如果你想把域分成几个部分,使用EntityFramework搜索BoundedContext策略。

回到你的问题,在你目前的情况下,我认为解决这个问题最简单的方法是创建一个新属性:

public static Expression<Func<Trainer, IEnumerable<ICollection<TrainingClass>>>> TrainingCoursesWithClasses
{
    get { return t => t._trainingCourses.Select(i => i.TrainingClasses); }
}

未测试的代码,但如果你当前的代码可以工作,这应该也可以工作。

另外,要注意:

public IEnumerable<TrainingCourse> TrainingCourses
{
    get
    {
        return _trainingCourses
            .Where(tc => tc.IsActive)
            .AsEnumerable();
    }
}

这可能会生成IEnumerable的多个枚举。如果你想每次获得属性时只获得"IsActive == true",你应该创建一个DbCommandTreeInterceptor

此外,没有理由将集合设为"私有"并创建返回该集合的公共表达式;没有任何理由。可以使用:

轻松访问该集合
var traininCoursesLambda = Trainer.TrainingCourses;
var trainingCourses = traininCoursesLambda.Compile().Invoke(trainer);

希望有帮助!