<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      EntityFramework之領域驅動設計實踐(十)

      規約(Specification)模式

      本來針對規約模式的討論,我并沒有想將其列入本系列文章,因為這是一種概念性的東西,從理論上講,與EntityFramework好像扯不上關系。但應廣大網友的要求,我決定還是在這里討論一下規約模式,并介紹一種專門針對.NET Framework的規約模式實現。

      很多時候,我們都會看到類似下面的設計:

      隱藏行號 復制代碼 Customer倉儲的一種設計
      1. public interface ICustomerRespository
      2.  {
      3.     Customer GetByName(string name);
      4.     Customer GetByUserName(string userName);
      5.     IList<Customer> GetAllRetired();
      6. }
      7. 
        

      接下來的一步就是實現這個接口,并在類中分別實現接口中的方法。很明顯,在這個接口中,Customer倉儲一共做了三個操作:通過姓名獲取客戶信息;通過用戶名獲取客戶信息以及獲得所有當前已退休客戶的信息。這樣的設計有一個好處就是一目了然,能夠很方便地看到Customer倉儲到底提供了哪些功能。文檔化的開發方式特別喜歡這樣的設計。

      還是那句話,應需而變。如果你的系統很簡單,并且今后擴展的可能性不大,那么這樣的設計是簡潔高效的。但如果你正在設計一個中大型系統,那么,下面的問題就會讓你感到困惑:

      1. 這樣的設計,便于擴展嗎?今后需要添加新的查詢邏輯,結果一大堆相關代碼都要修改,怎么辦?
      2. 隨著時間的推移,這個接口會變得越來越大,團隊中你一榔頭我一棒子地對這個接口進行修改,最后整個設計變得一團糟
      3. GetByName和GetByUserName都OK,因為語義一目了然。但是GetAllRetired呢?什么是退休?超過法定退休年齡的算退休,那么病退的是不是算在里面?這里返回的所有Customer中,僅僅包含了已退休的男性客戶,還是所有性別的客戶都在里面?

      規約模式就是DDD引入用來解決以上問題的一種特殊的模式。規約是一種布爾斷言,它表述了給定的對象是否滿足當前約定的語義。經典的規約模式實現中,規約類只有一個方法,就是IsSatisifedBy(object);如下:

      隱藏行號 復制代碼 規約
      1. public class Specification
      2.  {
      3.     public virtual bool IsSatisifedBy(object obj)
      4.     {
      5.         return true;
      6.     }
      7. }
      8. 
        

      還是先看例子吧。在引入規約以后,上面的代碼就可以修改為:

      隱藏行號 復制代碼 規約的引入
      1. public interface ICustomerRepository
      2.  {
      3.     Customer GetBySpecification(Specification spec);
      4.     IList<Customer> GetAllBySpecification(Specification spec);
      5. }
      6. 
        
      7. public class NameSpecification : Specification
      8.  {
      9.     protected string name;
      10.     public NameSpecification(string name) { this.name = name; }
      11.     public override bool IsSatisifedBy(object obj)
      12.     {
      13.         return (obj as Customer).FirstName.Equals(name);
      14.     }
      15. }
      16. 
        
      17. public class UserNameSpecification : NameSpecification
      18.  {
      19.     public UserNameSpecification(string name) : base(name) { }
      20.     public override bool IsSatisifedBy(object obj)
      21.     {
      22.         return (obj as Customer).UserName.Equals(this.name);
      23.     }
      24. }
      25. 
        
      26. public class RetiredSpecification : Specification
      27.  {
      28.     public override bool IsSatisifedBy(object obj)
      29.     {
      30.         return (obj as Customer).Age >= 60;
      31.     }
      32. }
      33. 
        
      34. public class Program1
      35.  {
      36.     static void Main(string[] args)
      37.     {
      38.         ICustomerRepository cr; // = new CustomerRepository();
      39.         Customer getByNameCustomer = cr.GetBySpecification(new NameSpecification("Sunny"));
      40.         Customer getByUserNameCustomer = cr.GetBySpecification(new UserNameSpecification("daxnet"));
      41.         IList<Customer> getRetiredCustomers = cr.GetAllBySpecification(new RetiredSpecification());
      42.     }
      43. }
      44. 
        

      通過使用規約,我們將Customer倉儲中所有“特定用途的操作”全部去掉了,取而代之的是兩個非常簡潔的方法:分別通過規約來獲得Customer實體和實體集合。規約模式解耦了倉儲操作與斷言條件,今后我們需要通過倉儲實現其它特定條件的查詢時,只需要定制我們的Specification,并將其注入倉儲即可,倉儲的實現無需任何修改。與此同時,規約的引入,使得我們很清晰地了解到,某一次查詢過濾,或者某一次數據校驗是以什么樣的規則實現的,這給斷言條件的設計與實現帶來了可測試性。

      為了實現復合斷言,通常在設計中引入復合規約對象。這樣做的好處是,可以充分利用規約的復合來實現復雜的規約組合以及規約樹的遍歷。不僅如此,在.NET 3.5引入Expression Tree以后,規約將有其特定的實現方式,這個我們在后面討論。以下是一個經典的實現方式,注意ICompositeSpecification接口,它包含兩個屬性:Left和Right,ICompositeSpecification是繼承于ISpecification接口的,而Left和Right本身也是ISpecification類型,于是,整個Specification的結構就可以看成是一種樹狀結構。


       

      還記得在《EntityFramework之領域驅動設計實踐(八)- 倉儲的實現:基本篇》里提到的倉儲接口設計嗎?當初還沒有牽涉到任何Specification的概念,所以,倉儲的FindBySpecification方法采用.NET的Func<TEntity, bool>委托作為Specification的聲明。現在我們引入了Specification的設計,于是,倉儲接口可以改為:

      隱藏行號 復制代碼 引入Specification的倉儲實現
      1. public interface IRepository<TEntity>
      2.     where TEntity : EntityObject, IAggregateRoot
      3. {
      4.     void Add(TEntity entity);
      5.     TEntity GetByKey(int id);
      6.     IEnumerable<TEntity> FindBySpecification(ISpecification spec);
      7.     void Remove(TEntity entity);
      8.     void Update(TEntity entity);
      9. }
      10. 
        

      針對規約模式實現的討論,我們才剛剛開始。現在,又出現了下面的問題:

      1. 直接在系統中使用上述規約的實現,效率如何?比如,倉儲對外暴露了一個FindBySpecification的接口。但是,這個接口的實現是怎么樣的呢?由于規約的IsSatisifedBy方法是基于領域實體的,于是,為了實現根據規約過濾數據,貌似我們只能夠首先從倉儲中獲得所有的對象(也就是數據庫里所有的記錄),再對這些對象應用給定的規約從而獲得所需要的子集,這樣做肯定是低效的。Evans在其提出Specification模式后,也同樣提出了這樣的問題
      2. 從.NET的實踐角度,這樣的設計,能否滿足各種持久化技術的架構設計要求?這個問題與上面第一個問題是如出一轍的。比如,LINQ to Entities采用LINQ查詢對象,而NHibernate又有其自己的Criteria API,Db4o也有自己的LINQ機制。總所周知,Specification是值對象,它是領域層的一部分,同樣也不會去關心持久化技術實現細節。換句話說,我們需要隱藏不同持久化技術架構的具體實現
      3. 規約實現的臃腫。根據經典的Specification實現,假設我們需要查找所有過期的、未付款的支票,我們需要創建這樣兩個規約:OverdueSpecification和UnpaidSpecification,然后用Specification的And方法連接兩者,再將完成組合的Specification傳入Repository。時間一長,項目里充斥著各種Specification,可能其中有相當一部分都只在一個地方使用。雖然將Specification定義為類可以增加模型擴展性,但同時也會使模型變得臃腫。這就有點像.NET里的委托方法,為了解決類似的問題,.NET引入了匿名方法

      基于.NET的Specification可以使用LINQ Expression(下面簡稱Expression)來解決上面所有的問題。為了引入Expression,我們需要對ISpecification的設計做點點修改。代碼如下:

      隱藏行號 復制代碼 基于LINQ Expression的規約實現
      1. public interface ISpecification
      2.  {
      3.     bool IsSatisfiedBy(object obj);
      4.     Expression<Func<object, bool>> Expression { get; }
      5.     
      6.     // Other member goes here...
      7.  }
      8. 
        
      9. public abstract class Specification : ISpecification
      10.  {
      11. 
        
      12.     #region ISpecification Members
      13. 
        
      14.     public bool IsSatisfiedBy(object obj)
      15.     {
      16.         return this.Expression.Compile()(obj);
      17.     }
      18. 
        
      19.     public abstract Expression<Func<object, bool>> Expression { get; }
      20. 
        
      21.     #endregion
      22.  }
      23. 
        

      僅僅引入一個Expression<Func<object, bool>>屬性,就解決了上面的問題。在實際應用中,我們實現Specification類的時候,由原來的“實現IsSatisfiedBy方法”轉變為“實現Expression<Func<object, bool>>屬性”。現在主流的.NET對象持久化機制(比如EntityFramework,NHibernate,Db4o等等)都支持LINQ接口,于是:

      1. 通過Expression可以將LINQ查詢直接轉交給持久化機制(如EntityFramework、NHibernate、Db4o等),由持久化機制在從外部數據源獲取數據時執行過濾查詢,從而返回的是經過Specification過濾的結果集,與原本傳統的Specification實現相比,提高了性能
      2. 與1同理,基于Expression的Specification是可以通用于大部分持久化機制的
      3. 鑒于.NET Framework對LINQ Expression的語言集成支持,我們可以在使用Specification的時候直接編寫Expression,而無需創建更多的類。比如:
        隱藏行號 復制代碼 Specification Evaluation
        1. public abstract class Specification : ISpecification
        2.  {
        3.     // ISpecification implementation omitted
        4.  
        5.     public static ISpecification Eval(Expression<Func<object, bool>> expression)
        6.     {
        7.         return new ExpressionSpec(expression);
        8.     }
        9. }
        10. 
          
        11. internal class ExpressionSpec : Specification
        12. {
        13.     private Expression<Func<object, bool>> exp;
        14.     public ExpressionSpec(Expression<Func<object, bool>> expression)
        15.     {
        16.         this.exp = expression;
        17.     }
        18.     public override Expression<Func<object, bool>> Expression
        19.     {
        20.         get { return this.exp; }
        21.     }
        22. }
        23. 
          
        24. class Client
        25. {
        26.     static void CallSpec()
        27.     {
        28.         ISpecification spec = Specification.Eval(o => (o as Customer).UserName.Equals("daxnet"));
        29.         // spec....
        30.     }
        31. }
        32. 
          

         

      下圖是基于LINQ Expression的Specification設計的完整類圖。與經典Specification模式的實現相比,除了LINQ Expression的引入外,本設計中采用了IEntity泛型約束,用于將Specification的操作約束在領域實體上,同時也提供了強類型支持。

      Specification實現

      【如果單擊上圖無法查看圖片,請點擊此處以便查看大圖】

      上圖的右上角有個ISpecificationParser的接口,它主要用于將Specification解析為某一持久化框架可以認識的對象,比如LINQ Expression或者NHibernate的Criteria。當然,在引入LINQ Expression的Specification中,這個接口是可以不去實現的;而對于NHibernate,我們可以借助NHibernate.Linq命名空間來實現這個接口,從而將Specification轉換為NHibernate Criteria。相關代碼如下:

      隱藏行號 復制代碼 NHibernate Specification Parser
      1. internal sealed class NHibernateSpecificationParser : ISpecificationParser<ICriteria>
      2. {
      3.     ISession session;
      4. 
        
      5.     public NHibernateSpecificationParser(ISession session)
      6.     {
      7.         this.session = session;
      8.     }
      9.     #region ISpecificationParser<Expression> Members
      10. 
        
      11.     public ICriteria Parse<TEntity>(ISpecification<TEntity> specification)
      12.         where TEntity : class, IEntity
      13.     {
      14.         var query = this.session.Linq<TEntity>().Where(specification.GetExpression());
      15. 
        
      16.         //Expression<Func<TEntity, bool>> exp = obj => specification.IsSatisfiedBy(obj);

      17.         //var query = this.session.Linq<TEntity>().Where(exp);

      18.         System.Linq.Expressions.Expression expression = query.Expression;
      19.         expression = Evaluator.PartialEval(expression);
      20.         expression = new BinaryBooleanReducer().Visit(expression);
      21.         expression = new AssociationVisitor((ISessionFactoryImplementor)this.session.SessionFactory)
      22.             .Visit(expression);
      23.         expression = new InheritanceVisitor().Visit(expression);
      24.         expression = CollectionAliasVisitor.AssignCollectionAccessAliases(expression);
      25.         expression = new PropertyToMethodVisitor().Visit(expression);
      26.         expression = new BinaryExpressionOrderer().Visit(expression);
      27. 
        
      28.         NHibernateQueryTranslator translator = new NHibernateQueryTranslator(this.session);
      29.         var results = translator.Translate(expression, ((INHibernateQueryable)query).QueryOptions);
      30.         ICriteria ca = results as ICriteria;
      31.         
      32.         return ca;
      33.     }
      34. 
        
      35.     #endregion
      36. }
      37. 
        

       

      其實,Specification相關的話題遠不止本文所討論的這些,更多內容需要我們在實踐中發掘、思考。本文也只是對規約模式及其在.NET中的實現作了簡要的討論,文中也會存在欠考慮的地方,歡迎各位網友各抒己見,提出寶貴意見。

       

      posted @ 2010-07-19 16:12  dax.net  閱讀(23543)  評論(65)    收藏  舉報
      主站蜘蛛池模板: 视频一区视频二区亚洲视频| 亚洲 日韩 国产 制服 在线| 久久精品激情亚洲一二区| 欧美黑人添添高潮a片www| 亚洲av日韩av永久无码电影| 亚洲人成人一区二区三区| 国产精品自在线拍国产手机版 | 强奷乱码中文字幕| 老熟妇仑乱换频一区二区| 亚洲久悠悠色悠在线播放| 亚洲精品国产免费av| 俄罗斯老熟妇性爽xxxx| 少妇愉情理伦片高潮日本| 无码人妻精品一区二区三区下载| 日本a在线播放| 亚洲av无码成人精品区一区| 国产午夜福利视频合集| 色国产视频| 性少妇tubevⅰdeos高清| 无套内射视频囯产| 亚洲欧美另类久久久精品播放的| 人妻少妇精品久久| 国产资源精品中文字幕| 亚洲精品国产字幕久久麻豆| 精品无码午夜福利理论片| 久久婷婷五月综合97色直播| 亚洲国产精品综合久久20| 强伦姧人妻免费无码电影| 久久人人爽人人爽人人av| 日韩亚av无码一区二区三区| 亚洲熟女乱综合一区二区三区| 亚洲天堂精品一区二区| 精品国产午夜福利理论片| 国产91精品丝袜美腿在线| 国产成人精品1024免费下载| 国产精品三级一区二区三区| 人妻少妇精品无码专区二区| a4yy私人毛片| 日本一区二区不卡精品| 中文字幕亚洲综合久久| 熟妇人妻任你躁在线视频|