自研 Fast.ORM 已全面支援AOT編譯

2023-11-23 21:01:26

Fast Framework

作者 Mr-zhong

程式碼改變世界....

一、前言

Fast Framework 基於NET6.0 封裝的輕量級 ORM 框架 支援多種資料庫 SqlServer Oracle MySql PostgreSql Sqlite

優點: 體積小、原生支援微軟特性、流暢API、使用簡單、效能高、模型資料繫結採用 Expression、強大的表示式解析、支援多種子查詢可實現較為複雜查詢、原始碼可讀性強、支援AOT 編譯。

缺點:目前僅支援Db Frist

ps:此版本不再支援動態切換資料庫驅動,DbOptions 依舊支援熱過載

AOT編譯經驗分享

1.不要在方法里根據名稱去動態獲取方法資訊
2.不要使用Emit
3.不要使用dynamic 關鍵字 使用object 代替
4.設定 rd.xml 檔案
5.Json序列化不要使用 System.Text.Json 可以使用 Newtonsoft.Json 替代
6.不要在同一個解決方案去依賴使用反射專案的類庫,需編譯後單獨參照,否則編譯有問題

二、專案明細
名稱 說明
Fast.Framework ORM
Fast.Framework.Logging 檔案紀錄檔 (擴充套件專案可不使用)
Fast.Framework.DependencyInjection 依賴注入 (擴充套件專案可不使用) 不支援AOT
Fast.Framework.Test 控制檯測試專案
Fast.Framework.UnitTest 單元測試專案
Fast.Framework.Web.Test Web測試專案
三、核心物件
  • Ado

                    IAdo ado = new AdoProvider(new DbOptions()
                    {
                        DbId = "1",
                        DbType = DbType.MySQL,
                        ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
                    });
    
  • DbContext 支援多租戶

                    IDbContext db = new DbContext(new List<DbOptions>() {
                    new DbOptions()
                    {
                        DbId = "1",
                        DbType = DbType.MySQL,
                        ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
                    }});
    
  • DbOptions Json檔案設定格式

    "DbOptions": [
        {
          "DbId": 1,
          "DbType": "SQLServer",
          "ConnectionStrings": "server=localhost;database=Test;user=sa;pwd=123456789;min pool size=3;max pool size=100;connect timeout=120;"
        }]
    
  • 主從分離(讀寫分離)設定

      "DbOptions": [
        {
          "DbId": 2,
          "DbType": "MySQL",
          "IsDefault": true,
          "ConnectionStrings": "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=0;max pool size=100;connect timeout=120;AllowLoadLocalInfile=true;",
          "UseMasterSlaveSeparation": true,//使用主從分離 注意所有事務將強制走主庫
          "SlaveItems": [
            {
              "Weight": 1,//注意多個從庫 必須設定權重且總權重>從庫數
              "ConnectionStrings": "server=localhost;database=Test1;user=root;pwd=123456789;port=3306;min pool size=0;max pool size=100;connect timeout=120;AllowLoadLocalInfile=true;",
              "Description": "從庫連線設定"
            },
            {
              "Weight": 2,
              "ConnectionStrings": "server=localhost;database=Test2;user=root;pwd=123456789;port=3306;min pool size=0;max pool size=100;connect timeout=120;AllowLoadLocalInfile=true;",
              "Description": "從庫連線設定"
            }
          ],
          "Description": "主庫連線設定"
        }
      ]
    
  • Asp.net Core 依賴注入

    // 註冊服務 
    var builder = WebApplication.CreateBuilder(args);
    
    // 新增資料庫上下文
    builder.Services.AddFastDbContext();
    
    // 從Json組態檔載入資料庫選項
    builder.Services.Configure<List<DbOptions>>(builder.Configuration.GetSection("DbOptions"));
    
    // 產品服務類 通過構造方法注入
    public class ProductService
    {
        /// <summary>
        /// 資料庫
        /// </summary>
        private readonly IDbContext db;
    
        /// <summary>
        /// 構造方法
        /// </summary>
        /// <param name="db">資料庫</param>
        public ProductService(IDbContext db)
        {
            this.db = db;
        }
    }
    
四、插入
  • 實體物件插入

                var product = new Product()
                {
                    ProductCode = "1001",
                    ProductName = "測試商品1"
                };
                var result = db.Insert(product).Exceute();
    
  • 實體物件插入並返回自增ID 僅支援 SQLServer MySQL SQLite

                var product = new Product()
                {
                    ProductCode = "1001",
                    ProductName = "測試產品1"
                };
                var result = db.Insert(product).ExceuteReturnIdentity();
    
  • 實體物件列表插入

                var list = new List<Product>();
                for (int i = 0; i < 2100; i++)
                {
                    list.Add(new Product()
                    {
                        ProductCode = $"編號{i + 1}",
                        ProductName = $"名稱{i + 1}"
                    });
                }
                var result = db.Insert(list).Exceute();
    
  • 匿名物件插入

                var obj = new
                {
                    ProductCode = "1001",
                    ProductName = "測試商品1"
                };
                //注意:需要使用As方法顯示指定表名稱
                var result = db.Insert(obj).As("Product").Exceute();
    
  • 匿名物件列表插入

                var list = new List<object>();
                for (int i = 0; i < 2100; i++)
                {
                    list.Add(new
                    {
                        ProductCode = $"編號{i + 1}",
                        ProductName = $"名稱{i + 1}"
                    });
                }
                //注意:需要使用As方法顯示指定表名稱
                var result = db.Insert(list).As("Product").Exceute();
    
  • 字典插入

                var product = new Dictionary<string, object>()
                {
                    {"ProductCode","1001"},
                    { "ProductName","測試商品1"}
                };
                var result = db.Insert(product).As("Product").Exceute();
    
  • 字典列表插入

                var list = new List<Dictionary<string, object>>();
                for (int i = 0; i < 2100; i++)
                {
                    list.Add(new Dictionary<string, object>()
                    {
                        {"ProductCode","1001"},
                        { "ProductName","測試商品1"}
                     });
                }
                var result = db.Insert(list).As("Product").Exceute();
    
