【问题标题】:Entity Framework testing with IRepository - problem with lazy loading使用 IRepository 进行实体框架测试 - 延迟加载问题
【发布时间】:2010-09-17 23:03:06
【问题描述】:

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

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

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

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

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

【问题讨论】:

  • 您能否发布您的代码:控制器、存储库并解释您想要单元测试的部分?
  • 已编辑。我希望它让它变得更好而不是更糟:)

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


【解决方案1】:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-12-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多