net core天馬行空系列-可用於依賴註入的,資料庫表和c#實體類互相轉換的介面實現

来源:https://www.cnblogs.com/hezp/archive/2022/06/10/16350382.html
-Advertisement-
Play Games

1.前言 hi,大家好,我是三合。作為一名程式猿,日常開發中,我們在接到需求以後,一般都會先構思一個模型,然後根據模型寫實體類,寫完實體類後在資料庫里建表,接著進行增刪改查, 也有第二種情況,就是有些人喜歡先在資料庫里建表,然後再添加實體類。前者是code First,後者是db First,如果數 ...


1.前言

hi,大家好,我是三合。作為一名程式猿,日常開發中,我們在接到需求以後,一般都會先構思一個模型,然後根據模型寫實體類,寫完實體類後在資料庫里建表,接著進行增刪改查, 也有第二種情況,就是有些人喜歡先在資料庫里建表,然後再添加實體類。前者是code First,後者是db First,如果資料庫表和c#實體類可以互相轉換的話,那麼無疑將大大加快我們的開發速度,很幸運的是,當前依靠一些第三方建模軟體或者是efcode就可以實現,我們以ef core舉例,

1.1 ef core根據實體類生成資料庫表

 //先定義一個我們自己的dbcontext
public class SqlServerDb : DbContext
{
    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var connectionString = MyConfiguration.GetConfiguration("sqlServerDbConnectionString");
        if (string.IsNullOrWhiteSpace(connectionString))
        {
            throw new ArgumentNullException("sqlServer connectionString must not be null");
        }

        optionsBuilder.UseSqlServer(connectionString);
    }

    public DbSet<Customer> Customer { get; set; }
}
//然後在代碼里這樣調用
using (var database = new SqlServerDb()) 
{
    database.Database.EnsureCreated();
}

那麼efcore就會自動替我們生成一個資料庫,庫裡面有一張表叫做Customer。整個過程無須我們手動干預。接著如果實體類有變更的話,也可以通過add-migration指令進行遷移。

1.2 ef core根據資料庫表生成實體類

通過nuget安裝了Microsoft.EntityFrameworkCore,Microsoft.EntityFrameworkCore.SqlServer,Microsoft.EntityFrameworkCore.Tools,Microsoft.VisualStudio.Web.CodeGeneration.Design這幾個包之後,我們就可以在"程式包管理器控制台"中執行以下語句生成實體類:

Scaffold-DbContext "Data Source=192.168.12.34;Initial Catalog=資料庫名稱;User ID=登錄名;Password=密碼" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force

1.3 ef core互相轉換存在的一些缺陷

在我使用這種方式的過程中,發現了ef core的幾個缺點。

  1. 當整個解決方案下,子項目比較多時,我要在程式包管理器控制台以及各個項目的切換上花費很多時間才能順利執行命令,這就很麻煩。
  2. 我無法快速的獲取某個實體類的建表語句(比如我希望只是生成sql,但不執行遷移),以及快速的從資料庫表生成某個實體類(ef core可以自定義的根據表的集合生成指定的實體類,但是在項目中已存在該實體類的情況,要麼直接生成失敗,要麼根據force條件直接覆蓋項目中原有的實體類,這太粗暴了,有時候會將我手動修改過的實體類覆蓋掉,我希望能自己控制這個生成的過程)。
  3. 很多資料庫的efcore驅動不支持實體類註釋遷移到資料庫表的註釋,這麼重要的功能不知道為啥不實現。
  4. 整個ef core自成一派,沒有提供介面給到我們,這樣就無法將資料庫表和c#實體類互轉的功能集成到我們自己的系統里。
  5. 要獲取遷移生成的sql以供備份,還要執行命令
Script-Migration -From 20220610.cs -To :"202206101.cs" 

,執行這命令還要找到2個變更點,這還是很麻煩。

2 IDbGenerator橫空出世

基於以上痛點,在找不到合適組件的情況下,我在SummerBoot框架中定義了IDbGenerator介面,實現了四種資料庫(sqlserver,mysql,oracle(僅支持12),sqlite)表和c#實體類的互相轉換。具體資料庫表欄位類型和c#類型之間的映射關係,我則是參考了各個ef core驅動里的實現,確保和ef core生成的表或者實體類相一致。接下來介紹整個使用過程。

2.1 通過nuget包添加SummerBoot引用

PM> Install-Package SummerBoot

2.2 在startup.cs類中註冊服務

services.AddSummerBoot();