五、刪除
  • 實體物件刪除

                var product = new Product()
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "測試商品1"
                };
                var result = db.Delete(product).Exceute();
    
  • 實體物件列表刪除

                var list = new List<Product>();
                for (int i = 0; i < 2100; i++)
                {
                    list.Add(new Product()
                    {
                        ProductCode = $"編號{i + 1}",
                        ProductName = $"名稱{i + 1}"
                    });
                }
                var result = db.Delete(list).Exceute();
    
  • 無條件刪除

                var result = db.Delete<Product>().Exceute();
    
  • 條件刪除

                var result = await db.Delete<Product>().Where(w => w.ProductId == 1).ExceuteAsync();
    
  • 邏輯刪除

                   /*     
                    可用特性標記邏輯刪除列 僅支援 int bool datetime 型別,其它型別不合適
    
                    /// <summary>
                    /// 刪除標記
                    /// </summary>
                    [Logic]
                    public bool DeleteMark { get; set; }
    
                    */
    
    				//額外設定其它屬性值,使用SetColumns方法前需先使用IsLogic方法
    				//型別邏輯刪除
                    var result1 = db.Delete<Product>().IsLogic().SetColumns(c => new Product()
                    {
                        ModifyTime = DateTime.Now
                    }).Where(w => w.ProductId == 1).Exceute();
    
                    //物件邏輯刪除
                    var result2 = db.Delete(new Product() { ProductId = 1 }).IsLogic().SetColumns(c => new Product()
                    {
                        ModifyTime = DateTime.Now
                    }).Exceute();
    
                    //特殊邏輯刪除(不想建實體類可以用該方式)
                    var result3 = db.Delete<object>().As("Product").IsLogic().SetColumns(c => new 
                    {
                        ModifyTime = DateTime.Now
                    }).Exceute();
    
  • 特殊刪除

    			//特殊用法 如需單個條件或多個可搭配 Where或WhereColumns方法
                var result = await db.Delete<object>().As("Product").ExceuteAsync();
                Console.WriteLine($"無實體刪除 受影響行數 {result}");
    
六、更新
  • 實體物件更新

                var product = new Product()
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "測試商品1"
                };
                //注意:標記KeyAuttribute特性屬性或使用Where條件,為了安全起見全表更新將必須使用Where方法
                var result = db.Update(product).Exceute();
    
  • 指定列更新

                var result = db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" }).UpdateColumns(c=> new { c.ProductCode , c.ProductName }).Exceute();
    			// 推薦使用表示式 c=>new {} 好處更改屬性名稱可以同步修改
    
  • 忽略列更新

                var result = db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" }).IgnoreColumns(c=> new { c.Custom1 }).Exceute();
                // 同上使用方法一樣
    
  • 實體物件列表更新

                var list = new List<Product>();
                for (int i = 0; i < 2022; i++)
                {
                    list.Add(new Product()
                    {
                        ProductCode = $"編號{i + 1}",
                        ProductName = $"名稱{i + 1}"
                    });
                }
                //注意:標記KeyAuttribute特性屬性或使用WhereColumns方法指定更新條件列
                var result = db.Update(list).Exceute();
    
  • 匿名物件更新

                var obj = new
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "測試商品1"
                };
                //注意:需要顯示指定表名稱 以及更新條件 使用 Where或者WhereColumns方法均可
                var result = db.Update(obj).As("product").WhereColumns("ProductId").Exceute();
    
  • 匿名物件列表更新

                var list = new List<object>();
                for (int i = 0; i < 2022; i++)
                {
                    list.Add(new
                    {
                        ProductId = i + 1,
                        ProductCode = $"編號{i + 1}",
                        ProductName = $"名稱{i + 1}"
                    });
                }
                //由於是匿名物件需要顯示指定表名稱,使用WhereColumns方法指定更新條件列
                var result = db.Update(list).As("Product").WhereColumns("ProductId").Exceute();
    
  • 字典更新

                var product = new Dictionary<string, object>()
                {
                    { "ProductId",1},
                    {"ProductCode","1001"},
                    { "ProductName","測試商品1"}
                };
                var result = db.Update(product).As("Product").WhereColumns("ProductId").Exceute();
    
  • 字典列表更新

                var list = new List<Dictionary<string, object>>();
                for (int i = 0; i < 2022; i++)
                {
                    list.Add(new Dictionary<string, object>()
                    {
                        { "ProductId",i+1},
                        {"ProductCode",$"更新編號:{i+1}"},
                        { "ProductName",$"更新商品:{i + 1}"}
                    });
                }
                var result = db.Update(list).As("Product").WhereColumns("ProductId").Exceute();
    
  • 設定列更新

                // 設定列更新
    			db.Update<Product>().SetColumns(c => new Product()
                {
                    ProductCode = "1001",
                    ProductName = "測試產品1"
                }).Where(w => w.ProductId == 1).Exceute();
    
  • 指定條件更新

                var product = new Product()
                {
                    ProductId = 1,
                    ProductCode = "1001",
                    ProductName = "測試商品1"
                };
                var result = db.Update(product).Where(p => p.ProductId == 100).Exceute();
                Console.WriteLine($"表示式更新 受影響行數 {result}");
    
  • 並行更新 樂觀鎖-版本控制

                //注意:僅支援非列表更新 版本列資料型別僅支援 object、string、Guid 時間型別存在精度丟失所以不做支援
    			var obj = db.Query<Product>().Where(w => w.ProductId == 1).Frist();
                obj.Custom1 = "測試版本控制修改";
    			//引數為 true 更新失敗將丟擲異常
                var result = db.Update(obj).ExceuteWithOptLock(true);
    
