本系列文章將介紹如何在.Net框架下,從零開始搭建一個完成CRUD的Framework,該Framework將具有如下功能,基本實體結構(基於DDD)、基本倉儲結構、模塊加載系統、工做單元、事件總線(EventBus,具備事件溯源的功能)、以及依賴注入管理系統.數據庫
一、簡介app
本文將經過源碼和代碼註釋和文字說明來解釋基本實體結構的構建和基本倉儲的構建框架
二、實戰 異步
(1)、基本實體的構建ide
在OOP的概念之下,對象大體能夠分爲兩類,持久化對象和非持久化對象.本文主要討論的是持久化對象,即須要寫入到數據庫或者其餘數據容器中的對象,也就是實體(固然這裏不是所謂的實體,而是經過OOP技術構建出來的一個實體結構,這個結構須要知足平常開發中絕大多數的業務需求).接下去,就是使用OOP技術來構建這個實體結構.優化
首先這個實體既然須要寫入數據庫,那麼它一定有一個主鍵Id.同時這個主鍵Id能夠是任意數據類型,固然用的最多的就是GUID和INT做爲主鍵.前面全局惟一,後者查詢效率快.ui
因此,就有了以下結構:spa
public interface IEntity<TPrimaryKey> { /// <summary> /// 實體的主鍵Id /// </summary> TPrimaryKey Id { get; set; } }
其次,以不一樣數據類型(GUID、int、string)爲主鍵的實體類型,存在一些共有方法,好比須要編寫更加語義化的ToString方法,因此當不一樣類型須要共同的實現的時候,這個時候就須要一層抽象,來處理這層關係,因此就有了以下結構:code
[Serializable]
public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey> { public TPrimaryKey Id { get; set; } /// <summary> /// 返回當前實體的類型名稱+Id的形式 /// </summary> /// <returns></returns> public override string ToString() { return $"{GetType().Name} {Id}"; } }
固然這個結構中能夠有任何的共有方法,只要它們能成功的抽象出來,就能寫入到裏面.對象
接着,基本實體就出現了,這裏我分爲兩類,一類以int爲主鍵,一類已Guid主鍵,爲別寫道兩個類中,以下代碼:
/// <summary> /// 以int爲主鍵的實體類型 /// </summary> [Serializable] public abstract class Entity: Entity<int> { } /// <summary> /// 以Guid爲爲主鍵的實體類型 /// </summary> [Serializable] public abstract class GEntity : Entity<Guid> { }
打上Serializable特性,方便序列化.這裏不一樣的子類使用abstract來實現,也是爲了提供各自實體的共有抽象屬性(或者方法).到這一步,最最基本的實體抽象構建完畢,可是尚未結束,由於這個結構能夠繼續優化.使它能夠爲咱們的業務更好的服務.因此須要持久化的實體一定存在一個建立的過程,可能該實體在某些業務下不須要修改、刪除或者查詢功能,可是它有極大的機率存在一個建立的過程,因此這裏須要構建一個實體建立的抽象類,代碼以下:
public interface ICreationAudited { /// <summary> /// 建立該實體的用戶Id /// </summary> int? CreatorUserId { get; set; } /// <summary> /// 建立當前實體時的時間 /// </summary> DateTime CreationTime { get; set; } } [Serializable] public abstract class CreationAuditedEntity<TPrimaryKey> : Entity<TPrimaryKey>, ICreationAudited { public int? CreatorUserId { get; set; } public DateTime CreationTime { get; set; } /// <summary> /// 構造 當前實體注入內存時,給定建立時間,存在偏差,由於從業務點擊頁面建立到實際生成該實體階段存在時間差,可是這個時間差能夠忽略不計 /// </summary> protected CreationAuditedEntity() { CreationTime = DateTime.Now; } } /// <summary> /// 實體建立 主鍵爲int /// </summary> [Serializable]
public abstract class CreationAuditedEntity : CreationAuditedEntity<int> { }
這裏考慮文章大小,Guid的實體建立類型就不實現了,接下去只實現int.
最後實體建立的結構,構建完畢以後,在編寫一個須要增刪查改全部功能都具有的實體結構,整個實體結構大體就構建完畢了,代碼以下:
public interface IDeletionAudited { /// <summary> /// 刪除實體的用戶Id /// </summary> int? DeleterUserId { get; set; } /// <summary> /// 刪除實體的時間 /// </summary> DateTime? DeletionTime { get; set; } } public interface IModificationAudited { /// <summary> /// 最後一次修改實體的用戶Id /// </summary> int? LastModifierUserId { get; set; } /// <summary> /// 最後一次修改實體的時間 /// </summary> DateTime? LastModificationTime { get; set; } } public interface IFullAudited : IDeletionAudited, IModificationAudited, ICreationAudited { } [Serializable] public abstract class FullAuditedEntity<TPrimaryKey>:CreationAuditedEntity<TPrimaryKey>,IFullAudited { public int? DeleterUserId { get; set; } public DateTime? DeletionTime { get; set; } public int? LastModifierUserId { get; set; } public DateTime? LastModificationTime { get; set; } } /// <summary> /// 具備增刪查該功能的實體結構 主鍵爲int /// </summary> public abstract class FullAuditedEntity : FullAuditedEntity<int> { }
ok,到這裏基本的實體結構構建完畢,固然這裏你能夠隨意的擴展,好比構建各類各樣的默認的實體類,如主鍵爲string的只具備修改和刪除的實體類.能夠根據業務的實體特性來動態的擴展.也能夠向全部的抽象實體類中添加任意的抽象屬性或者方法.好比給Entity添加獲取HashCode的共有方法.
(2)、基本倉儲結構的構建
關於倉儲就很少介紹了,能夠自行上網查閱相關的文章,基本倉儲結構是依賴於實體結構的。本文將基本Dapper構建一套基本倉儲結構.
首先必須有一個倉儲接口標識,這個標識自己不具備方法,單單只是一個標識,方便後期實現工做單元和模塊加載系統時,辨別出程序集中的倉儲類型.以下:
/// <summary> /// 倉儲接口 /// </summary> public interface IRepository { }
接着,基於這個接口來構建Dapper通用倉儲具備的基本的功能,即增刪查改、分頁、列表等功能.代碼以下:
/// <summary> /// Dapper通用倉儲接口 /// </summary> /// <typeparam name="TEntity"></typeparam> /// <typeparam name="TPrimaryKey"></typeparam> public interface IDapperRepository<TEntity, TPrimaryKey> : IRepository where TEntity : class, IEntity<TPrimaryKey> { /// <summary> /// 添加一條實體信息 /// </summary> /// <param name="entity"></param> /// <returns></returns> Task InsertAsync(TEntity entity); /// <summary> /// 刪除一條實體信息 /// </summary> /// <param name="entity"></param> /// <returns></returns> Task DeleteAsync(TEntity entity); /// <summary> /// 修改一條實體信息 /// </summary> /// <param name="entity"></param> /// <returns></returns> Task UpdateAsync(TEntity entity); /// <summary> /// 根據主鍵Id異步獲取一條數據信息 /// </summary> /// <param name="id"></param> /// <returns></returns> Task<TEntity> GetAsync(TPrimaryKey id); }