讓EFCore更瘋狂些的擴展類庫(一):通過json文件配置sql語句

来源:http://www.cnblogs.com/skig/archive/2017/01/19/EFCoreExtend.html
-Advertisement-
Play Games

前言 EF通過linq和各種擴展方法,再加上實體模型,編寫資料庫的訪問代碼確實是優美、舒服,但是生成的sql不盡如意、性能低下,尤其是複雜些的邏輯關係,最終大家還是會回歸自然,選擇能夠友好執行sql語句的ORM,認認真真的編寫sql;問題是:EF是否也能夠很友好的執行sql語句?EF提供直接執行sq ...


前言

EF通過linq和各種擴展方法,再加上實體模型,編寫資料庫的訪問代碼確實是優美、舒服,但是生成的sql不盡如意、性能低下,尤其是複雜些的邏輯關係,最終大家還是會回歸自然,選擇能夠友好執行sql語句的ORM,認認真真的編寫sql;問題是:EF是否也能夠很友好的執行sql語句?EF提供直接執行sql語句的方法並不多,而且也是極其簡單的是否容易進行擴展?答案是肯定的,在DbContext下提供了Database屬性就是為了執行sql用的,然後自己就通過Database下的方法屬性進行了擴展(不過最後為了各種資料庫的相容性,使用了DbContext的擴展方法GetService獲取相應的服務進行sql語句的執行),完成這個擴展類庫的編寫

擴展類庫大體功能簡介:

1) sql語句執行器:用於直接執行sql語句

2) EF的查詢緩存器IQueryable(linq) sql語句 的查詢緩存,分為本地存儲 或 非本地存儲(Redis)

a) 緩存存儲:永久緩存(不過期) 或者 過期緩存

b) 緩存清理

3) sql配置管理器(讓EFCoreMyBatis配置sql,但是通過json配置):載入與管理配置文件中的sql語句

a) sql配置執行器:用於執行配置的sql語句

b) 策略管理器:用於管理策略 策略執行器(目前分為三種策略執行器)

i. 策略管理:管理各種策略類型,用於初始化配置文件中的策略配置轉換成對象

ii. 策略執行器(一般通過策略對象進行相應的處理)

1. 初始化的策略執行器

a) 配置策略對象的初始化、替換表名合併分部sql等的策略執行器

2. sql執行前的策略執行器

a) foreach策略執行器:對SqlParameter或者某些數據類型(list/dictionary/model)進行遍歷生成字串替換到sql

3. sql執行時的策略執行器

a) sql與參數的日誌記錄策略執行器

b) 查詢緩存與清理策略執行器

4) 類庫的擴展與優化(因為類庫中的各種類是通過DI進行管理的,因此易於擴展與優化)

a) 將查詢緩存存儲到Redis

b) 策略與策略執行器的擴展

c) 其他:例如反射幫助類的優化(如果有更好的實現,因為類庫內部有不少實現需要通過反射)

 

源碼:

githubhttps://github.com/skigs/EFCoreExtend 

引用類庫:

nugethttps://www.nuget.org/packages/EFCoreExtend/ 

PM> Install-Package EFCoreExtend

查詢緩存引用Redis

PM> Install-Package EFCoreExtend.Redis

類庫的使用說明會分幾篇文章進行詳細描述,也可參考源碼(源碼中也有使用測試),類庫目前僅支持EFCore 1.1.0,相容性:MSSqlServersqlitemysqlPostgreSql基本都相容(EFCore相容的應該都可以相容),因為剛完成不久,可能還存在一些bug或不合理的地方,望大家諒解,也請告知。

 

通過json文件配置sql

Person.json配置文件內容:

{
  //"name" :  "Person", //設置表名,如果不指定name,那麼預設文件名為表名
  //配置sql:key為Sql的名稱(SqlName,獲取配置sql執行器的時候需要根據key獲取)
  "sqls": {
    "GetList": {
      //"sql": "select name,birthday,addrid from [Person] where name=@name or id=@id",
      "sql": "select name,birthday,addrid from ##tname where name=@name or id=@id", //##tname => 表名
      "type": "query"
    }
  }
}

表的實體模型:

1     [Table(nameof(Person))]
2     public class Person
3     {
4         public int id { get; set; }
5         public string name { get; set; }
6         [Column(TypeName = "datetime")]
7         public DateTime? birthday { get; set; }
8         public int? addrid { get; set; }
9     }
View Code