七、查詢
  • 單一查詢

                var data = db.Query<Product>().First();
    
  • 列表查詢

                var data = db.Query<Product>().ToList();
    
  • 返回單個字典

                var data = db.Query<Product>().ToDictionary();
    
  • 返回字典列表

                var data = db.Query<Product>().ToDictionaryList();
    
  • 分頁查詢

                //分頁查詢不返回總數
                var data = db.Query<Product>().ToPageList(1,100);
                //分頁查詢返回總數
    			var total = 0;//定義總數變數
    			var data = db.Query<Product>().ToPageList(1, 1, ref total);
                Console.WriteLine($"總數:{total}");
    
  • 計數查詢

                var data = db.Query<Product>().Count();
    
  • 任何查詢

                var data = db.Query<Product>().Any();
    
  • 條件查詢

                var data = db.Query<Product>().Where(w => w.ProductId == 1).ToList;
    
  • Like 查詢

                var data = db.Query<Product>().Where(w => w.ProductName.StartsWith("左模糊") || w.ProductName.EndsWith("右模糊") || w.ProductName.Contains("全模糊")).ToList();
    
  • Not Like查詢

    var data = db.Query<Product>().Where(w => !w.ProductName.StartsWith("左模糊") || !w.ProductName.EndsWith("右模糊") || !w.ProductName.Contains("全模糊")).ToList();
    
  • Select查詢 (選擇欄位)

                var data = db.Query<Product>().Select(s => new
                {
                    s.ProductId,
                    s.ProductName
                }).ToList();
    
  • Select查詢 (Case When)

                    var data = db.Query<Product>().Select(s => new
                    {
                        CaseTest1 = SqlFunc.Case(s.Custom1).When("1").Then("xx1").When("2").Then("xx2").Else("xx3").End(),
                        CaseTest2 = SqlFunc.CaseWhen<string>(s.Custom1 == "1").Then("xx1").When(s.Custom1 == "2").Then("xx2").Else("xx3").End()
                    }).ToList();
    
  • 分組查詢

                var data = db.Query<Product>().GroupBy(s => new
                {
                    s.ProductId,
                    s.ProductName
                }).ToList();
    
  • 分組聚合查詢

                var sql = db.Query<Order>().InnerJoin<OrderDetail>((a, b) => a.OrderId == b.OrderId).GroupBy((a, b) => new
                {
                    a.OrderCode
                }).Select((a, b) => new
                {
                    a.OrderCode,
                    Sum_Qty = SqlFunc.Sum(b.Qty)//支援巢狀
                }).ToList();
    
  • 排序查詢

                var data = db.Query<Product>().OrderBy(s => new
                {
                    s.CreateTime
                }).ToList();
                //這是多個欄位排序使用方法 還有其它過載方法
    
  • Having查詢

                var data = db.Query<Product>().GroupBy(s => new
                {
                    s.ProductId,
                    s.ProductName
                }).Having(s => SqlFunc.Count(s.ProductId) > 1).ToList();
                //必須先使用GroupBy方法 懂得都懂
    
  • 聯表查詢

                var data = db.Query<Product>().LeftJoin<Class1>((a, b) => a.ProductId == b.ProductId).ToList();
                // 右連線 RightJoin 內連線 InnerJoin 全連線 FullJoin
    
  • 聯合查詢

                var query1 = db.Query<Product>();
                var query2 = db.Query<Product>();
                db.Union(query1, query2);//聯合
                db.UnionAll(query1, query2);//全聯合
                //執行查詢呼叫Toxx方法
    
  • 導航查詢 (支援無限層級)

                    /// <summary>
                    /// 類別
                    /// </summary>
                    public class Category
                    {
                        /// <summary>
                        /// 類別ID
                        /// </summary>
                        [Key]
                        public int CategoryId { get; set; }
                    
                        /// <summary>
                        /// 類別名稱
                        /// </summary>
                        public string CategoryName { get; set; }
                    
                        /// <summary>
                        /// 產品 Navigate MainName和ChildName 可不顯示指定,會自動查詢主鍵匹配或ID為結尾的屬性
                        /// </summary>
                        [Navigate(MainName = nameof(CategoryId), ChildName = nameof(Product.CategoryId))]
                        public IEnumerable<Product> Products { get; set; }
                    
                    }
    
                    var data = db.Query<Category>()
                        .Include(i => i.Products)
                        .ToList();
    
  • 查詢並插入 僅支援同範例的資料庫 跨庫 個人還是建議 用事務分開寫查詢和插入

                    //方式1
                    var result1 = db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
                    {
                        s.ProductCode,
                        s.ProductName
                    }).Insert<Product>(p => new
                    {
                        p.ProductCode,
                        p.ProductName
                    });
    
                    //方式2
                    var result2 = db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
                    {
                        s.ProductCode,
                        s.ProductName
                    }).Insert("表名稱 同範例不同庫 可以使用 db.資料庫名稱.表名稱 ", "列名稱1", "列名稱2");
    
                    //方式3 需要注意同方式2 一樣
                    var result3 = db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
                    {
                        s.ProductCode,
                        s.ProductName
                    }).Insert("表名稱 同範例不同庫 可以使用 db.資料庫名稱.表名稱 ", new List<string>() { "列名稱1" });
    
  • In查詢

                    var data1 = db.Query<Product>().Where(w => new List<string>(){"1001", "1002"}.Contains(w.ProductCode)).ToList();
    
  • Select巢狀查詢和子查詢

                    var data1 = db.Query<Product>().Select(s => new
                    {
                        XX = db.Query<Product>().Select(s => 1).First()//需呼叫返回結果的方法 否則無法解析
                    }).First();
    
    				//進價用法,下面範例方法的過載均支援
                    var count = 0;
                    var refAsync = new RefAsync<int>();
    
                    var query = db.Query<Product>().Select(s => new
                    {
                        WithAttr_First = db.QueryWithAttr<Product>().First(),
                        WithAttr_FirstAsync = db.QueryWithAttr<Product>().FirstAsync(),
                        WithAttr_ToList = db.QueryWithAttr<Product>().ToList(),
                        WithAttr_ToListAsync = db.QueryWithAttr<Product>().ToListAsync(),
                        First_1 = db.Query<Category>().Select(s => 1).First(),//解析成Sql
                        First = db.Query<Category>().First(),
                        FirstAsync = db.Query<Category>().FirstAsync(),
                        ToArray = db.Query<Category>().ToArray(),
                        ToArrayAsync = db.Query<Category>().ToArrayAsync(),
                        ToList = db.Query<Category>().ToList(),
                        ToListAsync = db.Query<Category>().ToListAsync(),
                        ToPageList = db.Query<Category>().ToPageList(1, 10),
                        ToPageListAsync = db.Query<Category>().ToPageListAsync(1, 10),
                        ToPageList_Count = db.Query<Category>().ToPageList(1, 10, ref count),
                        ToPageListAsync_Count = db.Query<Category>().ToPageListAsync(1, 10, refAsync),
                        ToDictionary = db.Query<Category>().ToDictionary(),
                        ToDictionaryAsync = db.Query<Category>().ToDictionaryAsync(),
                        ToDictionaryList = db.Query<Category>().ToDictionaryList(),
                        ToDictionaryListAsync = db.Query<Category>().ToDictionaryListAsync(),
                        ToDictionaryPageList = db.Query<Category>().ToDictionaryPageList(1, 10),
                        ToDictionaryPageListAsync = db.Query<Category>().ToDictionaryPageListAsync(1, 10),
                        ToDictionaryPageList_Count = db.Query<Category>().ToDictionaryPageList(1, 10, ref count),
                        ToDictionaryPageListAsync_Count = db.Query<Category>().ToDictionaryPageListAsync(1, 10, refAsync),
                        ToDataTable = db.Query<Category>().ToDataTable(),
                        ToDataTableAsync = db.Query<Category>().ToDataTableAsync(),
                        Max = db.Query<Category>().Max(a => a.CategoryId),//解析成Sql
                        MaxAsync = db.Query<Category>().MaxAsync(a => a.CategoryId),
                        Min = db.Query<Category>().Min(a => a.CategoryId),//解析成Sql
                        MinAsync = db.Query<Category>().MinAsync(a => a.CategoryId),
                        Count = db.Query<Category>().Count(),//解析成Sql
                        CountAsync = db.Query<Category>().CountAsync(),
                        Sum = db.Query<Category>().Sum(s => s.CategoryId),//解析成Sql
                        SumAsync = db.Query<Category>().SumAsync(s => s.CategoryId),
                        Avg = db.Query<Category>().Avg(s => s.CategoryId),//解析成Sql
                        AvgAsync = db.Query<Category>().AvgAsync(s => s.CategoryId)
                    });
    				var data2= query.First();
    
  • From子查詢

                    var subQuery2 = db.Query<Product>().Select(s=>new
                    {
                        s.ProductId,
                        s.CategoryId,
                        s.ProductCode,
                        s.ProductName,
                        s.DeleteMark
                    });
                    var data = db.Query(subQuery2).ToList();
    
  • Join子查詢

                    var subQuery1 = db.Query<Product>().Select(s => new
                    {
                        s.ProductId,
                        s.CategoryId,
                        s.ProductCode,
                        s.ProductName,
                        s.DeleteMark
                    });
                    var data = db.Query<Category>().InnerJoin(subQuery1, (a, b) => a.CategoryId == b.CategoryId).ToList();
    
  • Include查詢

                    // 聯表條件 預設優先匹配主鍵 其次帶有ID結尾的名稱
                    var data = db.Query<Category>().Include(i => i.Products).ToList();
    
  • Exists查詢

                    var data = db.Query<Product>()
                        .Where(w => db.Query<Product>().WhereIF(!string.IsNullOrWhiteSpace("測試"), a => a.ProductId == 1).Select(s => 1).Any())
                        .Select(s => new
                        {
                            s.ProductId,
                            s.ProductCode
                        }).ToList();
    
  • 查詢繫結欄位(注意 欄位必須是公開的,否則繫結外部無法存取,沒有意義)

                    //當某些欄位需要參與計算並且不返回前端時推薦用欄位繫結,無需從A實體轉換到B實體,強烈推薦此方式
    				var data = db.Query<Product>().Select(s => new Product()
                    {
                        _xx = s.ProductName
                    }).First();
    
