asp.net core 一個中小型項目實戰的起手式——項目搭建與倉儲模式下的持久層創建(1)

来源:https://www.cnblogs.com/invoker-Fv/archive/2019/08/10/11331009.html
-Advertisement-
Play Games

常規的中小型項目搭建方式一般是三層架構加上mvc與webapi作為一個主要框架,再加上一些第三方庫,例如orm框架(EF、SqlSugar、Dapper等),API文檔工具(Swagger)這些的應用。 接下來我們以一個數據管理平臺做為實戰項目來演示三層架構與倉儲模式。 項目Github連接: 一、 ...


  常規的中小型項目搭建方式一般是三層架構加上mvc與webapi作為一個主要框架,再加上一些第三方庫,例如orm框架(EF、SqlSugar、Dapper等),API文檔工具(Swagger)這些的應用。

  接下來我們以一個數據管理平臺做為實戰項目來演示三層架構與倉儲模式。

  項目Github連接:

https://github.com/FaithGuo/DataManager

 

  一、項目創建

  首先搭建以下目錄結構

 

1. 數據持久介面:IDAL

2.數據持久實現:DAL

3.業務邏輯介面:IBLL

4.業務邏輯實現:BLL

5.業務規則介面:IBussness

6.業務規則實現:Bussness

7.數據實體:Entity

8.模型:Model

9.公共工具:Common

10.頁面UI:Web

其中我將常規三層的業務邏輯層分為了業務邏輯與業務規則兩部分,這樣分層的優點是,便於後期維護;缺點是工作量的增加。

二、第三方庫的加入

項目搭建完了自然就是加入一些第三方框架。

這裡我選擇的ORM框架為SqlSugar:

    分別在Common、Entity、IDAL與DAL項目依賴項中右鍵-管理NuGet程式包-搜索sqlSugarCore-安裝(此處為5.0.0.9版本)

    這個項目中我將sqlSugar資料庫鏈接類放入到Common中,以便未來其他項目中可以直接復用Common項目,所以才在Common中引入sqlSugarCore。

    首先在Common中新建BaseDbContext類:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Text;
using Microsoft.Extensions.Configuration;
using SqlSugar;

namespace DataManager.Common.Db
{
    /// <summary>
    /// 資料庫操作基類
    /// </summary>
    public class BaseDbContext
    {
        public SqlSugarClient Db;

        /// <summary>
        /// 構造函數
        /// </summary>
        public BaseDbContext(IConfiguration configuration)
        {
            try
            {
                //主庫
                var connMain = ConfigurationManager.AppSettings["ConnMain"];
                //從庫
                var connFrom = ConfigurationManager.AppSettings["ConnFrom"];
                InitDataBase(connFrom == null
                    ? new List<string> {connMain.ToString()}
                    : new List<string> {connMain.ToString(), connFrom.ToString()});
            }
            catch (Exception ex)
            {
                throw new Exception("未配置資料庫連接字元串");
            }
        }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="listConnSettings">
        /// 連接字元串配置Key集合,配置多個連接則是讀寫分離 
        /// </param>
        public BaseDbContext(List<string> listConnSettings)
        {
            try
            {
                var listConn = new List<string>();
                foreach (var t in listConnSettings)
                {
                    listConn.Add(ConfigurationManager.ConnectionStrings[t].ToString());
                }

                InitDataBase(listConn);
            }
            catch
            {
                throw new Exception("未配置資料庫連接字元串");
            }

        }

        /// <summary>
        /// 構造函數
        /// </summary>
        /// <param name="serverIp">伺服器IP</param>
        /// <param name="user">用戶名</param>
        /// <param name="pass">密碼</param>
        /// <param name="dataBase">資料庫</param>
        public BaseDbContext(string serverIp, string user, string pass, string dataBase)
        {
            InitDataBase(new List<string>
                {$"server={serverIp};user id={user};password={pass};persistsecurityinfo=True;database={dataBase}"});
        }

