NHibernate Best Practices with ASP.NET, 1.2nd Ed (4.1) NHibernate基本整合-架构备注

 
NHibernate 基本整合
When developing an application, my primary goals are to:
  • Write code once and only once.
  • Focus on simplicity and readability.
  • Keep coupling and dependencies to a minimum.
  • Maintain a clean separation of concerns.
  • Do all of the above using test-driven development.
当开发程序的过程中,我的基本目标是:
  • 写代码写一次并且只有一次.
  • (代码)简单并且容易读.
  • 保证耦合依赖最少化
  • Maintain a clean separation of concerns.(保证关注点之间完全分离)
  • 使用测试驱动开发完成以上所有的目标.
This section discusses using these design principles for the integration of NHibernate into ASP.NET applications. We'll do this by dissecting the internals of the Basic NHibernate Sample.
这个章节讨论NHibernate Asp.net程序整合的设计原理,我们将会这些设计原理应用到Basic NHibernate 的例子中.
架构备注
The basic sample should not, necessarily, be seen as a reusable framework for your own ASP.NET application. The focus within this example application is on presenting NHibernate integration in a simple and concise manner. If you are looking for a "ready for the real-world" architecture, be sure to take a look at Extending the Basics to an "Enterprise" Solution after reviewing this section. With that said, the basic sample does present a number of best practice techniques and design patterns:
The basic Sample》的示例不能重用在你的Asp.net项目中,这个示例的中心是以简练的方式介绍NHibernate的整合。如果你想找一个能够用于实际项目中的架构,那么就需要关注在以后章节中的《Extending the Basics to an "Enterprise" Solution》解决方案。而这个简单的示例中会介绍很多最佳实践和设计模式
分离接口(Separated Interface
Separated Interface, also known as Dependency Inversion, is a technique for establishing a clean separation of concerns between application tiers. This technique is described by Martin Fowler, by Object Mentor, and in further detail in Robert Martin's Agile Software Development. The technique is often used for cleanly separating a data access layer from a domain layer. For example, assume a Customer object - in the domain layer - needs to communicate with a data access object (DAO) to retrieve a number of past Orders. (Whether or not the domain layer should ever communicate directly with a DOA is left for another discussion.) Consequently, the Customer object has a dependency on the Order DAO - in the data layer. But for the Order DAO to return orders, the DAO requires a dependency back to the domain layer.
接口分离, 也称依赖倒置 ,这项技术目的是在应用层之间建立一个完全分离的关注点. 这个技术由  Martin Fowler,Object Mentor发明 ,并进一步在 Robert Martin's 的《Agile Software Development》讲述. 这个技术经常被使用在完整地从领域层中分离数据访问层. 例如, 假设一在领域层的客户对象,需要和数据访问层中的DAO通信,从中获取已有的订单. (这里不讨论领域层直接和DAO通信是否正确) 因此, 在数据层中,这个客户对象就依赖于数据访问层中订单数据访问对象(OrderDAO.而订单对象需要把订单数据返回,因而DAO也就依赖领域层。
The simplest solution is to put the data layer, containing the DAOs, into the same physical assembly as the domain layer. To maintain a "virtual" separation of concerns, the containing project could include two folders, one named Domain and the other named Data. (I've actually used this approach on a good-sized project, in a former life, with regrettable consequences.) This approach brings with it a number of ill effects:
  • The domain and data layers share a bi-directional dependency with each other.
  • There's nothing to prevent the data layer from bleeding into the domain layer and vice-versa. If it can't be structurally prevented then it will occur.
  • It's difficult to unit test the domain layer without using a live database to support the data layer. Among other drawbacks, this brings unit testing performance to a crawl; therefore, developers stop unit testing.
简单的解决方案是把包含了DAO的数据访问层和领域层放入相同的物理程序集中并把它看着一个领域层. 为了维护这一个“虚”的关注点分离, 因而项目中可能包含了两个文件夹,一个叫做Domain另一个叫做Data. (我曾经用这个解决办法应用在一个大项目中,后来,为此后悔不而.) 这个解决办法带来了不少的坏影响:
  • 领域层和数据层双向依赖
  • 这样无法避免数据层渗入领域层反之亦然.如果这不能够从结构上防止,那么这样就会发生
  • 在没有真实数据库支持数据层的情况下测试领域层是很困难的。在另外的缺点,这样会使用单元测试的执行缓慢.因而开发人员停止了单元测试。
Alternatively, the domain and data layers could be placed into physically separate assemblies; e.g., Project.Core and Project.Data, respectively. The domain layer (the Project.Core assembly) would contain domain objects and DAO interfaces. The data layer (the Project.Data assembly) would contain concrete implementations of the DAO interfaces defined in the domain layer. This is shown in the package diagram below. Note that the arrow signifies a uni-directional dependency from the data layer to the domain layer.
因此, 领域层和数据层应该放入两个独立的物理程序集中. , Project.Core Project.Data, 在领域层中 (Project.Core程序集) 应该包含领域对象和数据访问层的接口. 数据层 ( Project.Data程序集合)应该包含在领域层中数据访问层接口的具体实现. 下图显示了关于两个程序集合的图.注意箭头是单向的从数据层指向领域层的。.
This clean separation brings with it a number of benefits:
  • Developers are structurally unable to put concrete data-access code into the domain layer without adding a number of easy-to-spot references, such as to NHibernate or System.Data.SqlClient.
  • The domain layer remains in ignorant bliss of how the data layer communicates or with who it communicates. Therefore, it's easier to switch out the data-access implementation details (e.g., from ADO.NET to NHibernate) without having to modify the domain layer.
  • Having dependencies on interfaces makes it easy to provide a "mocked" data-access layer to the domain layer while unit testing. This keeps unit tests blazing fast and eliminates the need to maintain test data in the database.

完整的分离带来了多种的好处:
  • 开发人员在结构上不会在领域层直接使用具体的数据访问代码,当没有增加大量容易混乱的引用情况下。NHibernate或者System.Data.SqlClient.
  • 领域层并不需要知道怎样和数据访问层通信或和数据访问层通信.因此, 在无需更改领域层的情况下,比较容易切换数据访问层的实现 (例如从ADO.NET 切换到NHibernate) .
  • 因为依赖于接口,所以非常容易提供“mocked”的数据访问层进行对领域模式的单元测试。这样可以保证单元测试快速运行并且不需要维护数据库中的测试数据
An implementation example of Separated Interface is included in the sample applications and is discussed further below.
下面,将进一步讨论如何实现一个已分离的接口。
依赖注入
Separated Interface, as described above, introduces a dilemma: how are the concrete DAO implementations given to the domain layer which only "knows" about interfaces? Dependency Injection, also known as Inversion of Control (IoC), describes the technique for doing this. With Dependency Injection, it's possible to remove many direct instantiations of concrete objects along with the inflexibility that comes along with calling the new keyword directly. Dependency Injection may be performed manually or via an IoC Container. The manual approach is performed by simply passing an object's "service dependencies" to it via its constructor or via property setters. I've written an introduction to this approach within the CodeProject article, Dependency Injection for Loose Coupling. (Note that a "service dependency" may be a DAO, an emailing utility, or anything that leverages external, expensive, or shouldn't-be-used-in-unit-test resources.) This manual approach is most useful within unit tests because the explicitness of passing concrete dependencies is helpful in describing functionality. (Martin Fowler has some wise words on the value of being explicit.) Alternatively, dependency injection may be performed via an IoC Container driven by code or XML. (Fowler has also written a great introduction to IoC Containers, service locators and making an appropriate choice between the two.) Think of IoC Containers as providing decoupling on steroids. The benefits of an IoC Container include a flexible, loosely-coupled framework and an increased emphasis on coding-to-interface. The drawback is an added layer of complexity within the application. This is one of the many trade-offs between flexibility and complexity which needs to be considered when developing an application. Here are two great tools for putting IoC Containers into practice:
 
接口分离, 如上所述。现在讲述一个难题:怎样创建应用了领域层中已知接口的DAO?依赖注入,也叫控制翻转(IoC),可以应用这个技术解决刚才的问题。应用依赖注入,有可能移除很多具体对象的实例以及他那些随着新关键字直接调用的不灵活(注:也就是说,应用依赖注入,可以移除很多通过直接从具体类型创建的实例的代码。而这些代码在没有应用依赖注入的时候,需要不断地更改创建的代码)。依赖注入可以通过手工或IoC容器来执行。人工的方法是简单地把对象所“依赖的服务”放入他的所在构造器或者属性中。我曾经在CodeProject写个一篇名为《Dependency Injection for Loose Coupling.》介绍这个方法。(“依赖的服务”可能是个DAO,是个邮件系统、或者任何其他外在支持、花费巨大的任何系统,或者一定不能在单元测试中被使用的资源)人工方法对於单元测试非常有用,因为在描述功能方面,清晰地使具体依赖关系的引用通过(单元测试)是很有帮助的(Martin Fowler有些是关于外在的价值的建议,另外依赖注入可能通过代码或者Xml在IoC容器执行(Flower为IoC容器和服务地位作过的重要介绍,并且为两者的作出适当的选择)。想象下把IoC容器作为解偶的灵丹妙药. 一个IoC容器的优点包括一个包含灵活、松耦合框架和强调以接口开发。缺点是实施中增加了复杂性。在开发一个程序过程中,这是从多需要考虑灵活性和复杂性如何取舍的其中一个。这里有两个非常好的IoC容器工具以供使用:)
  • Castle Windsor: The Castle Windsor, IoC Container provides great IoC support using a combination of XML configuration and strongly typed declarations. Some of the advantages Castle Windsor brings to the table are less XML and more compile-time error catching, when compared against other options. The Castle Project also has a number of other powerful modules, making it an attractive option if you're looking for more than just IoC. Wide support has been shown for this IoC Container which has generated a lot of buzz around the .NET development community. The "Enterprise" NHibernate Sample includes an example of using Castle Windsor.

  • Spring .NET: This framework provides IoC declared via XML configuration files. Like the Castle Project, Spring.NET also provides a wide assortment of additional development utilities. This option should be particularly attractive for developers coming from the Java world and are already familiar with the Spring Framework.
I've found that using an IoC Container is most useful outside of unit tests for enabling ASPX pages to be given dependencies, to avoid ever having to call the new keyword directly. This may be taken further to wire up dependencies within a presenters-layer or service layer, as well.
我已经发现使用IoC容器在对外在单元测试是非常有用,使Aspx页面中单元测试获得依赖,以避免不得不调用新的关键字(访问Aspx页面的依赖项)。这进一步更好地使依赖项连接用界面层或服务层(不知道是否应该这样翻译)
契约式设计
Design-by-Contract (DBC) is quite simply the best way to never have to use the debugger again. Although infrequently discussed, this technique should be given the same amount of praise that test-driven development receives. (Not that I'm saying that DBC gets jealous, but it should.) It increases quality, reduces checks for null, reduces debugging time, and greatly improves the overall stability of an application. To be more technically descriptive, DBC is a technique for contractually obligating users of your code (which is usually you, yourself) to use methods and properties in a particular way and for those methods and properties to promise to successfully carry out the given request. If the contract is not followed, an exception is thrown. It may seem a bit drastic at first, but it goes a long way to improving code quality. With DBC, "pre-conditions" assert what contractual obligations must be adhered to when invoking a method or property. "Post-conditions" assert what contractual obligations have been ensured. I highly recommend reading an introduction to Design-by-Contract; you'll be hooked on using it very quickly. The sample projects included with this article use a modified DBC class originally written by Kevin McFarlane. The original allows conditional compilation for turning contracts on for debug mode and off for release mode while the variation included in this article's samples maintains contractual obligations regardless of compilation mode. In my opinion, a contract is always a contract and the behavior should never vary.
契约式设计(DBC)是永不使用调试器的最好方法.虽然很少讨论,但这项技术获得的称赞应该等同于测试驱动开发所获得的。(倒不是说的DBC妒忌(TDD),而是说应该)。它增加质量,减少空检查,减少调试实践和极大地增加程序稳定性。详细点说,DBC是一项在你的代码(通常是你自己)中有责任的契约调用者,通过特定的途径使用方法和属性,而这些方法和属性并承诺成功地完成请求的技术。如果没有遵守契约,那么异常会被抛出。开始这样会有点受不了,这样会有助于改善代码质量。使用DBC,“前置条件”会断言那些在调用方法和属性时必须附带着契约上的职责(contractual obligations)。“后置条件”会断言那些已经被确定的契约职责。我极力推荐阅读《introduction to Design-by-Contract》;你会快速地迷上它。文章中的简单示例就是使用经过修改原来Kevin McFarlane所写的DBC。在原来的程序中,允许在调试模式中打开契约和在发布模式中关闭,但现在这个版本中在任何模式中都是维持契约中的职责开启。在我观点看来,契约总是契约,动作应该永不改变。
In the code, you'll find that pre-conditions are declared with Check.Require while post-condition are declared with Check.Ensure. As DBC is not domain-specific, this class is appropriately pulled out into a reusable, utilities library in the "enterprise" sample.
在这个代码中,你会发现前置条件被定义在Check.Require中,而后置条件被定义在Check.Ensure.DBC不在领域中时,这个类可以适当地重用在“企业”示例中
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章