八、Lambda表示式
  • 動態表示式 名稱空間 Fast.Framework.Utils

                    var ex = DynamicWhereExp.Create<Product>().AndIF(1 == 1, a => a.DeleteMark == true).Build();
                    var data = db.Query<Product>().Where(ex).ToList();
    
  • Sql函數 自定義函數 需引入名稱空間 Fast.Framework.Utils 使用SqlFunc類

    • SqlServer

      • 型別轉換

        方法名稱 解析範例值 說明 自定義函數
        ToString CONVERT( VARCHAR(255),123) 轉換 VARCHAR
        ToDateTime CONVERT( DATETIME,‘2022-09-16’) 轉換 DATETIME
        ToDecimal CONVERT( DECIMAL(10,6),‘123’) 轉換 DECIMAL
        ToDouble CONVERT( NUMERIC(10,6),‘123’) 轉換 NUMERIC
        ToSingle CONVERT( FLOAT,‘123’) 轉換 FLOAT
        ToInt32 CONVERT( INT,‘123’) 轉換 INT
        ToInt64 CONVERT( BIGINT,‘123’) 轉換 BIGINT
        ToBoolean CONVERT( BIT,‘1’) 轉換 BIT
        ToChar CONVERT( CHAR(2),'x') 轉換 CHAR
      • 聚合函數

        方法名稱 解析範例值 說明 自定義函數
        Max MAX( a.[xx] ) 最大值
        Min MIN( a.[xx] ) 最小值
        Count COUNT( a.[xx] ) 計數
        Sum SUM( a.[xx] ) 合計
        Avg AVG( a.[xx] ) 平均
      • 數學函數

        方法名稱 解析範例值 說明 自定義函數
        Abs ABS( a.[xx] ) 絕對值
        Round ROUND( a.[xx] ,2 ) 四捨五入
      • 字串函數

        方法名稱 解析範例值 說明 自定義函數
        StartsWith LIKE 'xx'+‘%’ 左模糊
        EndsWith LIKE ‘%’+'xx' 右模糊
        Contains LIKE ‘%’+'xx'+‘%’ 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3) 擷取
        Replace REPLACE( 'xxx','x','y') 替換
        Len LEN( 'xxx' ) 長度
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大寫
        ToLower LOWER( 'xx' ) 小寫
        Concat CONCAT(a.[xx1],a.[xx2]) 字串拼接
        Operation [CreateTime] >= @Now_1 日期、數值、字串範圍比較
      • 日期函數

        方法名稱 解析範例值 說明 自定義函數
        DateDiff DATEDIFF( DAY ,a.[xx],b.[xx]) 日期相差
        AddYears DATEADD( YEAR,a.[xx],1 ) 新增年份
        AddMonths DATEADD( MONTH,a.[xx],1 ) 新增月份
        AddDays DATEADD( DAY,a.[xx],1 ) 新增天數
        AddHours DATEADD( HOUR,a.[xx],1 ) 新增時
        AddMinutes DATEADD( MINUTE,a.[xx],1 ) 新增分
        AddSeconds DATEADD( SECOND,a.[xx],1 ) 新增秒
        AddMilliseconds DATEADD( MILLISECOND,a.[xx],1 ) 新增毫秒
        Year YEAR( a.[xx] ) 獲取年份
        Month MONTH( a.[xx] ) 獲取月份
        Day DAY( a.[xx] ) 獲取天數
      • 其它函數

        方法名稱 解析範例值 說明 自定義函數
        NewGuid NEWID() 獲取GUID
        Equals p.[ProductCode] = '123' 比較
        IsNull ISNULL(a.[xx],0) 是否為空
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • MySql

      • 型別轉換

        方法名稱 解析範例值 說明 自定義函數
        ToString CAST( a.`xx` AS CHAR(510) ) 轉換 CHAR(510)
        ToDateTime CAST( a.`xx` AS DATETIME ) 轉換 DATETIME
        ToDecimal CAST( a.`xx` AS DECIMAL(10,6) ) 轉換 DECIMAL(10,6)
        ToDouble CAST( a.`xx` AS DECIMAL(10,6) ) 轉換 DECIMAL(10,6)
        ToInt32 CAST( a.`xx` AS DECIMAL(10) ) 轉換 DECIMAL(10)
        ToInt64 CAST( a.`xx` AS DECIMAL(19) ) 轉換 DECIMAL(19)
        ToBoolean CAST( a.`xx` AS UNSIGNED ) 轉換 UNSIGNED
        ToChar CAST( a.`xx` AS CHAR(2) ) 轉換 CHAR(2)
      • 聚合函數

        方法名稱 解析範例值 說明 自定義函數
        Max MAX( a.`xx` ) 最大值
        Min MIN( a.`xx` ) 最小值
        Count COUNT( a.`xx` ) 計數
        Sum SUM( a.`xx` ) 合計
        Avg AVG( a.`xx` ) 平均
      • 數學函數

        方法名稱 解析範例值 說明 自定義函數
        Abs ABS( a.`xx` ) 絕對值
        Round ROUND( a.`xx` ,2 ) 四捨五入
      • 字串函數

        方法名稱 解析範例值 說明 自定義函數
        StartsWith LIKE CONCAT( 'xx','%' ) 左模糊
        EndsWith LIKE CONCAT( '%','xx' ) 右模糊
        Contains LIKE CONCAT( '%','xx','%' ) 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3 ) 擷取
        Replace REPLACE( 'xxx','x','y' ) 替換
        Len LEN( 'xxx' ) 長度
        Trim TRIM( ' xx ' ) 修剪空格
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大寫
        ToLower LOWER( 'xx' ) 小寫
        Concat CONCAT(a.`xx1`,a.`xx2`) 字串拼接
        Operation `CreateTime` >= @Now_1 日期、數值、字串範圍比較
      • 日期函數

        方法名稱 解析範例值 說明 自定義函數
        DateDiff DATEDIFF( a.`xx`,b.`xx` ) 日期相差 返回相差天數
        TimestampDiff TIMESTAMPDIFF( DAY,a.`xx`,b.`xx` ) 日期相差 指定時間單位
        AddYears DATE_ADD( a.`xx`,INTERVAL 1 YEAR ) 新增年份
        AddMonths DATE_ADD( a.`xx`,INTERVAL 1 MONTH ) 新增月份
        AddDays DATE_ADD( a.`xx`,INTERVAL 1 DAY ) 新增天數
        AddHours DATE_ADD( a.`xx`,INTERVAL 1 HOUR ) 新增時
        AddMinutes DATE_ADD( a.`xx`,INTERVAL 1 MINUTE ) 新增分
        AddSeconds DATE_ADD( a.`xx`,INTERVAL 1 SECOND ) 新增秒
        AddMilliseconds DATE_ADD( a.`xx`,INTERVAL 1 MINUTE_SECOND ) 新增毫秒
        Year YEAR( a.`xx` ) 獲取年份
        Month MONTH( a.`xx` ) 獲取月份
        Day DAY( a.`xx` ) 獲取天數
      • 其它函數

        方法名稱 解析範例值 說明 自定義函數
        NewGuid UUID() 獲取GUID
        Equals p.`ProductCode` = '123' 比較
        IfNull IFNULL( a.`xx`,0 ) 如果為空
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • Oracle

      • 型別轉換

        方法名稱 解析範例值 說明 自定義函數
        ToString CAST( a."xx" AS VARCHAR(255) ) 轉換 VARCHAR
        ToDateTime TO_TIMESTAMP( a."xx" ,'yyyy-MM-dd hh:mi:ss.ff') 轉換 DATETIME
        ToDecimal CAST( a."xx" AS DECIMAL(10,6) ) 轉換 DECIMAL
        ToDouble CAST( a."xx" AS NUMBER ) 轉換 NUMBER
        ToSingle CAST( a."xx" AS FLOAT ) 轉換 FLOAT
        ToInt32 CAST( a."xx" AS INT ) 轉換 INT
        ToInt64 CAST( a."xx" AS NUMBER ) 轉換 NUMBER
        ToBoolean CAST( a."xx" AS CHAR(1) ) 轉換 CHAR
        ToChar CAST( a."xx" AS CHAR(2) ) 轉換 CHAR
      • 聚合函數

        方法名稱 解析範例值 說明 自定義函數
        Max MAX( a."xx" ) 最大值
        Min MIN( a."xx" ) 最小值
        Count COUNT( a."xx" ) 計數
        Sum SUM( a."xx" ) 合計
        Avg AVG( a."xx" ) 平均
      • 數學函數

        方法名稱 解析範例值 說明 自定義函數
        Abs ABS( a."xx" ) 絕對值
        Round ROUND( a."xx" ,2 ) 四捨五入
      • 字串函數

        方法名稱 解析範例值 說明 自定義函數
        StartsWith LIKE CONCAT( 'xx','%' ) 左模糊
        EndsWith LIKE CONCAT( '%','xx' ) 右模糊
        Contains LIKE CONCAT( '%','xx','%' ) 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3) 擷取
        Replace REPLACE( 'xxx','x','y') 替換
        Length LENGTH( 'xxx' ) 長度
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大寫
        ToLower LOWER( 'xx' ) 小寫
        Concat CONCAT(a."xx1",a."xx2") 字串拼接
        Operation 」CreateTime「 >= @Now_1 日期、數值、字串範圍比較
      • 日期函數

        方法名稱 解析範例值 說明 自定義函數
        Year EXTRACT( YEAR FROM a."xx" ) 獲取年份
        Month EXTRACT( MONTH FROM a."xx" ) 獲取月份
        Day EXTRACT( DAY FROM a."xx" ) 獲取天數
      • 其它函數

        方法名稱 解析範例值 說明 自定義函數
        Equals p."ProductCode" = '123' 比較
        Nvl NVL( a."xx",0 ) 空,預設
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • PostgreSql

      • 型別轉換

        方法名稱 解析範例值 說明 自定義函數
        ToString a."xx"::VARCHAR(255) 轉換 VARCHAR
        ToDateTime a."xx"::TIMESTAMP 轉換 TIMESTAMP
        ToDecimal a."xx"::DECIMAL(10,6) 轉換 DECIMAL
        ToDouble a."xx"::NUMERIC(10,6) 轉換 NUMERIC
        ToSingle a."xx"::REAL 轉換 REAL
        ToInt32 a."xx"::INTEGER 轉換 INT
        ToInt64 a."xx"::BIGINT 轉換 BIGINT
        ToBoolean a."xx"::BOOLEAN 轉換 BOOLEAN
        ToChar a."xx"::CHAR(2) 轉換 CHAR
      • 聚合函數

        方法名稱 解析範例值 說明 自定義函數
        Max MAX( a."xx" ) 最大值
        Min MIN( a."xx" ) 最小值
        Count COUNT( a."xx" ) 計數
        Sum SUM( a."xx" ) 合計
        Avg AVG( a."xx" ) 平均
      • 數學函數

        方法名稱 解析範例值 說明 自定義函數
        Abs ABS( a."xx" ) 絕對值
        Round ROUND( a."xx" ,2 ) 四捨五入
      • 字串函數

        方法名稱 解析範例值 說明 自定義函數
        StartsWith LIKE CONCAT( '%','xx' ) 左模糊
        EndsWith LIKE CONCAT( 'xx','%' ) 右模糊
        Contains LIKE CONCAT( '%','xx','%' ) 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3 ) 擷取
        Replace REPLACE( 'xxx','x','y' ) 替換
        Length LENGTH( 'xxx' ) 長度
        Trim TRIM( ' xx ' ) 修剪空格
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大寫
        ToLower LOWER( 'xx' ) 小寫
        Concat CONCAT(a."xx1",a."xx2") 字串拼接
        Operation 」CreateTime「 >= @Now_1 日期、數值、字串範圍比較
      • 日期函數

        方法名稱 解析範例值 說明 自定義函數
        AddYears a."xx" + INTERVAL '1 YEAR' 新增年份
        AddMonths a."xx" + INTERVAL '1 MONTH' 新增月份
        AddDays a."xx" + INTERVAL '1 DAY' 新增天數
        AddHours a."xx" + INTERVAL '1 HOUR' 新增時
        AddMinutes a."xx" + INTERVAL '1 MINUTE' 新增分
        AddSeconds a."xx" + INTERVAL '1 SECOND' 新增秒
        AddMilliseconds a."xx" + INTERVAL '1 MINUTE_SECOND' 新增毫秒
        Year YEAR( a."xx" ) 獲取年份
        Month MONTH( a."xx" ) 獲取月份
        Day DAY( a."xx" ) 獲取天數
      • 查詢函數

        方法名稱 解析範例值 說明 自定義函數
        In IN ( a."xx" ,'x1','x2','x3' ) In查詢
        NotIn NOT IN ( a."xx" ,'x1','x2','x3' ) Not In查詢
      • 其它函數

        方法名稱 解析範例值 說明 自定義函數
        Equals p.」ProductCode「 = '123' 比較
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
    • Sqlite

      • 型別轉換

        方法名稱 解析範例值 說明 自定義函數
        ToString CAST(a.[xx] AS TEXT ) 轉換 TEXT
        ToDateTime DATETIME( a.[xx] ) 轉換 DateTime
        ToDecimal CAST(a.[xx] AS DECIMAL(10,6) ) 轉換 DECIMAL
        ToDouble CAST(a.[xx] AS NUMERIC(10,6) ) 轉換 NUMERIC
        ToSingle CAST(a.[xx] AS FLOAT ) 轉換 FLOAT
        ToInt32 CAST(a.[xx] AS INTEGER ) 轉換 INTEGER
        ToInt64 CAST(a.[xx] AS BIGINT ) 轉換 BIGINT
        ToBoolean CAST(a.[xx] AS CHAR(1) ) 轉換 CHAR
        ToChar CAST(a.[xx] AS CHAR(2) ) 轉換 CHAR
      • 聚合函數

        方法名稱 解析範例值 說明 自定義函數
        Max MAX( a.[xx] ) 最大值
        Min MIN( a.[xx] ) 最小值
        Count COUNT( a.[xx] ) 計數
        Sum SUM( a.[xx] ) 合計
        Avg AVG( a.[xx] ) 平均
      • 數學函數

        方法名稱 解析範例值 說明 自定義函數
        Abs ABS( a.[xx] ) 絕對值
        Round ROUND( a.[xx] ,2 ) 四捨五入
      • 字串函數

        方法名稱 解析範例值 說明 自定義函數
        StartsWith LIKE 'xx'||'%' 左模糊
        EndsWith LIKE '%'||'xx'
        Contains LIKE '%'||'xx'||'%' 全模糊
        SubString SUBSTRING( 'xxxxxx' ,1,3 ) 擷取
        Replace REPLACE( 'xxx','x','y' ) 替換
        Length LENGTH( 'xxx' ) 長度
        Trim TRIM( ' xx ' ) 修剪空格
        TrimStart LTRIM( ' xx ' ) 修剪起始空格
        TrimEnd RTRIM( ' xx ' ) 修剪末尾空格
        ToUpper UPPER( 'xx' ) 大寫
        ToLower LOWER( 'xx' ) 小寫
        Operation [CreateTime] >= @Now_1 日期、數值、字串範圍比較
      • 日期函數

        方法名稱 解析範例值 說明 自定義函數
        AddYears DATETIME( a.[xx],'1 YEAR' ) 新增年份
        AddMonths DATETIME( a.[xx],'1 MONTH' ) 新增月份
        AddDays DATETIME( a.[xx],'1 DAY' ) 新增天數
        AddHours DATETIME( a.[xx],'1 HOUR' ) 新增時
        AddMinutes DATETIME( a.[xx],'1 MINUTE' ) 新增分
        AddSeconds DATETIME( a.[xx],'1 SECOND' ) 新增秒
        AddMilliseconds DATETIME( a.[xx],'1 YEAR' ) 新增毫秒
        Year STRFTIME( '%Y', a.[xx] ) 獲取年份
        Month STRFTIME( '%m', a.[xx] ) 獲取月份
        Day STRFTIME( '%j', a.[xx] ) 獲取天數
      • 其它函數

        方法名稱 解析範例值 說明 自定義函數
        Equals p.」ProductCode「 = '123' 比較
        Case CASE case
        When WHEN when
        Then THEN then
        Else ELSE else
        End END end
  • 新增自定義函數解析

                    //注意:只能擴充套件未實現的方法名稱 不能覆蓋原有的實現
                    Models.DbType.MySQL.AddSqlFunc("方法名稱", (visit, method, sqlStack) =>
                    {
                        //解析邏輯
                    });
    