        /// <summary>
        /// 初始化資料庫連接
        /// </summary>
        /// <param name="listConn">連接字元串</param>
        private void InitDataBase(List<string> listConn)
        {
            var connStr = ""; //主庫
            var slaveConnectionConfigs = new List<SlaveConnectionConfig>(); //從庫集合
            for (var i = 0; i < listConn.Count; i++)
            {
                if (i == 0)
                {
                    connStr = listConn[i]; //主資料庫連接
                }
                else
                {
                    slaveConnectionConfigs.Add(new SlaveConnectionConfig()
                    {
                        HitRate = i * 2,
                        ConnectionString = listConn[i]
                    });
                }
            }

            //如果配置了 SlaveConnectionConfigs那就是主從模式,所有的寫入刪除更新都走主庫,查詢走從庫,
            //事務內都走主庫,HitRate表示權重 值越大執行的次數越高,如果想停掉哪個連接可以把HitRate設為0
            var ctx = new ConfigureExternalServices();

            Db = new SqlSugarClient(new ConnectionConfig()
            {
                ConnectionString = connStr,
                DbType = DbType.SqlServer,
                IsAutoCloseConnection = true,
                SlaveConnectionConfigs = slaveConnectionConfigs
            });
            Db.Ado.CommandTimeOut = 30000; //設置超時時間
            Db.Aop.OnLogExecuted = (sql, pars) => //SQL執行完事件
            {
            };
            Db.Aop.OnLogExecuting = (sql, pars) => //SQL執行前事件
            {
            };
            Db.Aop.OnError = (exp) => //執行SQL 錯誤事件
            {
                throw new Exception("出錯SQL:" + exp.Sql + "\r\n" + exp.Message);
            };
            Db.Aop.OnExecutingChangeSql = (sql, pars) => //SQL執行前 可以修改SQL
            {
                return new KeyValuePair<string, SugarParameter[]>(sql, pars);
            };
        }

        public SqlSugarClient GetClient() => Db;
    }
}
DataManage.Common.Db

    查詢排序條件類:

using System;
using System.Collections.Generic;
using System.Text;
using SqlSugar;

namespace DataManager.Common
{
    /// <summary>
    /// 排序類型
    /// </summary>
    public enum OrderSequence
    {
        Asc,
        Desc
    }

    /// <summary>
    /// 排序枚舉
    /// </summary>
    public class OrderByClause
    {
        public string Sort { get; set; }
        public OrderSequence Order { get; set; }
    }

    /// <summary>
    /// 查詢條件
    /// </summary>
    public class QueryDescriptor
    {
        /// <summary>
        /// 行數
        /// </summary>
        public int PageSize { get; set; }

        /// <summary>
        /// 頁碼
        /// </summary>
        public int PageIndex { get; set; }

        /// <summary>
        /// 排序
        /// </summary>
        public List<OrderByClause> OrderBys { get; set; }

        /// <summary>
        /// 條件
        /// </summary>
        public List<ConditionalModel> Conditions { get; set; }
    }
}
DataManager.Common

    在IDAL中新建倉儲基類介面IBaseRepository:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;
using DataManager.Common;
using SqlSugar;

namespace DataManager.IDAL
{
    public interface IBaseRepository<T> : IDisposable where T : class, new()
    {
        #region 事務
        /// <summary>
        /// 初始化事務
        /// </summary>
        /// <param name="level"></param>
        void BeginTran(IsolationLevel level = IsolationLevel.ReadCommitted);

        /// <summary>
        /// 完成事務
        /// </summary>
        void CommitTran();

        /// <summary>
        /// 完成事務
        /// </summary>
        void RollbackTran();
        #endregion

        #region 新增 
        /// <summary>
        /// 新增
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entity"> 實體對象 </param> 
        /// <param name="isLock">是否加鎖</param>
        /// <returns>操作影響的行數</returns>
        int Add<T>(T entity, bool isLock = false) where T : class, new();

        /// <summary>
        /// 新增
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entitys">泛型集合</param>
        /// <param name="isLock">是否加鎖</param>
        /// <returns>操作影響的行數</returns>
        int Add<T>(List<T> entitys, bool isLock = false) where T : class, new();

        /// <summary>
        /// 新增
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entity"> 實體對象 </param> 
        /// <param name="isLock">是否加鎖</param>
        /// <returns>返回實體</returns>
        T AddReturnEntity<T>(T entity, bool isLock = false) where T : class, new();
        /// <summary>
        /// 新增
        /// </summary> 
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entity"> 實體對象 </param> 
        /// <param name="isLock">是否加鎖</param>
        /// <returns>返回bool, 並將identity賦值到實體</returns>
        bool AddReturnBool<T>(T entity, bool isLock = false) where T : class, new();

        /// <summary>
        /// 新增
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entitys">泛型集合</param>
        /// <param name="isLock">是否加鎖</param>
        /// <returns>返回bool, 並將identity賦值到實體</returns>
        bool AddReturnBool<T>(List<T> entitys, bool isLock = false) where T : class, new();
        #endregion

        #region 修改 

        /// <summary>
        /// 修改數據源
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <returns>數據源</returns>
        IUpdateable<T> Updateable<T>() where T : class, new();

