在上一篇,咱們搭建了一個項目框架,基本上是一個完整的項目。目前而言,大部分的應用基本都是這個結構。好的,不廢話了,進入今天的議題:完成並實現數據層的基礎實現。數據庫
一般狀況下,一個項目的數據實體中字段並非徹底沒有規律可尋。一般狀況下,必須有一個主鍵。有些時候,會要求在數據表中增長上次修改時間和建立時間,以及建立人和修改人的主鍵。c#
因此,咱們能夠建立一個泛型父類,來幫咱們定義這些公共字段:架構
using System; namespace Data.Infrastructure { public class BaseEntity<T> { public T Id { get; set; } public string ModifyUserId { get; set; } public DateTime? ModifyTime { get; set; } public string CreatorId { get; set; } public DateTime? CreateTime { get; set; } } }
看上述代碼裏,命名空間並不在Data裏,而是在Data.Infrastructure裏。這個命名空間 Infrastructure 用來存放一些項目的架構類或者接口,裏面還會其餘的類。框架
那麼,給這個類補充一些可能有用的方法:大數據
public void Create(object userId) { CreatorId = userId.ToString(); CreateTime = DateTime.Now; } public void Create(object userId, DateTime createTime) { CreatorId = userId.ToString(); CreateTime = createTime; } public void Modify(object userId) { ModifyUserId = userId.ToString(); ModifyTime = DateTime.Now; } public void Modify(object userId, DateTime modifyTime) { ModifyUserId = userId.ToString(); ModifyTime = modifyTime; }
這裏用來保存用戶ID的字段,我都用了字符串作保存,是借用字符串類型保存數據時能容納更多的數據類型。spa
在正常開發中,一個完整的數據操做接口會有不少分類,可是不少時候咱們須要分開增刪改和查詢這兩種操做。對於數據庫而言,視圖和有些數據表都是不被容許改變的,這時候就須要咱們只對調用方開放查詢接口,而不開放修改接口。code
因此,在Domain下應該有如下兩個接口:對象
using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Domain.Infrastructure { /// <summary> /// 修改接口 /// </summary> /// <typeparam name="T"></typeparam> public interface IModifyRepository<T> { /// <summary> /// 插入數據 /// </summary> /// <param name="entity"></param> /// <returns></returns> T Insert(T entity); /// <summary> /// 插入數據 /// </summary> /// <param name="entities"></param> void Insert(params T[] entities); /// <summary> /// 插入數據 /// </summary> /// <param name="entities"></param> void Insert(IEnumerable<T> entities); /// <summary> /// 保存已提交的修改 /// </summary> /// <param name="entity"></param> void Update(T entity); /// <summary> /// 保存已提交的修改 /// </summary> /// <param name="entities"></param> void Update(params T[] entities); /// <summary> /// 更新數據 /// </summary> /// <param name="predicate"></param> /// <param name="updator"></param> void Update(Expression<Func<T, bool>> predicate, Expression<Func<T, T>> updator); /// <summary> /// 刪除 /// </summary> /// <param name="entity"></param> void Delete(T entity); /// <summary> /// 刪除數據 /// </summary> /// <param name="entities"></param> void Delete(params T[] entities); /// <summary> /// 根據條件刪除數據 /// </summary> /// <param name="predicate"></param> void Delete(Expression<Func<T,bool>> predicate); /// <summary> /// 刪除主鍵對應的數據 /// </summary> /// <param name="key"></param> void DeleteByKey(object key); /// <summary> /// 刪除主鍵對應的數據 /// </summary> /// <param name="keys"></param> void DeleteByKeys(params object[] keys); } }
上述是更新接口,那麼咱們回過頭來寫查詢接口,查詢接口的方法有不少。咱們先建立一個接口文件:排序
using System; using System.Linq.Expressions; namespace Domain.Infrastructure { /// <summary> /// 查詢接口 /// </summary> /// <typeparam name="T"></typeparam> public interface ISearchRepository<T> { } }
一個查詢接口應該包括如下方法:接口
/// <summary> /// 根據主鍵獲取數據 /// </summary> /// <param name="key"></param> /// <returns></returns> T Get(object key); /// <summary> /// 查詢 /// </summary> /// <param name="predicate"></param> /// <returns></returns> T Get(Expression<Func<T,bool>> predicate);
/// <summary> /// 返回數據庫中的數據條目 /// </summary> /// <returns></returns> int Count(); /// <summary> /// 返回數據庫中的數據條目,類型爲Long /// </summary> /// <returns></returns> long LongCount(); /// <summary> /// 返回符合條件的數據數目 /// </summary> /// <param name="predicate"></param> /// <returns></returns> int Count(Expression<Func<T,bool>> predicate); /// <summary> /// 返回長整形的符合條件的數目 /// </summary> /// <param name="predicate"></param> /// <returns></returns> long LongCount(Expression<Func<T,bool>> predicate);
/// <summary> /// 是否存在知足條件的數據 /// </summary> /// <param name="predicate"></param> /// <returns></returns> bool IsExists(Expression<Func<T, bool>> predicate);
// <summary> /// 返回數據庫中全部記錄 /// </summary> /// <returns></returns> List<T> Search(); /// <summary> /// 返回全部符合條件的數據 /// </summary> /// <param name="predicate"></param> /// <returns></returns> List<T> Search(Expression<Func<T,bool>> predicate); /// <summary> /// 返回一個延遲查詢的對象 /// </summary> /// <returns></returns> IEnumerable<T> Query(); /// <summary> /// 返回一個延遲查詢的對象,並預設了一個查詢條件 /// </summary> /// <param name="predicate"></param> /// <returns></returns> IEnumerable<T> Query(Expression<Func<T,bool>> predicate);
/// <summary> /// 排序查詢,默認升序 /// </summary> /// <param name="predicate"></param> /// <param name="order"></param> /// <typeparam name="P"></typeparam> /// <returns></returns> List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order); /// <summary> /// 排序查找,指定是否降序排列 /// </summary> /// <param name="predicate"></param> /// <param name="order"></param> /// <param name="isDesc"></param> /// <typeparam name="P"></typeparam> /// <returns></returns> List<T> Search<P>(Expression<Func<T, bool>> predicate, Expression<Func<T, P>> order, bool isDesc);
實際上分頁的接口定義模型須要兩個類的輔助,若是沒有這兩個類,接口的定義會變得十分複雜,不利於代碼的可讀性:
using System; using System.Collections.Generic; using System.Linq.Expressions; namespace Data.Infrastructure { /// <summary> /// 分頁條件模型 /// </summary> /// <typeparam name="T"></typeparam> public class PageCondition<T> { /// <summary> /// 查詢條件 /// </summary> /// <value></value> public Expression<Func<T, bool>> Predicate { get; set; } /// <summary> /// 排序字段 /// </summary> /// <value></value> public string OrderProperty { get; set; } /// <summary> /// 升序排序或者降序排序,升序爲 asc或者空,降序爲desc /// </summary> /// <value></value> public string Sort{get;set;} /// <summary> /// 每頁最大數據容量 /// </summary> /// <value></value> public int PerpageSize { get; set; } /// <summary> /// 當前頁 /// </summary> /// <value></value> public int CurrentPage { get; set; } } /// <summary> /// 分頁結果 /// </summary> /// <typeparam name="T"></typeparam> public class PageModel<T> { /// <summary> /// 數據 /// </summary> /// <value></value> public List<T> Items { get; set; } /// <summary> /// 當前頁碼 /// </summary> /// <value></value> public int CurrentPage { get; set; } /// <summary> /// 每頁最大數據容量 /// </summary> /// <value></value> public int PerpageSize { get; set; } /// <summary> /// 查詢數據總數 /// </summary> /// <value></value> public long TotalCount { get; set; } /// <summary> /// 總頁碼 /// </summary> /// <value></value> public int TotalPages { get; set; } } }
這是兩個輔助類,能夠簡單看一下若是這些參數不進行封裝直接傳給方法,能夠預見方法的參數列表會特別長,這對於可讀性和可維護性來講簡直就是災難。我曾經接手過一個項目的維護,上一位開發者在一個方法寫了近15個參數,並且還有大量的可選參數,嗯,十分頭疼。因此,我不建議你們這樣寫,一個方法參數超過4個我建議仍是封裝一下。
那麼,看一看方法的聲明:
/// <summary> /// 根據分頁參數設置,進行分頁查詢 /// </summary> /// <param name="condition"></param> /// <returns></returns> PageModel<T> Search(PageCondition<T> condition);
這是使用參數封裝了請求的寫法,小夥伴們能夠試試不用封裝,方法是如何聲明的。
在這一篇帶領你們梳理了一下數據訪問的接口定義,對一個系統來講,這些方法都是有必要的(但不是每一個方法使用頻率都同樣高)。也是簡單的跟你們分享一下我在實際工做中寫代碼的總結。
更多內容煩請關注個人博客《高先生小屋》