使用IRepository进行实体框架测试 - 延迟加载的问题

时间:2010-09-17 09:45:13

标签: asp.net-mvc unit-testing entity-framework testing entity-framework-4

我正在重构MVC项目以使其可测试。目前,Controller直接使用Entity Framework的上下文对象来请求所需的数据。我开始抽象这个,它只是不起作用。最终我有一个IService和一个IRepository抽象,但为了描述这个问题,我们只看一下IRepository。许多人建议使用返回其中一些功能的界面: IQueriable< ...&gt ;, IEnumerable< ...&gt ;, IList< ...>,SomeEntityObject,SomeDTO 。然后,当想要测试服务层时,他们可以使用一个类来实现接口,该类不会返回数据库以返回这些接口。

问题:使用linq到实体我在我的工具集中有懒惰(延迟)加载。这实际上非常有用,因为我的控制器动作函数知道视图需要哪些数据,而且我没有要求超过要求。但是 linq to anythingelse没有延迟加载。因此,当我的IRepository函数返回上述任何内容时,我会失去延迟加载。我使用“GetAnything”和“GetAnythingDeep”等功能扩展了界面,但这还不够:它必须更精细。对于相同类型的对象,这将导致5-6个函数,具体取决于我想在结果中获得的属性。也许可能是一个带有“include properties”参数的通用函数,但我也不喜欢它。

最终atm我认为如果我想让它可测试会导致效率低得多或代码复杂得多。听起来不对。

顺便说一下,我正在考虑将实体模型背后的数据源更改为xml或某些对象数据,因此我可以将linq保留给实体。我发现它不支持开箱即用......这也很难过:这意味着实体框架意味着数据库源 - 而不是真正有用的抽象。

具体示例:

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

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

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

修改前的代码:

    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应该返回一个包含Languages的Article对象,并且包含整个Person对象(它不应该知道viewmodel只需要Person的Name)。到目前为止,我还有3个不同的视图模型用于文章。例如,如果有人想要查看文章列表,则无需获取语言,正文和其他一些属性,但获取发布者的名称可能很有用(深的)。在“可测试”代码之前,控制器操作可以只包含linq到实体查询,并使用延迟加载,包含函数,使用子查询,引用外部属性(Publisher.Name)来获取所需的任何数据...因此,对数据库没有不必要的查询,也没有从数据库传输不必要的数据。

IService或IRepository接口应该提供什么来获取3-4个不同级别的Article对象或有时列出这些对象?

1 个答案:

答案 0 :(得分:0)

不确定您是否计划坚持延迟加载,但如果您想要一种灵活的方式将热切加载集成到您的存储库和服务层中,请首先查看这篇文章:

http://blogs.msdn.com/b/alexj/archive/2009/07/25/tip-28-how-to-implement-include-strategies.aspx

他基本上为你提供了一种构建强类型包含策略的方法:

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

然后可以将其传递到存储库或服务层上的常规方法。这样您就不必为每种情况都有单独的方法(即您的GetArticleDeep方法)。

以下是使用上述包含策略的示例存储库方法:

public IQueryable<Article> Find(Expression<Func<Article, bool>> criteria, IncludeStrategy<Article> includes)
{
    var query = includes.ApplyTo(context.Articles).Where(criteria);
    return query;
}