services.AddSummerBootRepository(it =>
{
	//-----------以下為必填參數---------
	//註冊資料庫類型,比如SqliteConnection,MySqlConnection,OracleConnection,SqlConnection
	it.DbConnectionType = typeof(MySqlConnection);
	//添加資料庫連接字元串
	it.ConnectionString = "";
});

2.3 根據實體類自動生成/修改資料庫表

2.3.1 定義一個資料庫實體類

實體類註解大部分來自於系統命名空間System.ComponentModel.DataAnnotations 和 System.ComponentModel.DataAnnotations.Schema,比如表名Table,列名Column,主鍵Key,主鍵自增DatabaseGenerated(DatabaseGeneratedOption.Identity),不映射該欄位NotMapped,註釋Description等,接下來以Customer為例

public class Customer
 {
    [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { set; get; }
    /// <summary>
    /// 姓名
    /// </summary>
    [Description("姓名")]
    public string Name { set; get; }
    /// <summary>
    /// 年齡
    /// </summary>
    [Description("年齡")]
    public int Age { set; get; } 

    /// <summary>
    /// 會員號
    /// </summary>
    [Description("會員號")]
    public string CustomerNo { set; get; }

    /// <summary>
    /// 總消費金額
    /// </summary>
    [Description("總消費金額")]
    public decimal TotalConsumptionAmount { set; get; }
}

2.3.2 註入IDbGenerator介面,調用GenerateSql方法生成建表或者修改表結構的sql

public class TestController : Controller
{
	private readonly IDbGenerator dbGenerator;

	public TestController(IDbGenerator dbGenerator)
	{
		this.dbGenerator = dbGenerator;
	}

	[HttpGet("GenerateSql")]
	public async Task<IActionResult> GenerateSql()
	{
		var generateSqls = dbGenerator.GenerateSql(new List<Type>() { typeof(Customer) });
		return Content("ok");
	}
}

2.3.3.1 如果資料庫中不存在該表名的表

這裡以mysql為例,生成的sql如下:

CREATE TABLE test.`Customer` (
    `Id` int NOT NULL AUTO_INCREMENT,
    `Name` text NULL ,
    `Age` int NOT NULL ,
    `CustomerNo` text NULL ,
    `TotalConsumptionAmount` decimal(18,2) NOT NULL ,
    PRIMARY KEY (`Id`)
)

ALTER TABLE test.`Customer` MODIFY `Name` text NULL  COMMENT '姓名'
ALTER TABLE test.`Customer` MODIFY `Age` int NOT NULL  COMMENT '年齡'
ALTER TABLE test.`Customer` MODIFY `CustomerNo` text NULL  COMMENT '會員號'
ALTER TABLE test.`Customer` MODIFY `TotalConsumptionAmount` decimal(18,2) NOT NULL  COMMENT '總消費金額'

雖然分成了2部分,沒有生成的非常完美,但是不影響使用。

2.3.3.2 如果資料庫中已存在該表名的表

那麼生成的sql為,新增/更新欄位的sql或者新增/更新註釋的sql,為了避免數據丟失,不會有刪除欄位的sql,這裡以Customer表舉例,如果原本資料庫里已經有了customer表,接著我們更新實體類,添加了一個地址的屬性

public class Customer
{
    [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int Id { set; get; }
    /// <summary>
    /// 姓名
    /// </summary>
    [Description("姓名")]
    public string Name { set; get; }
    /// <summary>
    /// 年齡
    /// </summary>
    [Description("年齡")]
    public int Age { set; get; } 

    /// <summary>
    /// 會員號
    /// </summary>
    [Description("會員號")]
    public string CustomerNo { set; get; }

    /// <summary>
    /// 總消費金額
    /// </summary>
    [Description("總消費金額")]
    public decimal TotalConsumptionAmount { set; get; }
    
    /// <summary>
    /// 地址
    /// </summary>
    [Description("地址")]
    public string Address { set; get; }
}

,那麼此時生成的sql為

ALTER TABLE test.`Customer` ADD `Address` text NULL 
ALTER TABLE test.`Customer` MODIFY `Address` text NULL  COMMENT '地址'

雖然分成了2部分,沒有生成的非常完美,但是不影響使用。

2.3.3.3 可以選擇執行這些sql

把生成sql和執行sql分成2部分操作,對於日常而言是更方便的,我們可以快速拿到要執行的sql,進行檢查,確認沒問題後,可以保存下來,在正式發佈應用時,留給dba審查。執行sql的代碼如下

var generateSqls = dbGenerator.GenerateSql(new List<Type>() { typeof(Customer) });
foreach (var sqlResult in generateSqls)
{
	dbGenerator.ExecuteGenerateSql(sqlResult);
}

2.3.4 表的命名空間

sqlserver里命名空間即schemas,oracle里命名空間即模式,sqlite和mysql里命名空間即資料庫,
如果要定義不同命名空間下的表,可類似添加[Table("CustomerWithSchema", Schema = "test1")]註解即可。

[Table("CustomerWithSchema", Schema = "test1")]
public class CustomerWithSchema
{
	public string Name { set; get; }
        public int Age { set; get; } = 0;
        /// <summary>
        /// 會員號
        /// </summary>
        public string CustomerNo { set; get; }
        /// <summary>
        /// 總消費金額
        /// </summary>
        public decimal TotalConsumptionAmount { set; get; }
}

那麼此時生成的sql為

CREATE TABLE test1.`CustomerWithSchema` (
    `Name` text NULL ,
    `Age` int NOT NULL ,
    `CustomerNo` text NULL ,
    `TotalConsumptionAmount` decimal(18,2) NOT NULL 
)

2.3.5 自定義實體類欄位到資料庫欄位的類型映射或名稱映射

這裡統一使用column註解,如[Column("Age",TypeName = "float")]

public class Customer : BaseEntity
{
	public string Name { set; get; }
		
	[Column("Age",TypeName = "float")]
	public int Age { set; get; } = 0;
	/// <summary>
	/// 會員號
	/// </summary>
	public string CustomerNo { set; get; }
	/// <summary>
	/// 總消費金額
	/// </summary>
	public decimal TotalConsumptionAmount { set; get; }
}

生成的sql如下

CREATE TABLE `Customer2` (
    `Id` int NOT NULL AUTO_INCREMENT,
    `Name` text NULL ,
    `Age` float NOT NULL ,
    `CustomerNo` text NULL ,
    `TotalConsumptionAmount` decimal(18,2) NOT NULL ,
    PRIMARY KEY (`Id`)
)

2.4 根據資料庫表自動生成實體類

2.4.1 註入IDbGenerator介面,調用GenerateCsharpClass方法生成c#類的文本

參數為資料庫表名的集合和生成的實體類的命名空間

public class TestController : Controller
{
	private readonly IDbGenerator dbGenerator;

	public TestController(IDbGenerator dbGenerator)
	{
		this.dbGenerator = dbGenerator;
	}

	[HttpGet("GenerateClass")]
	public async Task<IActionResult> GenerateClass()
	{
		var generateClasses = dbGenerator.GenerateCsharpClass(new List<string>() { "Customer" },"Test.Model");
		return Content("ok");
	}
}

生成的c#實體類如下,新建一個類文件並把文本黏貼進去即可

using System;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
namespace Test.Model
{
   [Table("Customer")]
   public class Customer
   {
      [Key]
      [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
      [Column("Id")]
      public int Id { get; set; }
      /// <summary>
      ///姓名
      /// </summary>
      [Column("Name")]
      public string Name { get; set; }
      /// <summary>
      ///年齡
      /// </summary>
      [Column("Age")]
      public int Age { get; set; }
      /// <summary>
      ///會員號
      /// </summary>
      [Column("CustomerNo")]
      public string CustomerNo { get; set; }
      /// <summary>
      ///總消費金額
      /// </summary>
      [Column("TotalConsumptionAmount")]
      public decimal TotalConsumptionAmount { get; set; }
      /// <summary>
      ///地址
      /// </summary>
      [Column("Address")]
      public string Address { get; set; }
   }
}

3.與倉儲介面配合使用

首先定義倉儲介面,介面的具體實現類會由SummerBoot框架自動生成

[AutoRepository]
public interface ICustomerRepository:IBaseRepository<Customer>
{
}

接著就可以直接註入使用,整個增刪改查操作(支持同步非同步)如下所示

[ApiController]
[Route("[controller]")]
public class HomeController : ControllerBase
{
  private readonly ICustomerRepository customerRepository;
  private readonly IDbGenerator dbGenerator;

  public HomeController(ICustomerRepository customerRepository, IDbGenerator dbGenerator)
  {
      this.customerRepository = customerRepository;
      this.dbGenerator = dbGenerator;
  }

  [HttpGet("test")]
  public IActionResult Test()
  {
     var results= dbGenerator.GenerateSql(new List<Type>() { typeof(Customer) });
     var generateClasses = dbGenerator.GenerateCsharpClass(new List<string>() { "Customer" }, "Test.Model");
     //執行ddl操作
     foreach (var databaseSqlResult in results)
     {
        dbGenerator.ExecuteGenerateSql(databaseSqlResult);
     }

     var cusotmer = new Customer()
     {
          Name = "三合",
          Age = 3,
          CustomerNo = "00001",
          Address = "福建省",
          TotalConsumptionAmount = 999
     };
     //增
     customerRepository.Insert(cusotmer);
     //改
     cusotmer.Age = 5;
     customerRepository.Update(cusotmer);
     //也可以這樣改
     customerRepository.Where(it => it.Name == "三合").SetValue(it => it.Age, 6).ExecuteUpdate();
     //查
     var dbCustomer= customerRepository.FirstOrDefault(it => it.Name == "三合");
     //刪
     customerRepository.Delete(dbCustomer);
     //也可以這樣刪
     customerRepository.Delete(it=>it.Name== "三合");

     return Content("ok");
   }
}

自動生成資料庫表與倉儲介面配合使用,就會使我們的整個開發過程順暢無比,猶如行雲流水。

4.結尾

更多用法,可參考SummerBoot文檔,也可以加入QQ群:799648362反饋建議。同時各位看官,如果你覺得這篇文章還不錯的話,請記得一鍵三連哦(推薦+關註+github star)


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

-Advertisement-
Play Games
更多相關文章
  • 我們知道 Python 有很多運算符可以進行數學運算,如果是簡單的問題還好說,但是要處理一些相對複雜的問題也要我們自己一行一行手動的來編寫嗎?答案當然不是,Python 提供了 math 模塊對一些數學運算提供了支持。 1.簡介 math 模塊提供了對 C 標准定義的數學函數的訪問,但該模塊並不支持 ...
  • Skywalking介紹 Skywalking是一個國產的開源框架,2015年有吳晟個人開源,2017年加入Apache孵化器,國人開源的產品,主要開發人員來自於華為,2019年4月17日Apache董事會批准SkyWalking成為頂級項目,支持Java、.Net、NodeJs等探針,數據存儲支持 ...
  • 面試總結 最近棧長面試了一個 5 年經驗的 Java 程式員,簡歷和個人介紹都提到了精通 Java 多線程,於是我就問了幾個多線程方面的問題: 1、實現多線程有哪幾種方式,如何返回結果? 2、多個線程如何實現順序訪問? 3、兩個線程如何進行數據交換? 4、如何統計 5 個線程的運行總耗時? 5、如何 ...
  • 1 背景與挑戰 1.1 背景介紹 1.1.1 課程概述 瞭解雙11 的歷程 學習當前主流的電商系統架構體系 瞭解大促對電商系統的一些挑戰 面對大促活動,站在架構師角度思考,可能有哪些問題,如何應對 1.1.2 雙11歷程 (最早接觸雙11的年份?) ​ 起於2009年,剛開始的雙十一還並不出名,電商 ...
  • 一個工作了6年的粉絲,去阿裡面試,在第一面的時候被問到”Mysql的事務隔離級別“。 他竟然沒有回答上來,一直在私信向我訴苦。 我說,你只能怪年輕時候的你,那個時候不夠努力導致現在的你技術水平不夠。 好吧,關於這個問題,看看普通人和高手的回答。 普通人: Mysql的事務隔離級別它有四種 1.讀已提 ...
  • ArrayList是我們開發中最常用到的集合,但是很多人對它的源碼並不瞭解,導致面試時,面試官問的稍微深入的問題,就無法作答,今天我們一起來探究一下ArrayList源碼。 1. 簡介 ArrayList底層是數組,允許元素是null,能夠動態擴容 size、isEmpty、get、set、add ...
  • 0. 文章目的 面向C#新學者,介紹命名空間(namespace)的概念以及C#中的命名空間的相關內容。 1. 閱讀基礎 理解C與C#語言的基礎語法。 理解作用域概念。 2. 名稱衝突與命名空間 2.1 一個生活例子 假設貓貓頭在北京有一個叫AAA的朋友,在上海有兩個叫AAA的朋友,上海的兩個AAA ...
  • 現在有些客戶的業務數據還在使用傳統Excel來彙總信息,使用Email實現數據的傳遞與交互。iNeuOS線上報表工具的開發與發佈基本上可以替代傳統Excel的使用,並且用戶可以自定義模板、隨意查詢數據等。 ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...