ASP.NET MVC3和Entity Framework Code第一个架构

Dam*_*amb 53 architecture asp.net-mvc unit-testing entity-framework code-first

我之前的问题让我再次思考层,存储库,依赖注入和这样的架构.

我的架构现在看起来像这样:
我首先使用EF代码,所以我只创建了POCO类和上下文.这创建了db和model.
级别更高的是业务层类(提供者).我为每个域使用不同的提供程序...比如MemberProvider,RoleProvider,TaskProvider等.我在每个提供程序中创建我的DbContext的新实例.
然后我在我的控制器中实例化这些提供程序,获取数据并将它们发送到Views.

我的初始架构包括存储库,我之所以摆脱它是因为我被告知它只会增加复杂性,所以为什么我不仅仅使用EF.我想这样做..直接从控制器使用EF,但我必须编写测试,它与真正的数据库有点复杂.我不得不假装 - 以某种方式模拟数据.因此,我为每个提供商创建了一个接口,并使列表中的硬编码数据成为假提供者.有了这个,我回到了一些东西,我不知道如何正确地进行.

这些事情开始过于复杂化......许多方法和"模式"......它产生了太多的噪音和无用的代码.

是否有任何SIMPLE和可测试的体系结构用于使用Entity Framework创建和ASP.NET MVC3应用程序?

Lad*_*nka 93

如果您想要使用TDD(或任何其他具有高测试覆盖率的测试方法)和EF,您必须编写集成或端到端测试.这里的问题是任何模拟上下文或存储库的方法只会创建测试,它可以测试您的上层逻辑(使用那些模拟)但不测试您的应用程序.

简单的例子:

让我们定义通用存储库:

public interface IGenericRepository<TEntity> 
{
    IQueryable<TEntity> GetQuery();
    ...
}
Run Code Online (Sandbox Code Playgroud)

然后让我们写一些商业方法:

public IEnumerable<MyEntity> DoSomethingImportant()
{
    var data = MyEntityRepo.GetQuery().Select((e, i) => e);
    ...
}
Run Code Online (Sandbox Code Playgroud)

现在,如果您模拟存储库,您将使用Linq-To-Objects,您将进行绿色测试,但如果您使用Linq-To-Entities运行应用程序,则会出现异常,因为L2E不支持选择带索引的重载.

这是一个简单的例子,但在查询中使用方法和其他常见错误也会发生同样的情况.此外,这也会影响通常在存储库中公开的Add,Update,Delete等方法.如果您没有编写一个模拟精确模拟EF上下文和参照完整性行为的模拟,那么您将无法测试您的实现.

故事的另一部分是延迟加载的问题,对于模拟的单元测试也很难检测到.

因此,您还应该引入集成或端到端测试,这些测试将使用真正的EF上下文L2E来对抗真实数据库.顺便说一句.使用端到端测试需要正确使用TDD.为了在ASP.NET MVC中编写端到端测试,您可以使用WatiN,也可以使用SpecFlow for BDD,但这确实会增加很多工作,但您的应用程序将经过测试.如果你想阅读更多关于TDD的信息,我推荐这本书(唯一的缺点是示例是用Java编写的).

如果您不使用通用存储库并且在某些类中隐藏查询而不会公开IQueryable但直接返回数据,则集成测试是有意义的.

例:

public interface IMyEntityRepository
{
    MyEntity GetById(int id);
    MyEntity GetByName(string name); 
}
Run Code Online (Sandbox Code Playgroud)

现在您可以编写集成测试来测试此存储库的实现,因为查询隐藏在此类中而不会暴露给上层.但是这种类型的存储库在某种程度上被认为是与存储过程一起使用的旧实现.使用此实现会丢失很多ORM功能,或者您将需要执行大量额外工作 - 例如,添加规范模式以便能够在上层定义查询.

在ASP.NET MVC中,您可以使用控制器级别的集成测试来部分替换端到端测试.

根据评论进行编辑:

我不是说你需要单元测试,集成测试和端到端测试.我说制作经过测试的应用程序需要更多的努力.所需测试的数量和类型取决于应用程序的复杂性,应用程序的预期未来,您的技能和其他团队成员的技能.

