讓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
  • 前言 本文介紹一款使用 C# 與 WPF 開發的音頻播放器,其界面簡潔大方,操作體驗流暢。該播放器支持多種音頻格式(如 MP4、WMA、OGG、FLAC 等),並具備標記、實時歌詞顯示等功能。 另外,還支持換膚及多語言(中英文)切換。核心音頻處理採用 FFmpeg 組件,獲得了廣泛認可,目前 Git ...
  • OAuth2.0授權驗證-gitee授權碼模式 本文主要介紹如何筆者自己是如何使用gitee提供的OAuth2.0協議完成授權驗證並登錄到自己的系統,完整模式如圖 1、創建應用 打開gitee個人中心->第三方應用->創建應用 創建應用後在我的應用界面,查看已創建應用的Client ID和Clien ...
  • 解決了這個問題:《winForm下,fastReport.net 從.net framework 升級到.net5遇到的錯誤“Operation is not supported on this platform.”》 本文內容轉載自:https://www.fcnsoft.com/Home/Sho ...
  • 國內文章 WPF 從裸 Win 32 的 WM_Pointer 消息獲取觸摸點繪製筆跡 https://www.cnblogs.com/lindexi/p/18390983 本文將告訴大家如何在 WPF 裡面,接收裸 Win 32 的 WM_Pointer 消息,從消息裡面獲取觸摸點信息,使用觸摸點 ...
  • 前言 給大家推薦一個專為新零售快消行業打造了一套高效的進銷存管理系統。 系統不僅具備強大的庫存管理功能,還集成了高性能的輕量級 POS 解決方案,確保頁面載入速度極快,提供良好的用戶體驗。 項目介紹 Dorisoy.POS 是一款基於 .NET 7 和 Angular 4 開發的新零售快消進銷存管理 ...
  • ABP CLI常用的代碼分享 一、確保環境配置正確 安裝.NET CLI: ABP CLI是基於.NET Core或.NET 5/6/7等更高版本構建的,因此首先需要在你的開發環境中安裝.NET CLI。這可以通過訪問Microsoft官網下載並安裝相應版本的.NET SDK來實現。 安裝ABP ...
  • 問題 問題是這樣的:第三方的webapi,需要先調用登陸介面獲取Cookie,訪問其它介面時攜帶Cookie信息。 但使用HttpClient類調用登陸介面,返回的Headers中沒有找到Cookie信息。 分析 首先,使用Postman測試該登陸介面,正常返回Cookie信息,說明是HttpCli ...
  • 國內文章 關於.NET在中國為什麼工資低的分析 https://www.cnblogs.com/thinkingmore/p/18406244 .NET在中國開發者的薪資偏低,主要因市場需求、技術棧選擇和企業文化等因素所致。歷史上,.NET曾因微軟的閉源策略發展受限,儘管後來推出了跨平臺的.NET ...
  • 在WPF開發應用中,動畫不僅可以引起用戶的註意與興趣,而且還使軟體更加便於使用。前面幾篇文章講解了畫筆(Brush),形狀(Shape),幾何圖形(Geometry),變換(Transform)等相關內容,今天繼續講解動畫相關內容和知識點,僅供學習分享使用,如有不足之處,還請指正。 ...
  • 什麼是委托? 委托可以說是把一個方法代入另一個方法執行,相當於指向函數的指針;事件就相當於保存委托的數組; 1.實例化委托的方式: 方式1:通過new創建實例: public delegate void ShowDelegate(); 或者 public delegate string ShowDe ...