Asp.net mvc 使用IRepository进行实体框架测试-延迟加载问题

Asp.net mvc 使用IRepository进行实体框架测试-延迟加载问题,asp.net-mvc,unit-testing,entity-framework,testing,entity-framework-4,Asp.net Mvc,Unit Testing,Entity Framework,Testing,Entity Framework 4,我正在重构一个MVC项目,使其可测试。目前,控制器直接使用实体框架的上下文对象来请求所需的数据。我开始抽象这个,但它就是不起作用。最终我有了一个iSeries和一个IRepository抽象,但是为了描述这个问题,让我们看看IRepository。许多人建议使用返回以下函数的接口:iQuery、IEnumerable、IList、SomeEntityObject、SomeDTO。然后,当需要测试服务层时,可以使用一个类来实现接口,该类不会返回到数据库 问题:将linq用于工具集中已延迟加载的实体

我正在重构一个MVC项目,使其可测试。目前,控制器直接使用实体框架的上下文对象来请求所需的数据。我开始抽象这个,但它就是不起作用。最终我有了一个iSeries和一个IRepository抽象,但是为了描述这个问题,让我们看看IRepository。许多人建议使用返回以下函数的接口:iQuery、IEnumerable、IList、SomeEntityObject、SomeDTO。然后,当需要测试服务层时,可以使用一个类来实现接口,该类不会返回到数据库

问题:将linq用于工具集中已延迟加载的实体。这实际上非常有用,因为我的控制器操作函数知道视图需要哪些数据,我没有要求更多。但是,linq to anythingelse没有延迟加载。因此,当我的IRepository函数返回上述任何内容时,我将丢失延迟加载。我用诸如“GetAnything”和“GetAnythingDeep”之类的函数扩展了我的接口,但这还不够:它必须更细粒度。这将为相同类型的对象生成大约5-6个函数,具体取决于我希望在结果中获得的属性。可能是一个带有“include properties”参数的普通函数,但我也不喜欢这样

最后,我想如果我想让它可测试,结果要么效率低得多,要么代码复杂得多。听起来不对

顺便说一句,我正在考虑将实体模型后面的数据源更改为xml或某些对象数据,这样我就可以保留实体的linq。我发现它不支持开箱即用。。。这也是令人伤心的:这意味着实体框架意味着数据库源,而不是一个真正有用的抽象

具体示例:

实体对象: 文章、语言、人物。关系:文章可以有1-N种语言和一个人(出版商)

ViewModel对象: ArticleDeepViewModel:包含文章的所有属性,包括语言和人名(用于查看文章,因此不需要人名的其他属性)

将返回此视图的控制器操作应该从某处获取数据

修改前的代码:

    using (var context = new Entities.Articles())
        {
            var article = (from a in context.Articles.Include("Languages")
                       where a.ID == ID
                       select new ViewArticleViewModel()
                       {
                       ID = a.ID,
                       Headline = a.Headline,
                       Summary = a.Summary,
                       Body = a.Body,
                       CreatedBy = a.CreatedByEntity.Name,
                       CreatedDate = a.CreatedDate,
                       Languages = (from l in context.Languages select new ViewLanguagesViewModel() { ID = l.ID, Name = l.Name, Selected = a.Languages.Contains(l) })}).Single();
        this.ViewData.Model = article;
    }
    return View();
修改后的代码可能类似于:

var article = ArticleService.GetArticleDeep(ID);
var viewModel = /* mapping */
this.ViewData.Model = viewModel;
return View();
问题是GetArticleDeep应该返回一个包含语言的Article对象,并包含整个Person对象(它不应该知道viewmodel只需要Person的名称)。到目前为止,我还为一篇文章提供了3种不同的ViewModel。例如,如果有人想查看文章的列表,则不必获取语言、正文和其他一些属性,但是获取发布者的名称(位于最深处)可能会很有用。在“可测试”代码之前,控制器操作可以只包含linq to entities查询,并使用延迟加载、Include函数、子查询、引用外部属性(Publisher.Name)。。。因此,不会对数据库进行不必要的查询,也不会从数据库传输不必要的数据


iSeries或IRepository接口应该提供什么来获取3-4个不同级别的文章对象,或者有时是这些对象的列表?

不确定您是否打算坚持延迟加载,但是如果您想要一种灵活的方式将快速加载集成到存储库和服务层中,请首先查看本文:

他基本上为您提供了一种构建强类型包含策略的方法,如下所示:

var strategy = new IncludeStrategy<Article>();
strategy.Include(a => a.Author);

您能否发布您的代码:控制器、存储库,并解释您希望单元测试的部分?已编辑。我希望它能让事情变得更好,而不是更糟:)
public IQueryable<Article> Find(Expression<Func<Article, bool>> criteria, IncludeStrategy<Article> includes)
{
    var query = includes.ApplyTo(context.Articles).Where(criteria);
    return query;
}