asp.net-mvc - 使用 IRepository 进行 Entity Framework 测试 - 延迟加载问题

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

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

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

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

顺便说一句,我正在考虑将实体模型背后的数据源更改为 xml 或某些对象数据源,这样我就可以将 linq 保留到实体。我发现它不支持开箱即用......这也很可悲:这意味着 Entity Framework 意味着数据库源 - 不是真正有用的抽象。

具体例子:

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

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

将返回此 View 的 Controller 操作应该从某处获取数据。

修改前的代码:

    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 只需要人的名字)。到目前为止,我还有 3 种不同的文章 View 模型。例如,如果有人想查看文章列表,则无需获取语言、正文和其他一些属性,但是获取出版商的名称(位于深处)。在“可测试”代码之前, Controller 操作可能只包含 linq to entities 查询并使用延迟加载、Include 函数、使用子查询、引用外部属性 (Publisher.Name) 获取他们需要的任何数据......因此,没有对数据库的不必要查询,也没有从数据库传输不必要的数据。

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

最佳答案

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

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

关于asp.net-mvc - 使用 IRepository 进行 Entity Framework 测试 - 延迟加载问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3734233/

相关文章:

javascript - 如何使用 JSON 将 C# 对象转换为 Javascript 数组数组?

c# - 无法隐式列出到 IEnumerable

c# - 如何在自引用关系上设置级联?

java - 如何通过 PowerMockito 模拟多个静态类

asp.net-mvc - 如何将 Moq 与 Entity Framework IDbSet Count() 等方法一起使用

c# - 如何在 ASP.NET MVC 3 Entity Framework 中使用 DBContext 映射存储过程

entity-framework - Entity Framework 扩展抛出 DynamicProxy 异常

asp.net - 服务器响应时间太长(ASP.NET MVC),如何查找哪个操作消耗了这么长的时间?

java - 使用Spring Security的Principal进行单元测试方法

javascript - 西农(Sinon)监视函数表达式