DbContext(MSSqlServersqlitemysqlPostgreSql)

 1     public class MSSqlDBContext : DbContext
 2     {
 3         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
 4         {
 5             if (optionsBuilder.IsConfigured == false)
 6             {
 7                 optionsBuilder.UseSqlServer(@"data source=localhost;initial catalog=TestDB;uid=sa;pwd=123;");
 8             }
 9             base.OnConfiguring(optionsBuilder);
10         }
11 
12         public DbSet<Person> Person { get; set; }
13     }
14 
15     public class SqlieteDBContext : DbContext
16     {
17         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
18         {
19             if (optionsBuilder.IsConfigured == false)
20             {
21                 optionsBuilder.UseSqlite(@"data source=./Datas/db.sqlite"); //把/Datas/db.sqlite放到bin下
22             }
23             base.OnConfiguring(optionsBuilder);
24         }
25 
26         public DbSet<Person> Person { get; set; }
27     }
28 
29     public class MysqlDBContext : DbContext
30     {
31         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
32         {
33             if (optionsBuilder.IsConfigured == false)
34             {
35                 //SapientGuardian.EntityFrameworkCore.MySql
36                 optionsBuilder.UseMySQL(@"Data Source=localhost;port=3306;Initial Catalog=testdb;user id=root;password=123456;");
37             }
38             base.OnConfiguring(optionsBuilder);
39         }
40 
41         public DbSet<Person> Person { get; set; }
42 }
43 
44     public class PostgreSqlDBContext : DbContext
45     {
46         protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
47         {
48             if (optionsBuilder.IsConfigured == false)
49             {
50                 optionsBuilder.UseNpgsql(@"User ID=admin;Password=123456;Host=localhost;Port=5432;Database=TestDB;Pooling=true;");
51             }
52             base.OnConfiguring(optionsBuilder);
53         }
54 
55         public DbSet<Person> Person { get; set; }
56     }
View Code

載入配置文件(在程式初始化的時候調用):

1             ////載入指定的配置文件
2             //EFHelper.Services.SqlConfigMgr.Config.LoadFile(Directory.GetCurrentDirectory() + "/Person.json");
3             //載入指定目錄下的所有json配置文件
4             EFHelper.Services.SqlConfigMgr.Config.LoadDirectory(Directory.GetCurrentDirectory() + "/Datas");

獲取與調用配置sql的代碼:

 1             DbContext db = new MSSqlDBContext();
 2             //獲取指定表(配置文件名)的配置信息
 3             var tinfo = db.GetConfigTable<Person>();
 4             //獲取指定sql的執行器
 5             var exc = tinfo.GetExecutor();  //使用了CallerMemberNameAttribute,因此會自動獲取 方法/屬性名 作為參數
 6             var exc1 = tinfo.GetExecutor("GetList");    //這行和上面的一樣,"GetList"為在配置文件配置的key
 7 
 8             //執行sql:
 9             //方式一:使用SqlParameter傳遞sql參數
10             var rtn1 = exc.Query<Person>(   //泛型為返回值數據類型
11                 //SqlParams
12                 new [] { new SqlParameter("name", "tom"), new SqlParameter("id", 1) },
13                 //返回值類型中需要忽略的屬性
14                 new[] { "id" });    //select name,birthday,addrid,並沒有載入獲取id,因此需要忽略,否則拋異常
15 
16             //方式二:使用Dictionary傳遞sql參數
17             var rtn2 = exc.QueryUseDict<Person>(   //泛型為返回值數據類型
18                 //Dictionary => SqlParams
19                 new Dictionary<string, object>
20                 {
21                     { "name", "tom" },
22                     { "id", 1 },
23                 },
24                 //返回值類型中需要忽略的屬性
25                 new[] { "id" });    //select name,birthday,addrid,並沒有載入獲取id,因此需要忽略,否則拋異常
26 
27             //方式三:使用Model傳遞sql參數
28             var rtn3 = exc.QueryUseModel<Person>(
29                 //Model => SqlParams
30                 new { name = "tom", id = 1, addrid = 123 },
31                 //參數Model需要忽略的屬性
32                 new[] { "addrid" },    //where name=@name or id=@id,並不需要設置addrid
33                 //返回值類型中需要忽略的屬性
34                 new[] { "id" });    //select name,birthday,addrid,並沒有載入獲取id,因此需要忽略,否則拋異常

