ASP.NET MVC3 和实体框架代码优先架构 [英] ASP.NET MVC3 and Entity Framework Code first architecture

查看:27
本文介绍了ASP.NET MVC3 和实体框架代码优先架构的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

My previous question made me think again about layers, repository, dependency injection and architectural stuff like this.

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

My architecture now looks like this:
I am using EF code first, so I just made POCO classes, and context. That creates db and model.
Level higher are business layer classes (Providers). I am using different provider for each domain... like MemberProvider, RoleProvider, TaskProvider etc. and I am making new instance of my DbContext in each of these providers.
Then I instantiate these providers in my controllers, get data and send them to Views.

我最初的架构包括存储库,我摆脱了它,因为我被告知它只会增加复杂性,所以为什么我不只使用 EF.我想这样做.. 直接从控制器使用 EF,但我必须编写测试,这对于真正的数据库来说有点复杂.我不得不伪造 - 以某种方式模拟数据.所以我为每个提供者制作了一个接口,并用列表中的硬编码数据制作了假提供者.有了这个,我又回到了一些我不确定如何正确进行的事情上.

My initial architecture included repository, which I got rid of because I was told that it just adds complexity, so why I don't just use EF only. I wanted to did that.. working with EF directly from controllers, but I have to write tests and it was a bit complicate with real database. I had to fake - mock data somehow. So I made an interface for each provider and made fake providers with hardcoded data in lists. And with this I got back to something, where I am not sure how to proceed correctly.

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

These things starts to be overcomplicated too quickly... many approaches and "pattterns"... it creates just too much noise and useless code.

是否有任何简单且可测试的架构来使用实体框架创建和 ASP.NET MVC3 应用程序?

推荐答案

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

If you want to use TDD (or any other testing approach with high test coverage) and EF together you must write integration or end-to-end tests. The problem here is that any approach with mocking either context or repository just creates test which can test your upper layer logic (which uses those mocks) but not your application.

简单例子:

让我们定义通用存储库:

Let's define generic repository:

public interface IGenericRepository<TEntity> 
{
    IQueryable<TEntity> GetQuery();
    ...
}

让我们写一些商业方法:

And lets write some business method:

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

现在,如果您模拟存储库,您将使用 Linq-To-Objects 并且您将进行绿色测试,但是如果您使用 Linq-To-Entities 运行应用程序,您将收到一个异常,因为在L2E.

Now if you mock the repository you will use Linq-To-Objects and you will have a green test but if you run the application with Linq-To-Entities you will get an exception because select overload with indexes is not supported in L2E.

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

This was simple example but same can happen with using methods in queries and other common mistakes. Moreover this also affects methods like Add, Update, Delete usually exposed on repository. If you don't write a mock which will exactly simulate behavior of EF context and referential integrity you will not test your implementation.

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

Another part of story are problems with Lazy loading which can also hardly be detected with unit tests against mocks.

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

Because of that you should also introduce integration or end-to-end tests which will work against real database using real EF context ane L2E. Btw. using end-to-end tests is required to use TDD correctly. For writing end-to-end tests in ASP.NET MVC you can WatiN and possibly also SpecFlow for BDD but this will really add a lot of work but you will have your application really tested. If you want to read more about TDD I recommend this book (the only disadvantage is that examples are in Java).

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

Integration tests make sense if you don't use generic repository and you hide your queries in some class which will not expose IQueryable but returns directly data.

示例:

public interface IMyEntityRepository
{
    MyEntity GetById(int id);
    MyEntity GetByName(string name); 
}

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

Now you can just write integration test to test implementation of this repository because queries are hidden in this class and not exposed to upper layers. But this type of repository is somehow considered as old implementation used with stored procedures. You will lose a lot of ORM features with this implementation or you will have to do a lot of additional work - for example add specification pattern to be able to define query in upper layer.

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

In ASP.NET MVC you can partially replace end-to-end tests with integration tests on controller level.

根据评论进行

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

I don't say that you need unit tests, integration tests and end-to-end tests. I say that making tested applications require much more effort. The amount and types of needed tests is dependent on the complexity of your application, expected future of the application, your skills and skills of other team members.

无需测试也可以创建简单的小型项目(好吧,这不是一个好主意,但我们都做到了,最后它奏效了)但是一旦项目通过了某个门槛,您就会发现引入新功能或维护该项目非常困难,因为您永远不确定它是否会破坏已经起作用的东西 - 这称为回归.对回归的最佳防御是良好的自动化测试集.

Small straighforward projects can be created without tests at all (ok, it is not a good idea but we all did it and at the end it worked) but once a project passes some treshold you can find that introducing new features or maintaining the project is very hard because you are never sure if it breaks something which already worked - that is called regression. The best defence against regression is good set of automated tests.

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

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

It is not needed to test a feture multiple times - if you know that the feature is tested in end-to-end test you don't need to write integration test for the same code. Also if you know that method has only single execution path which is covered by integration test you don't need to write unit test for it. This works much better with TDD approach where you start with a big test (end-to-end or integration) and go deeper to unit tests.

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

Depending on your developement approach you don't have to start with multiple types of test from beginning but you can introduce them later as your application will become more complex. The exception is TDD/BDD where you should start to use at least end-to-end and unit tests before you even write single line of other code.

所以你问错了问题.问题不是什么更简单?问题是什么最终会对您有所帮助,以及适合您的应用程序的复杂性是什么?如果您想轻松地对应用程序和业务逻辑进行单元测试,您应该将 EF 代码包装到其他一些可以模拟的类中.但同时您必须引入其他类型的测试以确保 EF 代码有效.

So you are asking the wrong question. The question is not what is simpler? The question is what will help you at the end and what complexity fits your application? If you want to have easily unit tested application and business logic you should wrap EF code to some other classes which can be mocked. But in the same time you must introduce other type of tests to ensure that EF code works.

我不能告诉你哪种方法适合你的环境/项目/团队等.但我可以解释我过去项目的例子:

I can't say you what approach will fit your environment / project / team / etc. But I can explain example from my past project:

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

I worked on the project for about 5-6 months with two collegues. The project was based on ASP.NET MVC 2 + jQuery + EFv4 and it was developed in incremental and iterative way. It had a lot of complicated business logic and a lot of complicated database queries. We started with generic repositories and high code coverage with unit tests + integration tests to validate mapping (simple tests for inserting, deleting, updating and selecting entity). After few months we found that our approach doesn't work. We had more then 1.200 unit tests, code coverage about 60% (that is not very good) and a lot of regression problems. Changing anything in EF model could introduce unexpected problems in parts which were not touched for several weeks. We found that we are missing integration tests or end-to-end tests for our application logic. The same conclusion was made on a parallel team worked on another project and using integration tests was considered as recommendation for new projects.

这篇关于ASP.NET MVC3 和实体框架代码优先架构的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