可以在没有测试的情况下创建小的直接项目(好吧,这不是一个好主意,但我们都做到了,最后它工作了)但是一旦项目通过一些阈值,你就会发现引入新功能或维护项目是很难,因为你永远不确定它是否会打破已经奏效的东西 - 这就是所谓的回归.对回归的最好防御是良好的自动化测试.

  • 单元测试可以帮助您测试方法.理想情况下,此类测试应涵盖方法中的所有执行路径.这些测试应该非常简短且易于编写 - 复杂的部分可以设置依赖项(模拟,faktes,存根).
  • 集成测试可帮助您测试多个层的功能,并且通常涉及多个流程(应用程序,数据库).您不需要为所有内容提供它们,更多的是选择它们有用的经验.
  • 端到端测试类似于用例/用户故事/功能的验证.它们应涵盖整个要求的流程.

不需要多次测试一个物品 - 如果您知道该功能是在端到端测试中测试的,则不需要为相同的代码编写集成测试.此外,如果您知道该方法只有集成测试所涵盖的单个执行路径,则无需为其编写单元测试.使用TDD方法可以更好地进行大型测试(端到端或集成)并深入到单元测试.

根据您的开发方法,您不必从一开始就进行多种类型的测试,但随后您可以在应用程序变得更加复杂时引入它们.TDD/BDD是一个例外,在你编写其他代码的单行之前,你应该开始至少使用端到端和单元测试.

所以你问的是错误的问题.问题不是什么更简单?问题是最终会对您有什么帮助以及您的应用程序的复杂程度如何?如果你想拥有容易经过单元测试的应用程序和业务逻辑,你应该将EF代码包装到其他可以模拟的类中.但与此同时,您必须引入其他类型的测试以确保EF代码正常工作.

我不能说你的方法适合你的环境/项目/团队/等等.但我可以从我过去的项目中解释一下例子:

我和两个同事在这个项目上工作了大约5-6个月.该项目基于ASP.NET MVC 2 + jQuery + EFv4,它是以增量和迭代方式开发的.它有很多复杂的业务逻辑和许多复杂的数据库查询.我们从通用存储库和高代码覆盖开始,使用单元测试+集成测试来验证映射(插入,删除,更新和选择实体的简单测试).几个月后,我们发现我们的方法不起作用.我们有超过1.200个单元测试,代码覆盖率约为60%(这不是很好)和很多回归问题.更改EF模型中的任何内容都可能会在几周内未触及的部件中引入意外问题.我们发现我们缺少应用程序逻辑的集成测试或端到端测试.对于在另一个项目上工作的并行团队做出了相同的结论,并且使用集成测试被视为新项目的推荐.


Kam*_*yar 13

使用存储库模式会增加复杂性吗 在你的场景中,我不这么认为.它使TDD更容易,您的代码更易于管理.尝试使用Generic存储库模式以获得更多分离和更清晰的代码.

如果您想了解有关实体框架中TDD和设计模式的更多信息,请查看:http://msdn.microsoft.com/en-us/ff714955.aspx

但是,您似乎正在寻找模拟测试实体框架的方法.一种解决方案是使用虚拟种子方法生成有关数据库初始化的数据.请查看种子部分:http://blogs.msdn.com/b/adonet/archive/2010/09/02/ef-feature-ctp4-dbcontext-and-databases.aspx

您也可以使用一些模拟框架.我所知道的最着名的是:

要查看更完整的.NET模拟框架列表,请查看:https://stackoverflow.com/questions/37359/what-c​​-mocking-framework-to-use

另一种方法是使用像SQLite这样的内存数据库提供程序.进一步研究是否有实体框架的内存提供商?

最后,这里有一些关于单元测试实体框架的好链接(有些链接指的是实体框架4.0.但你会得到这个想法.):

http://social.msdn.microsoft.com/Forums/en/adodotnetentityframework/thread/678b5871-bec5-4640-a024-71bd4d5c77ff

http://mosesofegypt.net/post/Introducing-Entity-Framework-Unit-Testing-with-TypeMock-Isolator.aspx

在单元测试中伪造我的数据库层的方法是什么?