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不在領域中時,這個類可以適當地重用在“企業”示例中
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章