增刪改查sql語句配置內容:

{
  //"name" :  "Person", //設置表名,如果不指定name,那麼預設文件名為表名
  "policies": {
    ////表名策略
    //"tname": {
    //  //"tag": "##tname"  //預設值為 ##tname
    //  "prefix": "[", //首碼
    //  "suffix": "]" //尾碼
    //}
  },
  //配置sql:key為Sql的名稱(SqlName,獲取配置sql執行器的時候需要根據key獲取)
  "sqls": {
    "GetList": {
      //"sql": "select * from [Person] where name=@name",
      "sql": "select * from ##tname where name=@name", //##tname => Table Name
      "type": "query" //可以不設置,如果設置了會在執行前進行類型檢測,
          //  notsure(預設,不確定),query(查詢), nonquery(非查詢),scalar,nonexecute(不用於執行的sql,例如分部sql)
    },
    "GetPerson": {
      "sql": "select * from ##tname where name=@name",
      "type": "query"
    },
    "Count": {
      "sql": "select count(*) from ##tname",
      "type": "scalar"
    },
    "UpdatePerson": {
      "sql": "update ##tname set birthday=@birthday, addrid=@addrid where name=@name",
      "type": "nonquery"
    },
    "AddPerson": {
      "sql": "insert into ##tname(name, birthday, addrid) values(@name, @birthday, @addrid) ",
      "type": "nonquery"
    },
    "DeletePerson": {
      "sql": "delete from ##tname where name=@name",
      "type": "nonquery"
    },
    //執行存儲過程
    "ProcQuery": {
      "sql": "exec TestQuery @name",
      "type": "query"
    },
    "ProcUpdate": {
      "sql": "exec TestUpdate @addrid,@name",
      "type": "nonquery"
    }
  }
}
View Code

調用sql配置的代碼(包括事物處理):

  1     public class PersonBLL
  2     {
  3         string _name = "tom";
  4         DBConfigTable tinfo;
  5         public PersonBLL(DbContext db)
  6         {
  7             //獲取指定表(配置文件名)的配置信息
  8             tinfo = db.GetConfigTable<Person>();
  9         }
 10 
 11         public IReadOnlyList<Person> GetList()
 12         {
 13             return tinfo.GetExecutor().QueryUseModel<Person>(
 14                 //Model => SqlParams
 15                 new { name = _name, id = 123 },
 16                 //不需要的SqlParams
 17                 new[] { "id" },
 18                 //返回值類型需要忽略的屬性
 19                 new[] { "name" });
 20 
 21         }
 22 
 23         public int AddPerson()
 24         {
 25             return tinfo.GetExecutor()  //獲取sql執行器
 26                 .NonQueryUseModel(new Person
 27                 {
 28                     addrid = 1,
 29                     birthday = DateTime.Now,
 30                     name = _name,
 31                 }, null);
 32         }
 33 
 34         public int UpdatePerson(int? addrid = null)
 35         {
 36             var exc = tinfo.GetExecutor();
 37             return exc.NonQueryUseModel(new { name = _name, birthday = DateTime.Now, addrid = addrid }, null);
 38         }
 39 
 40         public int DeletePerson()
 41         {
 42             return tinfo.GetExecutor().NonQueryUseModel(new
 43             {
 44                 name = _name
 45             }, null);
 46         }
 47 
 48         public int Count()
 49         {
 50             var exc = tinfo.GetExecutor();
 51             var rtn = exc.ScalarUseModel(new { name = _name }, null);
 52             //MSSqlServer返回值會為int,而Sqlite會為long,轉換就會出錯,因此需要ChangeValueType
 53             return (int)typeof(int).ChangeValueType(rtn);
 54         }
 55 
 56         public Person GetPerson()
 57         {
 58             return tinfo.GetExecutor().QueryUseModel<Person>(new
 59             {
 60                 name = _name
 61             }, null)?.FirstOrDefault();
 62         }
 63 
 64         //執行存儲過程
 65         public IReadOnlyList<Person> ProcQuery()
 66         {
 67             ////Stored procedure sql:
 68             //create proc TestQuery
 69             //@name varchar(256) = null
 70             //as
 71             //begin
 72             //    select * from person where [name] = @name
 73             //end
 74 
 75             return tinfo.GetExecutor().QueryUseModel<Person>(new { name = "tom" }, null);
 76         }
 77 
 78         //執行存儲過程
 79         public int ProcUpdate()
 80         {
 81             ////Stored procedure sql:
 82             //create proc TestUpdate
 83             //@addrid int = 0,
 84             //@name varchar(256)
 85             //as
 86             //begin
 87 
 88             //    update person set addrid = @addrid where[name] = @name
 89             //end
 90 
 91             return tinfo.GetExecutor().NonQueryUseModel(new { addrid = 3, name = "tom" }, null);
 92         }
 93         
 94         //事物
 95         public void DoTran()
 96         {
 97             try
 98             {
 99                 //開啟事物
100                 tinfo.DB.Database.BeginTransaction();
101                 bool bRtn = UpdatePerson() > 0;
102                 bRtn &= AddPerson() > 0;
103                 if (bRtn)
104                 {
105                     tinfo.DB.Database.CommitTransaction();    //提交
106                 }
107                 else
108                 {
109                     tinfo.DB.Database.RollbackTransaction();  //回滾
110                 }
111             }
112             catch (Exception ex)
113             {
114                 tinfo.DB.Database.RollbackTransaction();  //回滾
115             }
116         }
117 
118     }
View Code