        /// <summary>
        /// 修改(主鍵是更新條件)
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entity"> 實體對象 </param> 
        /// <param name="isLock"> 是否加鎖 </param> 
        /// <returns>操作影響的行數</returns>
        int Update<T>(T entity, bool isLock = false) where T : class, new();

        /// <summary>
        /// 修改
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="update"> 欄位集合 </param> 
        /// <param name="where"> 條件 </param> 
        /// <param name="isLock"> 是否加鎖 </param> 
        /// <returns>操作影響的行數</returns>
        int Update<T>(Expression<Func<T, T>> update, Expression<Func<T, bool>> where, bool isLock = false) where T : class, new();

        /// <summary>
        /// 修改(主鍵是更新條件)
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entitys">泛型集合</param>
        /// <param name="isLock"> 是否加鎖 </param> 
        /// <returns>操作影響的行數</returns>
        int Update<T>(List<T> entitys, bool isLock = false) where T : class, new();
        #endregion

        #region 刪除


        /// <summary>
        /// 刪除(主鍵是條件)
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="entity"> 實體對象 </param> 
        /// <param name="isLock"> 是否加鎖 </param> 
        /// <returns>操作影響的行數</returns>
        int Delete<T>(T entity, bool isLock = false) where T : class, new();

        /// <summary>
        /// 刪除(主鍵是條件)
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="where"> 條件 </param> 
        /// <param name="isLock"> 是否加鎖 </param> 
        /// <returns>操作影響的行數</returns>
        int Delete<T>(Expression<Func<T, bool>> where, bool isLock = false) where T : class, new();
        #endregion

        #region 查詢 

        /// <summary>
        /// 查詢數據源
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <returns>數據源</returns>
        ISugarQueryable<T> Queryable<T>() where T : class, new();

        /// <summary>
        /// 查詢
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="whereLambda">查詢表達式</param>
        /// <returns></returns>
        T Query<T>(Expression<Func<T, bool>> whereLambda) where T : class, new();

        /// <summary>
        /// 查詢集合
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="whereLambda">查詢表達式</param>
        /// <returns>實體</returns>
        List<T> QueryList<T>(Expression<Func<T, bool>> whereLambda) where T : class, new();

        /// <summary>
        /// 查詢集合
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="sql">sql</param>
        /// <returns>實體</returns>
        List<T> QueryList<T>(string sql) where T : class, new();

        /// <summary>
        /// 查詢集合
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="whereLambda">查詢表達式</param>
        /// <returns>實體</returns>
        DataTable QueryDataTable<T>(Expression<Func<T, bool>> whereLambda) where T : class, new();

        /// <summary>
        /// 查詢集合
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="sql">sql</param>
        /// <returns>實體</returns>
        DataTable QueryDataTable<T>(string sql) where T : class, new();

        /// <summary>
        /// 分頁查詢
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="query">查詢條件</param>
        /// <param name="totalCount">總行數</param>
        /// <returns>實體</returns>
        List<T> QueryPageList<T>(QueryDescriptor query, out int totalCount) where T : class, new();

        /// <summary>
        /// 分頁查詢
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="query">查詢條件</param>
        /// <param name="totalCount">總行數</param>
        /// <returns>實體</returns>
        DataTable QueryDataTablePageList<T>(QueryDescriptor query, out int totalCount) where T : class, new();

        /// <summary>
        /// 查詢集合
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型)</typeparam>
        /// <param name="whereLambda">查詢表達式</param>
        /// <returns>Json</returns>
        string QueryJson<T>(Expression<Func<T, bool>> whereLambda) where T : class, new();

        /// <summary>
        /// 查詢存儲過程
        /// </summary>
        /// <param name="procedureName">存儲過程名稱</param>
        /// <param name="parameters">參數</param>
        DataTable QueryProcedure(string procedureName, List<SugarParameter> parameters);

        /// <summary>
        /// 查詢前多少條數據
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <param name="whereLambda">查詢表達式</param>
        /// <param name="num">數量</param>
        /// <returns></returns>
        List<T> Take<T>(Expression<Func<T, bool>> whereLambda, int num) where T : class, new();

        /// <summary>
        /// 查詢單條數據
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <param name="whereLambda">查詢表達式</param> 
        /// <returns></returns>
        T First<T>(Expression<Func<T, bool>> whereLambda) where T : class, new();

        /// <summary>
        /// 是否存在
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <param name="whereLambda">查詢表達式</param> 
        /// <returns></returns>
        bool IsExist<T>(Expression<Func<T, bool>> whereLambda) where T : class, new();

