為什么要重復造輪子
因為現有的輪子都在某些方面不太令我滿意,下面我來一一點評一下,歡迎拍磚。
Entity Framework
我喜歡傻瓜化使用方式的框架,同時又不失靈活性。
EF雖然使用起來足夠簡單,但卻不夠靈活。例如,在EF Core中你無法用原生SQL寫一個多表連接查詢(返回的結果是多表連接的結果)
單表簡單條件查詢還好,多表查詢時生成的SQL性能實在不敢恭維,我更喜歡自己寫SQL,可控性更高,心里有底
EF的設計不利於項目的分層;我理想的設計是隔離、低耦合,和數據庫打交道的事就交給Db層好了,所有數據庫的特性都隔離在Db層內部,對外按業務提供傻瓜化的接口。
而使用EF你沒法做到真正的隔離,因為即使能把SaveChanges封裝在Db層,導航屬性也會導致再次查詢數據庫。
Dapper
我喜歡它的輕量級,高性能。但它"只支持"原生SQL讀寫數據庫,使用起來還是不太方便。
很多常用的情景其實可以封裝一下不用寫SQL的,像EF一樣,直接Add一個Entity
雖然現在Dapper已經有了這樣一個封裝,但目前來看實在過於粗糙
PetaPoco
它可以像EF一樣直接Add一個Entity,也可以像Dapper一樣自己寫原生SQL,按理說這已經很完美了。
但是,它不支持批量插入、更新啊
DapperPoco
在實在找不到滿意框架的情況下,於是DapperPoco就誕生了,它是基於Dapper高度封裝的,有Dapper的一切優點,同時也彌補了它的不足,它有如下特點:
- 高性能(與Dapper一致),以熱啟動后計算(第一次啟動有緩存過程)
- 像EF一樣使用簡單,也可像Dapper一樣靈活使用原生SQL
- 支持使用Fluent API定義實體映射
- 內部模塊化靈活、可擴展
現已將其開源並放到了github上,地址為:https://github.com/md-frank/DapperPoco
如何使用
首先定義一個Poco類
//表示文章表里的一條記錄 public class Article { public long Id { get; set; } public string Title { get; set; } public string Content { get; set; } }
創建DbContext
class MasterDbContext : DbContext { protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) { optionsBuilder.UseConnectionString("連接字符串"); //使用SQL Server數據庫 optionsBuilder.UseSqlAdapter(new SqlServerAdapter(SqlClientFactory.Instance)); } //如果不使用Poco可以不重寫此方法 protected override void OnEntitiesBuilding(EntitiesBuilder entityBuilder) { //屬性名與表列名(列名)不一樣,可在此映射別名 entityBuilder.Entity<Article>() .TableName("T_Article") .ColumnName(p => p.Id, "article_id"); } }
插入數據
var masterDb = new MasterDbContext(); //插入一個Poco對象 var a = new Article { Title = "hello", Content = "hello word" }; masterDb.Insert(a); //插入了2條記錄 masterDb.Insert(new Article[] { a, a }); //也可以顯式指定表名 masterDb.Insert(a, "T_Article"); //原生SQL插入 this.Execute("insert T_Article(Title, Content) values (@Title, @Content)", a); //插入了2條記錄 this.Execute("insert T_Article(Title, Content) values (@Title, @Content)", a, a); //插入了2條記錄 this.Execute("insert T_Article(Title, Content) values (@Title, @Content)", new Article[] { a, a }); //也可以直接寫參數值 this.Execute("insert T_Article(Title, Content) values (@p0, @p1)", "hello", "hello word");
更新數據
var masterDb = new MasterDbContext(); //先查出來准備更新 var article = masterDb.FirstOrDefault<Article>("select * from T_Article where article_id = @p0", 1); //更新除主鍵外的所有列 article.Title = "hello 2"; article.Content = "content 1"; masterDb.Update(article); //僅更新指定列,指定表列名 article.Title = "hello 2"; masterDb.Update(article, new [] { "Title" }); //僅更新指定列,指定實體屬性名 article.Title = "hello 3"; article.Content = "content 1"; masterDb.Update(article, null, null, p=> p.Title, p=> p.Content);
保存數據
var masterDb = new MasterDbContext(); var article = new Article { Id = 1, Title = "hello", Content = "hello word" }; //如果記錄存在則更新,不存在則插入 masterDb.Save(article); //保存並指定列名 masterDb.Save(article, new [] { "Title" });
刪除數據
var masterDb = new MasterDbContext(); var article = masterDb.FirstOrDefault<Article>("select * from T_Article where article_id = @p0", 1); //刪除實體記錄 masterDb.Delete(article); //刪除實體記錄,顯式指定主鍵名 masterDb.Delete(article, "article_id");
查詢數據(立即執行)
var masterDb = new MasterDbContext(); //查詢T_Article表所有記錄 var articles = masterDb.FetchAll<Article>(); //指定條件查詢,直接寫參數值 var articles = masterDb.Fetch<Article>("select * from T_Article where Title=@p0 and Content=@p1", "hello", "hello word"); //指定條件查詢,支持列表(實現了IEnumerable接口的) var articles = masterDb.Fetch<Article>("select * from T_Article where article_id in @p0", new [] { 1, 2, 3 }); //查詢單條記錄 masterDb.FirstOrDefault<Article>("select * from T_Article where article_id = @p0", 1); //查詢單列 var count = masterDb.ExecuteScalar<long>("select count(*) from T_Article"); //查詢分頁的結果(第1頁,每頁20條) Paged<Article> paged = masterDb.Paged<Article>(1, 20, "select * from T_Article where Title=@p0", "hello"); //Paged的定義如下 public class Paged<T> where T : new() { //當前頁碼 public int CurrentPage { get; set; } //總頁數 public int TotalPages { get; set; } ///總記錄數 public long TotalItems { get; set; } //每頁記錄數 public int ItemsPerPage { get; set; } //當前頁記錄列表 public List<T> Items { get; set; } }
查詢數據(延遲執行)
延遲查詢使用Query,與Fetch不同的是Query返回的結果只有在使用時才會真正查詢數據庫
var masterDb = new MasterDbContext(); //延遲查詢 var articles = masterDb.Query<Article>("select * from T_Article where Title=@p0", "hello");
動態查詢條件
var title = "此變量來自用戶輸入"; var sb = new SqlBuilder(); sb.Append("select * from T_Article"); if(!string.IsNullOrEmpty(title)) sb.Append("where Title=@p0", title); var sql = sb.Build(); var articles = masterDb.Fetch<Article>(sql.Statement, sql.Parameters);
事務支持
using (var trans = this.GetTransaction()) { //這里修改數據庫 //提交事務 trans.Complete(); }