通過代碼設置sql配置:

配置sql除了通過配置文件之外 還可以通過代碼進行配置的:

 1         public void AddSqls()
 2         {
 3             EFHelper.Services.SqlConfigMgr.Config.AddSqls<Person>(new Dictionary<string, IConfigSqlInfo>
 4             {
 5                 {
 6                     "UpdatePerson", //SqlName
 7                     new ConfigSqlInfo
 8                     {
 9                         Sql = $"update {nameof(Person)} set name=@name where id=@id"	   

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

-Advertisement-
Play Games
更多相關文章
  • 1.泛型的約束: (1)介面約束; (2)基類約束,基類約束必須放在第一(假如有多個約束); (3)struct/class約束; (4)多個參數類型的約束,每個類型參數都要用where關鍵字; (5)構造器約束,只能是無參構造器,如new(); (6)約束可以由派生類繼承,但必須在派生類中顯式地指 ...
  • 多線程內容大致分兩部分,其一是非同步操作,可通過專用,線程池,Task,Parallel,PLINQ等,而這裡又涉及工作線程與IO線程;其二是線程同步問題,鄙人現在學習與探究的是線程同步問題。 通過學習《CLR via C#》裡面的內容,對線程同步形成了脈絡較清晰的體繫結構,在多線程中實現線程同步的是 ...
  • 1、設置圖片透明 this.pibox.BackColor = System.Drawing.Color.Transparent; //將背景設置為透明 this.pibox.Parent = lab_show; //將父容器設置為上一層的文件名 2、Timer不起作用 1、先托控制項Timer, 並 ...
  • 1.重寫GetHashCode方法註意點: (1)重寫GetHashCode方法,也應重寫Equals方法,否者編譯器會警告。 (2)相等的對象必須有相等的散列碼(若a.Equals(b),則a.GetHashCode()==b.GetHashCode())。 (3)GetHashCode()不應引 ...
  • 1.結構:結構除了可以含有屬性和欄位,還可以包方法和構造器,但不能包含黠認(無參數}的構造器。有的時候(比如在實例化一個數組的時候)不會調用值類型的構造器,因為所有數組記憶體都轉為用零來初始化,為了避免因為預設構造器只是偶爾調用而造成不一致,C#完全禁止了用戶顯式定義預設構造器,因為編譯器會將聲明時的 ...
  • 接 上一篇 內容, 這裡先看一下錯誤處理過濾器. 在看此部分之前, 先看看MVC已經提供的功能吧. 一. MVC 自帶功能 1. 配置方法 這裡的mode預設是 Off , 就是會在頁面中直接顯示詳細的錯誤信息. 如果是 On, 則不會顯示詳細的錯誤信息, 顯示是這樣的: 在沒有配置具體 statu ...
  • 最近遇到的一個項目中用到了標題所說的方法,用Spring.Net將業務類封閉成WebService供其它地方調用使用,感覺還是蠻新鮮的,於是在園子中搜了一篇園友寫的文章(這裡)自己也嘗試著搭了一個環境,最後是順利跑了起來,但是中間遇到了幾個問題,這裡記錄一下。 具體的搭建方法不再贅述,上面的文章寫的 ...
  • ...
一周排行
    -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.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...