【asp.net core】7 實(shí)戰(zhàn)之 數(shù)據(jù)訪問層定義

0. 前言

在上一篇,我們搭建了一個(gè)項(xiàng)目框架,基本上是一個(gè)完整的項(xiàng)目。目前而言,大部分的應(yīng)用基本都是這個(gè)結(jié)構(gòu)。好的,不廢話了,進(jìn)入今天的議題:完成并實(shí)現(xiàn)數(shù)據(jù)層的基礎(chǔ)實(shí)現(xiàn)。

1. 數(shù)據(jù)實(shí)體

通常情況下,一個(gè)項(xiàng)目的數(shù)據(jù)實(shí)體中字段并不是完全沒有規(guī)律可尋。通常情況下,必須有一個(gè)主鍵。有些時(shí)候,會(huì)要求在數(shù)據(jù)表中增加上次修改時(shí)間和創(chuàng)建時(shí)間,以及創(chuàng)建人和修改人的主鍵。

所以,我們可以創(chuàng)建一個(gè)泛型父類,來幫我們定義這些公共字段:

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里。這個(gè)命名空間 Infrastructure 用來存放一些項(xiàng)目的架構(gòu)類或者接口,里面還會(huì)其他的類。

那么,給這個(gè)類補(bǔ)充一些可能有用的方法:

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的字段,我都用了字符串做保存,是借用字符串類型保存數(shù)據(jù)時(shí)能容納更多的數(shù)據(jù)類型。

2. 常見數(shù)據(jù)操作接口

在正常開發(fā)中,一個(gè)完整的數(shù)據(jù)操作接口會(huì)有很多分類,但是很多時(shí)候我們需要分開增刪改和查詢這兩種操作。對于數(shù)據(jù)庫而言,視圖和有些數(shù)據(jù)表都是不被允許改變的,這時(shí)候就需要我們只對調(diào)用方開放查詢接口,而不開放修改接口。

所以,在Domain下應(yīng)該有以下兩個(gè)接口:

using System;
using System.Collections.Generic;
using System.Linq.Expressions;

namespace Domain.Infrastructure
{
    /// <summary>
    /// 修改接口
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface IModifyRepository<T>
    {
        /// <summary>
        /// 插入數(shù)據(jù)
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        T Insert(T entity);
        /// <summary>
        /// 插入數(shù)據(jù)
        /// </summary>
        /// <param name="entities"></param>
        void Insert(params T[] entities);
        /// <summary>
        /// 插入數(shù)據(jù)
        /// </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>
        /// 更新數(shù)據(jù)
        /// </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>
        /// 刪除數(shù)據(jù)
        /// </summary>
        /// <param name="entities"></param>
        void Delete(params T[] entities);
        /// <summary>
        /// 根據(jù)條件刪除數(shù)據(jù)
        /// </summary>
        /// <param name="predicate"></param>
        void Delete(Expression<Func<T,bool>> predicate);
        /// <summary>
        /// 刪除主鍵對應(yīng)的數(shù)據(jù)
        /// </summary>
        /// <param name="key"></param>
        void DeleteByKey(object key);
        /// <summary>
        /// 刪除主鍵對應(yīng)的數(shù)據(jù)
        /// </summary>
        /// <param name="keys"></param>
        void DeleteByKeys(params object[] keys);       
    }
}

上述是更新接口,那么我們回過頭來寫查詢接口,查詢接口的方法有很多。我們先創(chuàng)建一個(gè)接口文件:

using System;
using System.Linq.Expressions;

namespace Domain.Infrastructure
{
    /// <summary>
    /// 查詢接口
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public interface ISearchRepository<T>
    {
        
    }
}

一個(gè)查詢接口應(yīng)該包括以下方法:

  • 獲取單個(gè)數(shù)據(jù)
/// <summary>
/// 根據(jù)主鍵獲取數(shù)據(jù)
/// </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);
  • 統(tǒng)計(jì)數(shù)量:
/// <summary>
/// 返回?cái)?shù)據(jù)庫中的數(shù)據(jù)條目
/// </summary>
/// <returns></returns>
int Count();
/// <summary>
/// 返回?cái)?shù)據(jù)庫中的數(shù)據(jù)條目,類型為Long
/// </summary>
/// <returns></returns>
long LongCount();
/// <summary>
/// 返回符合條件的數(shù)據(jù)數(shù)目
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
int Count(Expression<Func<T,bool>> predicate);
/// <summary>
/// 返回長整形的符合條件的數(shù)目
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
long LongCount(Expression<Func<T,bool>> predicate);
  • 存在性判斷