九、資料庫紀錄檔
				        db.Aop.DbLog = (sql, dp) =>
                {
                    Console.WriteLine($"執行Sql:{sql}");
                    if (dp != null)
                    {
                        foreach (var item in dp)
                        {
                            Console.WriteLine($"引數名稱:{item.ParameterName} 引數值:{item.ParameterValue}");
                        }
                    }
                };
十、事務
  • 普通事務

                  try
                  {
                      db.Ado.BeginTran();//開啟事務
    
                      // 執行 CRUD
    
                      db.Ado.CommitTran();//提交事務
                  }
                  catch (Exception ex)
                  {
                      Console.WriteLine(ex.Message);
                      db.Ado.RollbackTran();//回滾事務
                  }
    
  • 更大範圍的事務

                    try
                    {
                        db.BeginTran();//開啟事務
    
                      	// 執行 CRUD
    
                        db.CommitTran();//提交事務
                    }
                    catch (Exception ex)
                    {
                        db.RollbackTran();//回滾事務
                        Console.WriteLine(ex.Message);
                    }
    
十一、多租戶
  • 改變資料庫

                    //資料庫設定可從Json組態檔載入
    				IDbContext db = new DbContext(new List<DbOptions>() {
                    new DbOptions()
                    {
                        DbId = "1",
                        DbType = Models.DbType.SQLServer,
                        ConnectionStrings = "server=localhost;database=Test;user=sa;pwd=123456789;min pool size=0;max pool size=100;connect timeout=120;"
                    },
                    new DbOptions()
                    {
                        DbId = "2",
                        DbType = Models.DbType.MySQL,
                        ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=0;max pool size=100;connect timeout=120;"
                    }});
                    db.ChangeDb("2");//切換到MySQL
    