        /// <summary>
        /// 合計
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <param name="field">欄位</param> 
        /// <returns></returns>
        int Sum<T>(string field) where T : class, new();

        /// <summary>
        /// 最大值
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <param name="field">欄位</param> 
        /// <returns></returns>
        object Max<T>(string field) where T : class, new();

        /// <summary>
        /// 最小值
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <param name="field">欄位</param> 
        /// <returns></returns>
        object Min<T>(string field) where T : class, new();

        /// <summary>
        /// 平均值
        /// </summary>
        /// <typeparam name="T">泛型參數(集合成員的類型</typeparam>
        /// <param name="field">欄位</param> 
        /// <returns></returns>
        int Avg<T>(string field) where T : class, new();

        #endregion
    }
}
DataManager.IDAL

    在DAL中新建倉儲基類的實現,在sqlSugar框架中,作者直接將SqlSugarClient預設單例,所以此處不用再做處理可以直接在倉儲基類中繼承BaseDbContext方便直接操作資料庫:

using System;
using System.Collections.Generic;
using System.Data;
using System.Linq.Expressions;
using System.Text;
using DataManager.Common;
using DataManager.Common.Db;
using DataManager.IDAL;
using Microsoft.Extensions.Configuration;
using SqlSugar;

namespace DataManager.DAL
{
    public class BaseService<T> : BaseDbContext, IBaseRepository<T> where T : class, new()
    {
        public SqlSugarClient DbContext;

        public BaseService(IConfiguration configuration) : base(configuration)
        {
            DbContext = GetClient();
        }


        #region 事務

        /// <summary>
        /// 初始化事務
        /// </summary>
        /// <param name="level"></param>
        public void

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 原題 | Generating a PEG Parser 作者 | Guido van Rossum(Python之父) 譯者 | 豌豆花下貓(“Python貓”公眾號作者) 聲明 | 本翻譯是出於交流學習的目的,基於 "CC BY NC SA 4.0" 授權協議。為便於閱讀,內容略有改動。 首發地 ...
  • 更新一篇知識星球裡面的源碼分析文章,去年寫的,周末自己錄了個視頻,大家看下效果好嗎?如果好的話,後面補錄發在知識星球裡面的其他源碼解析文章。 前言 之前自己本地 clone 了 Flink 的源碼,編譯過,然後 share 到了 GitHub 上去了,自己也寫了一些源碼的中文註釋,並且 push 到 ...
  • debug #排除程式故障 print()函數常和#號註釋結合在一起用來debug 多行註釋有兩種快捷操作:1、在需要註釋的多行代碼塊前後加一組三引號''' 2、選中代碼後使用快捷鍵操作:Windows快捷鍵是ctrl+/,Mac為cmd+/,適用於本地編輯器) 一種異常處理的機制,可以在異常出現時 ...
  • 一、補碼簡介 1.電腦中的符號數有三種表示方式,即為:原碼、反碼、補碼。三種表示方法均有符號位和數值位,符號位都是0表示正數,符號位都是1表示負數。 2.電腦中的數字的存儲方式:在電腦系統中,數值一律用補碼來表示和存儲,原因在於:使用補碼可以將符號位和數值域統一處理,同時,加法和減法可以統一處 ...
  • 1.JMS即Java消息服務,是Java平臺中面向消息中間件的API,用於在兩個應用程式之間,或分散式系統中發送消息,進行非同步通信。 2.JMS分為兩種,點對點消息模型 和發佈/訂閱消息模型 3.由六個模塊組成 連接工廠ConnectFactory 消息隊列的連接Connect 用於連接的對話Ses ...
  • 非同步流(AsyncStream) 原文地址: "https://github.com/dotnet/roslyn/blob/master/docs/features/async streams.md" 註意:以下內容最好能根據反編譯工具查看非同步流相關類生成的代碼效果最佳 非同步流是可枚舉類(Enume ...
  • 前言: 你曾經需要應用執行一個任務嗎?比如現在有一個需求,需要每天在零點定時執行一些操作,那應該怎樣操作呢? 這個時候,如果你和你的團隊是用.NET編程的話,可以考慮使用Quartz.NET調度器。允許開發人員根據日期間隔來實現任務調度任務。非常適合在平時的工作中,定時輪詢資料庫同步,定時郵件通知, ...
  • 總結了一下寫項目中遇到的各種方法,總結前輩們的經驗,生成了該類庫,引用net standard類庫,支持net core/net framework. 使用方法 1、nuget 搜索 ZXNetStandardDepot.Common 2、方法說明 2.1 封裝了讀取net core appsett ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...