/// <summary>
/// 是否存在滿足條件的數(shù)據(jù)
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
bool IsExists(Expression<Func<T, bool>> predicate);
  • 查詢
// <summary>
/// 返回?cái)?shù)據(jù)庫中所有記錄
/// </summary>
/// <returns></returns>
List<T> Search();
/// <summary>
/// 返回所有符合條件的數(shù)據(jù)
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
List<T> Search(Expression<Func<T,bool>> predicate);
/// <summary>
/// 返回一個(gè)延遲查詢的對象
/// </summary>
/// <returns></returns>
IEnumerable<T> Query();
/// <summary>
/// 返回一個(gè)延遲查詢的對象,并預(yù)設(shè)了一個(gè)查詢條件
/// </summary>
/// <param name="predicate"></param>
/// <returns></returns>
IEnumerable<T> Query(Expression<Func<T,bool>> predicate);
  • 排序
/// <summary>
/// 排序查詢,默認(rèn)升序
/// </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);
  • 分頁

實(shí)際上分頁的接口定義模型需要兩個(gè)類的輔助,如果沒有這兩個(gè)類,接口的定義會(huì)變得十分復(fù)雜,不利于代碼的可讀性:

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>
        /// 升序排序或者降序排序,升序?yàn)?asc或者空,降序?yàn)閐esc
        /// </summary>
        /// <value></value>
        public string Sort{get;set;}
        /// <summary>
        /// 每頁最大數(shù)據(jù)容量
        /// </summary>
        /// <value></value>
        public int PerpageSize { get; set; }
        /// <summary>
        /// 當(dāng)前頁
        /// </summary>
        /// <value></value>
        public int CurrentPage { get; set; }
    }
    /// <summary>
    /// 分頁結(jié)果
    /// </summary>
    /// <typeparam name="T"></typeparam>
    public class PageModel<T>
    {
        /// <summary>
        /// 數(shù)據(jù)
        /// </summary>
        /// <value></value>
        public List<T> Items { get; set; }
        /// <summary>
        /// 當(dāng)前頁碼
        /// </summary>
        /// <value></value>
        public int CurrentPage { get; set; }
        /// <summary>
        /// 每頁最大數(shù)據(jù)容量
        /// </summary>
        /// <value></value>
        public int PerpageSize { get; set; }
        /// <summary>
        /// 查詢數(shù)據(jù)總數(shù)
        /// </summary>
        /// <value></value>
        public long TotalCount { get; set; }
        /// <summary>
        /// 總頁碼
        /// </summary>
        /// <value></value>
        public int TotalPages { get; set; }
    }
}

這是兩個(gè)輔助類,可以簡單看一下如果這些參數(shù)不進(jìn)行封裝直接傳給方法,可以預(yù)見方法的參數(shù)列表會(huì)特別長,這對于可讀性和可維護(hù)性來說簡直就是災(zāi)難。我曾經(jīng)接手過一個(gè)項(xiàng)目的維護(hù),上一位開發(fā)者在一個(gè)方法寫了近15個(gè)參數(shù),而且還有大量的可選參數(shù),嗯,十分頭疼。所以,我不建議大家這樣寫,一個(gè)方法參數(shù)超過4個(gè)我建議還是封裝一下。

那么,看一看方法的聲明:

/// <summary>
/// 根據(jù)分頁參數(shù)設(shè)置,進(jìn)行分頁查詢
/// </summary>
/// <param name="condition"></param>
/// <returns></returns>
PageModel<T> Search(PageCondition<T> condition);

這是使用參數(shù)封裝了請求的寫法,小伙伴們可以試試不用封裝,方法是如何聲明的。

3. 總結(jié)

在這一篇帶領(lǐng)大家梳理了一下數(shù)據(jù)訪問的接口定義,對一個(gè)系統(tǒng)來說,這些方法都是有必要的(但不是每個(gè)方法使用頻率都一樣高)。也是簡單的跟大家分享一下我在實(shí)際工作中寫代碼的總結(jié)。

更多內(nèi)容煩請關(guān)注我的博客《高先生小屋》

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容