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

时间:2015-09-29 18:19:01

标签: c# entity-framework

根据这篇文章Exposing Private Collection Properties to Entity Framework,我试图让我的模型中的集合属性不被公开。该技术证明了单个级别包含的工作正常,但我无法让它在多个级别上工作。

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

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

我指的是the documentation on how to include nested collection properties。它表明以下内容应该有效:

  

要包含一个集合,然后将一个集合包含在一个级别:   query.Include(e =&gt; e.Level1Collection.Select(l1 =&gt;   l1.Level2Collection))。

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

有关其他参考,以下是Trainer如何定义其与TrainingCourse的关系:

private List<TrainingCourse> _trainingCourses { get; } = new List<TrainingCourse>();

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

1 个答案:

答案 0 :(得分:0)

嗯,恕我直言,我认为你正在尝试做一些非常奇怪的事情。如果您想“保护”源代码,则应尝试探索封装关键字,例如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);

希望它有所帮助!