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

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

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

      C#進階系列——DDD領域驅動設計初探(二):倉儲Repository(上)

      前言:上篇介紹了DDD設計Demo里面的聚合劃分以及實體和聚合根的設計,這章繼續來說說DDD里面最具爭議的話題之一的倉儲Repository,為什么Repository會有這么大的爭議,博主認為主要原因無非以下兩點:一是Repository的真實意圖沒有理解清楚,導致設計的紊亂,隨著項目的橫向和縱向擴展,到最后越來越難維護;二是趕時髦的為了“模式”而“模式”,倉儲并非適用于所有項目,這就像沒有任何一種架構能解決所有的設計難題一樣。本篇通過這個設計的Demo來談談博主對倉儲的理解,有不對的地方還望園友們斧正!

      DDD領域驅動設計初探系列文章:

      一、倉儲的定義

      倉儲,顧名思義,存儲數據的倉庫。那么有人就疑惑了,既然我們有了數據庫來存取數據,為什么還要弄一個倉儲的概念,其實博主覺得這是一個考慮層面不同的問題,數據庫主要用于存取數據,而倉儲作用之一是用于數據的持久化。從架構層面來說,倉儲用于連接領域層和基礎結構層,領域層通過倉儲訪問存儲機制,而不用過于關心存儲機制的具體細節。按照DDD設計原則,倉儲的作用對象的領域模型的聚合根,也就是說每一個聚合都有一個單獨的倉儲。可能這樣說大家未必能理解,相信看了倉儲的代碼設計,大家能有一個更加透徹的認識。

      二、使用倉儲的意義

      1、站在領域層更過關心領域邏輯的層面,上面說了,倉儲作為領域層和基礎結構層的連接組件,使得領域層不必過多的關注存儲細節。在設計時,將倉儲接口放在領域層,而將倉儲的具體實現放在基礎結構層,領域層通過接口訪問數據存儲,而不必過多的關注倉儲存儲數據的細節(也就是說領域層不必關心你用EntityFrameWork還是NHibernate來存儲數據),這樣使得領域層將更多的關注點放在領域邏輯上面。

      2、站在架構的層面,倉儲解耦了領域層和ORM之間的聯系,這一點也就是很多人設計倉儲模式的原因,比如我們要更換ORM框架,我們只需要改變倉儲的實現即可,對于領域層和倉儲的接口基本不需要做任何改變。

      三、代碼示例

      1、解決方案結構圖

       

       

      上面說了,倉儲的設計是接口和實現分離的,于是,我們的倉儲接口和工作單元接口全部放在領域層,在基礎結構層新建了一個倉儲的實現類庫ESTM.Repository,這個類庫需要添加領域層的引用,實現領域層的倉儲接口和工作單元接口。所以,通過上圖可以看到領域層的IRepositories里面的倉儲接口和基礎結構層ESTM.Repository項目下的Repositories里面的倉儲實現是一一對應的。下面我們來看看具體的代碼設計。其實園子里已有很多經典的倉儲設計,為了更好地說明倉儲的作用,博主還是來班門弄斧下了~~

      2、倉儲接口

         /// <summary>
          /// 倉儲接口,定義公共的泛型GRUD
          /// </summary>
          /// <typeparam name="TEntity">泛型聚合根,因為在DDD里面倉儲只能對聚合根做操作</typeparam>
          public interface IRepository<TEntity> where TEntity : AggregateRoot
          {
              #region 屬性
              IQueryable<TEntity> Entities { get; }
              #endregion
      
              #region 公共方法
              int Insert(TEntity entity);
      
              int Insert(IEnumerable<TEntity> entities);
      
              int Delete(object id);
      
              int Delete(TEntity entity);
      
              int Delete(IEnumerable<TEntity> entities);
      
              int Update(TEntity entity);
      
              TEntity GetByKey(object key);
              #endregion
          }
          /// <summary>
          /// 部門聚合根的倉儲接口
          /// </summary>
          public interface IDepartmentRepository:IRepository<TB_DEPARTMENT>
          {
      
          }
          /// <summary>
          /// 菜單這個聚合根的倉儲接口
          /// </summary>
          public interface IMenuRepository:IRepository<TB_MENU>
          {
              IEnumerable<TB_MENU> GetMenusByRole(TB_ROLE oRole);
          }
          /// <summary>
          /// 角色這個聚合根的倉儲接口
          /// </summary>
          public interface IRoleRepository:IRepository<TB_ROLE>
          {
          }
          /// <summary>
          /// 用戶這個聚合根的倉儲接口
          /// </summary>
          public interface IUserRepository:IRepository<TB_USERS>
          {
              IEnumerable<TB_USERS> GetUsersByRole(TB_ROLE oRole);
          }

      除了IRepository這個泛型接口,其他4個倉儲接口都是針對聚合建立的接口, 上章 C#進階系列——DDD領域驅動設計初探(一):聚合 介紹了聚合的劃分,這里的倉儲接口就是基于此建立。IUserRepository接口實現了IRepository接口,并把對應的聚合根傳入泛型,這里正好應征了上章聚合根的設計。

      3、倉儲實現類

        //倉儲的泛型實現類
          public class EFBaseRepository<TEntity> : IRepository<TEntity> where TEntity : AggregateRoot
          {
              [Import(typeof(IEFUnitOfWork))]
              private IEFUnitOfWork UnitOfWork { get; set; }
      
              public EFBaseRepository()
              {
             //注冊MEF Regisgter.regisgter().ComposeParts(
      this); } public IQueryable<TEntity> Entities { get { return UnitOfWork.context.Set<TEntity>(); } } public int Insert(TEntity entity) { UnitOfWork.RegisterNew(entity); return UnitOfWork.Commit(); } public int Insert(IEnumerable<TEntity> entities) { foreach (var obj in entities) { UnitOfWork.RegisterNew(obj); } return UnitOfWork.Commit(); } public int Delete(object id) { var obj = UnitOfWork.context.Set<TEntity>().Find(id); if (obj == null) { return 0; } UnitOfWork.RegisterDeleted(obj); return UnitOfWork.Commit(); } public int Delete(TEntity entity) { UnitOfWork.RegisterDeleted(entity); return UnitOfWork.Commit(); } public int Delete(IEnumerable<TEntity> entities) { foreach (var entity in entities) { UnitOfWork.RegisterDeleted(entity); } return UnitOfWork.Commit(); } public int Update(TEntity entity) { UnitOfWork.RegisterModified(entity); return UnitOfWork.Commit(); } public TEntity GetByKey(object key) { return UnitOfWork.context.Set<TEntity>().Find(key); } }

      倉儲的泛型實現類里面通過MEF導入工作單元,工作單元里面擁有連接數據庫的上下文對象。

        [Export(typeof(IDepartmentRepository))]
          public class DepartmentRepository : EFBaseRepository<TB_DEPARTMENT>,IDepartmentRepository
          {
          }
          [Export(typeof(IMenuRepository))]
          public class MenuRepository:EFBaseRepository<TB_MENU>,IMenuRepository
          {
              public IEnumerable<TB_MENU> GetMenusByRole(TB_ROLE oRole)
              {
                  throw new Exception();
              }
          }
          [Export(typeof(IRoleRepository))]
          public class RoleRepository:EFBaseRepository<TB_ROLE>,IRoleRepository
          {
      
          }
          [Export(typeof(IUserRepository))]
          public class UserRepository:EFBaseRepository<TB_USERS>,IUserRepository
          {
              public IEnumerable<TB_USERS> GetUsersByRole(TB_ROLE oRole)
              {
                  throw new NotImplementedException();
              }
          }

      倉儲是4個具體實現類里面也可以通過基類里面導入的工作單元對象UnitOfWork去操作數據庫。

      4、工作單元接口

         //工作單元基類接口
          public interface IUnitOfWork
          {
               bool IsCommitted { get; set; } 
      
              int Commit();
      
              void Rollback();
          }
          //倉儲上下文工作單元接口,使用這個的一般情況是多個倉儲之間存在事務性的操作,用于標記聚合根的增刪改狀態
          public interface IUnitOfWorkRepositoryContext:IUnitOfWork,IDisposable
          {
              /// <summary>
              /// 將聚合根的狀態標記為新建,但EF上下文此時并未提交
              /// </summary>
              /// <typeparam name="TEntity"></typeparam>
              /// <param name="obj"></param>
              void RegisterNew<TEntity>(TEntity obj)
                  where TEntity : AggregateRoot;
      
              /// <summary>
              /// 將聚合根的狀態標記為修改,但EF上下文此時并未提交
              /// </summary>
              /// <typeparam name="TEntity"></typeparam>
              /// <param name="obj"></param>
              void RegisterModified<TEntity>(TEntity obj)
                  where TEntity : AggregateRoot;
      
              /// <summary>
              /// 將聚合根的狀態標記為刪除,但EF上下文此時并未提交
              /// </summary>
              /// <typeparam name="TEntity"></typeparam>
              /// <param name="obj"></param>
              void RegisterDeleted<TEntity>(TEntity obj)
                  where TEntity : AggregateRoot;
          }

      看到這兩個接口可能有人就有疑惑了,為什么要設計兩個接口,直接合并一個不行么?這個工作單元的設計思路來源dax.net的系列文章,再次表示感謝!的確,剛開始,博主也有這種疑惑,仔細思考才知道,應該是出于事件機制來設計的,實現IUnitOfWorkRepositoryContext這個接口的都是針對倉儲設計的工作單元,而實現IUnitOfWork這個接口除了倉儲的設計,可能還有其他情況,比如事件機制。

      5、工作單元實現類

          //表示EF的工作單元接口,因為DbContext是EF的對象
          public interface IEFUnitOfWork : IUnitOfWorkRepositoryContext
          {
              DbContext context { get; }
          }

      為什么要在這里還設計一層接口?因為博主覺得,工作單元要引入EF的Context對象,同理,如果你用的NH,那么這里應該是引入Session對象

      /// <summary>
          /// 工作單實現類
          /// </summary>
          [Export(typeof(IEFUnitOfWork))]
          public class EFUnitOfWork : IEFUnitOfWork
          {
              #region 屬性
              //通過工作單元向外暴露的EF上下文對象
              public DbContext context { get { return EFContext; } }
      
              [Import(typeof(DbContext))]
              public DbContext EFContext { get; set; } 
              #endregion
      
              #region 構造函數
              public EFUnitOfWork()
              { 
                  //注冊MEF
                  Regisgter.regisgter().ComposeParts(this);
              }
              #endregion
      
              #region IUnitOfWorkRepositoryContext接口
              public void RegisterNew<TEntity>(TEntity obj) where TEntity : AggregateRoot
              {
                  var state = context.Entry(obj).State;
                  if (state == EntityState.Detached)
                  {
                      context.Entry(obj).State = EntityState.Added;
                  }
                  IsCommitted = false;
              }
      
              public void RegisterModified<TEntity>(TEntity obj) where TEntity : AggregateRoot
              {
                  if (context.Entry(obj).State == EntityState.Detached)
                  {
                      context.Set<TEntity>().Attach(obj);
                  }
                  context.Entry(obj).State = EntityState.Modified;
                  IsCommitted = false;
              }
      
              public void RegisterDeleted<TEntity>(TEntity obj) where TEntity : AggregateRoot
              {
                  context.Entry(obj).State = EntityState.Deleted;
                  IsCommitted = false;
              } 
              #endregion
      
              #region IUnitOfWork接口
      
              public bool IsCommitted { get; set; }
      
              public int Commit()
              {
                  if (IsCommitted)
                  {
                      return 0;
                  }
                  try
                  {
                      int result = context.SaveChanges();
                      IsCommitted = true;
                      return result;
                  }
                  catch (DbUpdateException e)
                  {
      
                      throw e;
                  }
              }
      
              public void Rollback()
              {
                  IsCommitted = false;
              } 
              #endregion
      
              #region IDisposable接口
              public void Dispose()
              {
                  if (!IsCommitted)
                  {
                      Commit();
                  }
                  context.Dispose();
              } 
              #endregion
          }

      工作單元EFUnitOfWork上面注冊了MEF的Export,是為了供倉儲的實現基類里面Import,同理,這里有一點需要注意的,這里要想導入DbContext,那么EF的上下文對象就要Export

          [Export(typeof(DbContext))]
          public partial class ESTMContainer:DbContext
          {
          }

      這里用了萬能的部分類partial,還記得上章說到的領域Model么,也是在edmx的基礎上通過部分類在定義的。同樣,在edmx的下面肯定有一個EF自動生成的上下文對象,如下:

        public partial class ESTMContainer : DbContext
          {
              public ESTMContainer()
                  : base("name=ESTMContainer")
              {
              }
          
              protected override void OnModelCreating(DbModelBuilder modelBuilder)
              {
                  throw new UnintentionalCodeFirstException();
              }
          
              public DbSet<TB_DEPARTMENT> TB_DEPARTMENT { get; set; }
              public DbSet<TB_MENU> TB_MENU { get; set; }
              public DbSet<TB_MENUROLE> TB_MENUROLE { get; set; }
              public DbSet<TB_ROLE> TB_ROLE { get; set; }
              public DbSet<TB_USERROLE> TB_USERROLE { get; set; }
              public DbSet<TB_USERS> TB_USERS { get; set; }
          }

      上文中多個地方用到了注冊MEF的方法

      Regisgter.regisgter().ComposeParts(this);

      是因為我們在基礎結構層里面定義了注冊方法

      namespace ESTM.Infrastructure.MEF
      {
          public class Regisgter
          {
              private static object  obj =new object();
              private static CompositionContainer _container;
              public static CompositionContainer regisgter()
              {
                  lock (obj)
                  {
                      try
                      {
                          if (_container != null)
                          {
                              return _container;
                          }
                          AggregateCatalog aggregateCatalog = new AggregateCatalog();
                          string path = AppDomain.CurrentDomain.BaseDirectory;
                          var thisAssembly = new DirectoryCatalog(path, "*.dll");
                          if (thisAssembly.Count() == 0)
                          {
                              path = path + "bin\\";
                              thisAssembly = new DirectoryCatalog(path, "*.dll");
                          }
                          aggregateCatalog.Catalogs.Add(thisAssembly);
                          _container = new CompositionContainer(aggregateCatalog);
                          return _container;
                      }
                      catch (Exception ex)
                      {
                          return null;
                      }
                  }
              }
          }
      }

      6、Demo測試

      為了測試我們搭的框架能運行通過,我們在應用層里面寫一個測試方法。正常情況下,應用層ESTM.WCF.Service項目只需要添加ESTM.Domain項目的引用,那么在應用層里面如何找到倉儲的實現呢?還是我們萬能的MEF,通過IOC依賴注入的方式,應用層不必添加倉儲實現層的引用,通過MEF將倉儲實現注入到應用層里面,但前提是應用層的bin目錄下面要有倉儲實現層生成的dll,需要設置ESTM.Repository項目的生成目錄為ESTM.WCF.Service項目的bin目錄。這個問題在C#進階系列——MEF實現設計上的“松耦合”(終結篇:面向接口編程)這篇里面介紹過

      還是來看看測試代碼

      namespace ESTM.WCF.Service
      {
          class Program
          {
      
              [Import]
              public IUserRepository userRepository { get; set; }
      
              static void Main(string[] args)
              {
                  var oProgram = new Program();
                  Regisgter.regisgter().ComposeParts(oProgram);
      
      
                  var lstUsers = oProgram.userRepository.Entities.ToList();
              }
          }
      }

       運行得到結果:

       

      7、總結

      至此,我們框架倉儲的大致設計就完了,我們回過頭來看看這樣設計的優勢所在:

      (1)倉儲接口層和實現層分離,使得領域模型更加純凈,領域模型只關注倉儲的接口,而不用關注數據存儲的具體細節,使得領域模型將更多的精力放在領域業務上面。

      (2)應用層只需要引用領域層,只需要調用領域層里面的倉儲接口就能得到想要的數據,而不用添加倉儲具體實現的引用,這也正好符合項目解耦的設計。

      (3)更換ORM方便。項目現在用的是EF,若日后需要更換成NH,只需要再實現一套倉儲和上下文即可。這里需要說明一點,由于整個框架使用EF的model First,為了直接使用EF的model,我們把edmx定義在了領域層里面,其實這樣是不合理的,但是我們為了使用簡單,直接用了partial定義領域模型的行為,如果要更好的使用DDD的設計,EF現在的Code First是最好的方式,領域層里面只定義領域模型和關注領域邏輯,EF的CRUD放在基礎結構層,切換ORM就真的只需要重新實現一套倉儲即可,這樣的設計才是博主真正想要的效果,奈何時間和經歷有限,敬請諒解。以后如果有時間博主會分享一個完整設計的DDD。

       

      posted @ 2015-09-24 13:40  懶得安分  閱讀(45204)  評論(45)    收藏  舉報
      主站蜘蛛池模板: 国产制服丝袜无码视频| 最新中文字幕av无码专区不| 国产精品熟女一区二区三区| 亚洲永久精品日本久精品| 国产精品高潮无码毛片| 高雄县| 亚洲精品国产综合久久一线| 日韩精品一区二区av在线| 日产精品一区二区三区免费| 宜丰县| 久久理论片午夜琪琪电影网| 国产精品自在线拍国产手机版| 贡觉县| 91久久国产成人免费观看| 亚洲欧美综合人成在线| 国产精品久久久久久av| 色婷婷五月综合久久| 国产91精品一区二区亚洲| 国产精品成人99一区无码| 亚洲欧美日韩综合久久久| 欧美裸体xxxx极品| 国产AV影片麻豆精品传媒| 中文字幕无码乱码人妻系列蜜桃| 国产精品一区二区国产馆| 内射一区二区三区四区| 国产一区二区三区怡红院| 国产高清在线精品一本大道| 亚洲成aⅴ人在线电影| 人妻18毛片A级毛片免费看| 国产在线观看播放av| 国产精品美女www爽爽爽视频 | 丰原市| 成人av一区二区亚洲精| 日韩av日韩av在线| 亚洲国产精品久久久久久久| 白白发布视频一区二区视频| 国产精品亚洲二区在线看| 牛牛视频一区二区三区| a男人的天堂久久a毛片| 色天天天综合网色天天| 贡觉县|