十二、原生特性支援
    /// <summary>
    /// 產品
    /// </summary>
    [Table("ProductMain")]
    public class Product
    {
        /// <summary>
        /// 產品ID
        /// </summary>
        [Key]
        [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
        public int ProductId { get; set; }

        /// <summary>
        /// 產品編號
        /// </summary>
        [Column("ProductCode")]//不標記預設取當前屬性名稱
        public string ProductCode { get; set; }

        /// <summary>
        /// 自定義1
        /// </summary>
        [NotMapped]
        public string Custom1 { get; set; }
        
        /// <summary>
        /// 自定義2
        /// </summary>
        [Column(TypeName="Json")]//型別標記為Json格式物件 匿名物件屬性以及字典值型別 IsClass&&!type.Equals(typeof(string)) 將自動序列化成Json格式
        public Category Custom2 { get; set; }
    }
十三、原生Ado
                // 原始起步
                // var conn = db.Ado.DbProviderFactory.CreateConnection();
                // var cmd = conn.CreateCommand();

                // 封裝的方法分別以Execute和Create開頭以及預處理 PrepareCommand 方法
                // 該方法可以自動幫你處理執行的預操作,主要作用是程式碼複用。

                // 當有非常複雜的查詢 ORM不能滿足需求的時候可以使用原生Ado滿足業務需求

                // 構建資料集核心擴充套件方法 分別有 FirstBuild ListBuild DictionaryBuild DictionaryListBuild
                var data = db.Ado.ExecuteReader(CommandType.Text, "select * from product", null).ListBuild<Product>();
十四、工作單元
  • 註冊資料庫上下文和工作單元服務

    var builder = WebApplication.CreateBuilder(args);
    
    var configuration = builder.Configuration;
    
    // 新增資料庫上下文服務
    builder.Services.AddFastDbContext();
    // 新增工作單元服務
    builder.Services.AddUnitOfWork();
    
    // 載入資料庫設定
    builder.Services.Configure<List<DbOptions>>(configuration.GetSection("DbConfig"));
    
  • 實際應用

            /// <summary>
            /// 工作單元
            /// </summary>
            private readonly IUnitOfWork unitOfWork;
    
    
            /// <summary>
            /// 構造方法
            /// </summary>
            /// <param name="unitOfWork">工作單元</param>
            public UnitOfWorkTestService(IUnitOfWork unitOfWork)
            {
                this.unitOfWork = unitOfWork;
            }
    
            /// <summary>
            /// 測試
            /// </summary>
            /// <returns></returns>
            public string Test()
            {
                //unitOfWork 物件無需顯示使用using
                var result1 = unitOfWork.Db.Insert(new Category()
                {
                    CategoryName = "類別3"
                }).Exceute();
    
                var result2 = unitOfWork.Db.Insert(new Product()
                {
                    ProductCode = "測試工作單元",
                }).Exceute();
    
                unitOfWork.Commit();
    
                return "工作單元執行完成...";
            }
    
十五、巨量資料匯入
  • 批覆制 僅支援SqlServer Oracle MySql PostgreSql

                    var list = new List<Product>();
                    for (int j = 1; j <= 100000; j++)
                    {
                        list.Add(new Product()
                        {
                            CategoryId = 1,
                            ProductCode = $"測試編號_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            ProductName = $"測試名稱_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            CreateTime = DateTime.Now,
                            Custom1 = $"測試自定義1_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom2 = $"測試自定義2_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom3 = $"測試自定義3_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom4 = $"測試自定義4_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom5 = $"測試自定義5_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom6 = $"測試自定義6_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom7 = $"測試自定義7_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom8 = $"測試自定義8_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom9 = $"測試自定義9_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom10 = $"測試自定義10_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom11 = $"測試自定義11_{Timestamp.CurrentTimestampSeconds()}_{j}",
                            Custom12 = $"測試自定義12_{Timestamp.CurrentTimestampSeconds()}_{j}",
                        });
                    }
                    db.Fast<Product>().BulkCopy(list);