Fast Framework
作者 Mr-zhong
開源專案地址 https://github.com/China-Mr-zhong/Fast.Framework
QQ交流群 954866406 歡迎小伙伴加入交流探討技術
一、前言
Fast Framework 是一個基于NET6.0 封裝的輕量級 ORM 框架 支持多種資料庫 SqlServer Oracle MySql PostgreSql Sqlite
優點: 體積小、可動態切換不同實作類別庫、原生支持微軟特性、流暢API、使用簡單、性能高、模型資料系結采用 委托+快取、強大的運算式決議、子查詢的原生支持、復雜運算式含成員變數決議,決議性能是目前常見框架中 No1 主要是有快取的支持 、源代碼可讀性強,
缺點:目前僅支持Db Frist Code Frist 暫時不考慮 主要是需要花費大量時間和精力,
二、專案明細
| 名稱 | 說明 |
|---|---|
| Fast.Framework | 介面實作類別庫(框架核心介面實作) |
| Fast.Framework.Aop | Aop類別庫(基于微軟DispatchProxy抽象類封裝) |
| Fast.Framework.Extensions | 擴展類別庫(主要擴展框架核心方法,方便使用) |
| Fast.Framework.Interfaces | 介面類別庫(框架核心介面定義) |
| Fast.Framework.Logging | 日志類別庫(主要實作自定義檔案日志) |
| Fast.Framework.Models | 模型 框架所用到的物體類 |
| Fast.Framework.Utils | 工具類別庫 |
| Fast.Framework.Test | 控制臺終端測驗專案 |
| Fast.Framework.UnitTest | 單元測驗專案 |
| Fast.Framework.Web.Test | Web測驗專案 |
三、核心物件
- Ado 原生Ado物件
IAdo ado = new AdoProvider(new DbOptions()
{
DbId = "1",
DbType = DbType.MySQL,
ProviderName = "MySqlConnector",
FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
});
- DbContext 支持多租戶 支持切換不同Ado實作類別庫 設定 ProviderName和FactoryName 即可
IDbContext db = new DbContext(new List<DbOptions>() {
new DbOptions()
{
DbId = "1",
DbType = DbType.MySQL,
ProviderName = "MySqlConnector",
FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
}});
依賴注入
// 注冊服務
builder.Services.AddScoped<IDbContext, DbContext>();
// 資料庫選項支持Options介面注入 不是很理解的可以看代碼實作
builder.Services.Configure<List<DbOptions>>(configuration.GetSection("DbConfig"));
// 產品服務類 通過構造方法注入
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 = await db.Insert(product).ExceuteAsync();
Console.WriteLine($"物體物件插入 受影響行數 {result}");
- 物體物件插入并回傳自增ID 僅支持 SQLServer MySQL SQLite
var product = new Product()
{
ProductCode = "1001",
ProductName = "測驗產品1"
};
var result = await db.Insert(product).ExceuteReturnIdentityAsync();
Console.WriteLine($"物體物件插入 回傳自增ID {result}");
- 物體物件串列插入
var list = new List<Product>();
for (int i = 0; i < 2100; i++)
{
list.Add(new Product()
{
ProductCode = $"編號{i + 1}",
ProductName = $"名稱{i + 1}"
});
}
var result = await db.Insert(list).ExceuteAsync();
Console.WriteLine($"物體物件串列插入 受影響行數 {result}");
- 匿名物件插入
var obj = new
{
ProductCode = "1001",
ProductName = "測驗商品1"
};
//注意:需要使用As方法顯示指定表名稱
var result = await db.Insert(obj).As("product").ExceuteAsync();
Console.WriteLine($"匿名物件插入 受影響行數 {result}");
- 匿名物件串列插入
var list = new List<object>();
for (int i = 0; i < 2100; i++)
{
list.Add(new
{
ProductCode = $"編號{i + 1}",
ProductName = $"名稱{i + 1}"
});
}
//注意:需要使用As方法顯示指定表名稱
var result = await db.Insert(list).As("Product").ExceuteAsync();
Console.WriteLine($"匿名物件串列插入 受影響行數 {result}");
- 字典插入
var product = new Dictionary<string, object>()
{
{"ProductCode","1001"},
{ "ProductName","測驗商品1"}
};
//注意:需要顯示指定型別否則無法多載到正確的方法,如果沒有物體型別可用object型別并配合As方法顯示指定表名稱.
var result = await db.Insert<Product>(product).ExceuteAsync();
Console.WriteLine($"字典插入 受影響行數 {result}");
- 字典串列插入
var list = new List<Dictionary<string, object>>();
for (int i = 0; i < 2100; i++)
{
list.Add(new Dictionary<string, object>()
{
{"ProductCode","1001"},
{ "ProductName","測驗商品1"}
});
}
//注意:需要顯示指定泛型型別否則無法多載到正確的方法,如果沒有物體可用object型別并配合As方法顯示指定表名稱.
var result = await db.Insert<Product>(list).ExceuteAsync();
Console.WriteLine($"字典串列插入 受影響行數 {result}");
五、洗掉
- 物體物件洗掉
var product = new Product()
{
ProductId = 1,
ProductCode = "1001",
ProductName = "測驗商品1"
};
//注意:必須標記KeyAuttribute特性 否則將拋出例外
var result = await db.Delete(product).ExceuteAsync();
Console.WriteLine($"物體洗掉 受影響行數 {result}");
- 無條件洗掉
var result = await db.Delete<Product>().ExceuteAsync();
Console.WriteLine($"無條件洗掉 受影響行數 {result}");
- 運算式洗掉
var result = await db.Delete<Product>().Where(w => w.ProductId == 1).ExceuteAsync();
Console.WriteLine($"條件洗掉 受影響行數 {result}");
- 特殊洗掉
//特殊用法 如需單個條件或多個可搭配 WhereColumn或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 = await db.Update(product).ExceuteAsync();
Console.WriteLine($"物件更新 受影響行數 {result}");
- 指定列更新
var result = await db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" })
.Columns("ProductCode", "ProductName").ExceuteAsync();
// 欄位很多的話可以直接new List<string>(){"列1","列2"}
- 忽略列更新
var result = await db.Update<Product>(new Product() { ProductCode = "1001", ProductName = "1002" })
.IgnoreColumns("Custom1").ExceuteAsync();
// 同上使用方法一樣
- 物體物件串列更新
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 = await db.Update(list).ExceuteAsync();
Console.WriteLine($"物件串列更新 受影響行數 {result}");
- 匿名物件更新
var obj = new
{
ProductId = 1,
ProductCode = "1001",
ProductName = "測驗商品1"
};
//注意:需要顯示指定表名稱 以及更新條件 使用 Where或者WhereColumns方法均可
var result = await db.Update(obj).As("product").WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"匿名物件更新 受影響行數 {result}");
- 匿名物件串列更新
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 = await db.Update(list).As("Product").WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"匿名物件串列更新 受影響行數 {result}");
- 字典更新
var product = new Dictionary<string, object>()
{
{ "ProductId",1},
{"ProductCode","1001"},
{ "ProductName","測驗商品1"}
};
//注意:需要顯示指定泛型型別否則無法多載到正確的方法并且使用WhereColumns方法指定條件列
var result = await db.Update<Product>(product).WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"字典更新 受影響行數 {result}");
- 字典串列更新
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}"}
});
}
//注意:需要顯示指定泛型型別否則無法多載到正確的方法并且使用WhereColumns方法執行條件列
var result = await db.Update<Product>(list).WhereColumns("ProductId").ExceuteAsync();
Console.WriteLine($"字典串列更新 受影響行數 {result}");
- 運算式更新
var product = new Product()
{
ProductId = 1,
ProductCode = "1001",
ProductName = "測驗商品1"
};
var result = await db.Update(product).Where(p => p.ProductId == 100).ExceuteAsync();
Console.WriteLine($"運算式更新 受影響行數 {result}");
七、查詢
- 單一查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().FristAsync();
- 串列查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().ToListAsync();
- 回傳單個字典
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().ToDictionaryAsync();
- 回傳字典串列
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().ToDictionaryListAsync();
- 分頁查詢
var page = new Pagination() { Page = 1, PageSize = 100 };
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().ToPageListAsync(page);
- 計數查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().CountAsync();
- 任何查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().AnyAsync();
- 條件查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().Where(w => w.ProductId == 1);
//需要呼叫回傳資料結果的方法 例如:ToListAsync
- Like 查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().Where(w => w.ProductName.StartsWith("左模糊") || w.ProductName.EndsWith("右模糊") || w.ProductName.Contains("全模糊"));
- Not Like查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().Where(w => !w.ProductName.StartsWith("左模糊") || !w.ProductName.EndsWith("右模糊") || !w.ProductName.Contains("全模糊"));
//由于沒有專門去擴展 Not Like 方法,可以用取反或使用比較變通實作 例如 w.ProductName.StartsWith("左模糊")==false
//Mysql舉例 最終決議后的結果為 `ProductName` Like '%左模糊' = 0 這種用法資料庫是支持的 相當于 Not Like
- Select查詢 (選擇欄位)
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().Select(s => new
{
s.ProductId,
s.ProductName
}).ToListAsync();
- 分組查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().GroupBy(s => new
{
s.ProductId,
s.ProductName
}).ToListAsync();
- 分組聚合查詢
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)//支持嵌套
}).ToListAsync();
- 排序查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().OrderBy(s => new
{
s.CreateTime
}).ToListAsync();
//這是多個欄位排序使用方法 還有其它多載方法
- Having查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().GroupBy(s => new
{
s.ProductId,
s.ProductName
}).Having(s => SqlFunc.Count(s.ProductId) > 1).ToListAsync();
//必須先使用GroupBy方法 懂得都懂
- 聯表查詢
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().
LeftJoin((a, b) => a.ProductId == b.ProductId).ToListAsync();
// 右連接對應的是 RightJoin 內連接對應 InnerJoin
- 聯合查詢
var query1 = db.Query<Product>();
var query2 = db.Query<Product>();
db.Union(query1, query2);//聯合
db.UnionAll(query1, query2);//全聯合
//執行查詢呼叫Toxx方法
- 查詢并插入 僅支持同實體的資料庫 跨庫 個人還是建議 用事務分開寫查詢和插入
//方式1
var result1 = await db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
{
s.ProductCode,
s.ProductName
}).Insert<Product>(p => new
{
p.ProductCode,
p.ProductName
});
//方式2 需要注意的是 顯示指定不帶 列識別符號 例如 `列名稱1` 如有欄位沖突 可自行加上識別符號
var result2 = await db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
{
s.ProductCode,
s.ProductName
}).Insert("表名稱 同實體不同庫 可以使用 db.資料庫名稱.表名稱 ", "列名稱1", "列名稱2", "`帶標識的列名稱3`");
//方式3 需要注意同方式2 一樣
var result3 = await db.Query<Product>().Where(w => w.ProductId == 1489087).Select(s => new
{
s.ProductCode,
s.ProductName
}).Insert("表名稱 同實體不同庫 可以使用 db.資料庫名稱.表名稱 ", new List<string>() { "列名稱1" });
- In查詢
// 方式1
var data1 = await db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, "1001", "1002")).ToListAsync();
// 方式2
var data2 = await db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, new List<string>() { "123", "456" })).ToListAsync();
// 方式3 需要動態更新IN值 使用這種
var list = new List<string>() { "123", "456" };
var data3 = await db.Query<Product>().Where(w => SqlFunc.In(w.ProductCode, list)).ToListAsync();
// 方法4 引數同上一樣 單獨分離IN和NotIN 是為了兼容匿名查詢
var data4 = await db.Query<Product>().In("欄位名稱", "1001", "1002").ToListAsync();
- 子查詢
var subQuery = db.Query<Product>().Where(w => w.ProductId == 1).Select(s => s.ProductName);
var sql1 = db.Query<Product>().Select(s => new Product()
{
Custom1 = db.SubQuery<string>(subQuery)// SubQuery 的泛型是根據你左邊賦值的屬性型別來定義
}).ToListAsync();
// 這種沒有使用new 的 泛型可隨意定義 實際作用就是避免 物件屬性賦值型別沖突的問題
var sql2 = db.Query<Product>().Select(s => db.SubQuery<string>(subQuery)).ToListAsync();
八、Lambda運算式
- 高性能運算式動態快取的支持
var list = new List<string>() { "1001" };
Expression<Func<Product, bool>> ex = p => SqlFunc.In(p.ProductCode, list);
for (int i = 1; i <= 3; i++)
{
list.Add($"動態添加引數{i}");
var stopwatch1 = new Stopwatch();
stopwatch1.Start();
var result = ex.ResolveSql(new ResolveSqlOptions()
{
DbType = Models.DbType.MySQL,
ResolveSqlType = ResolveSqlType.Where
});
stopwatch1.Stop();
Console.WriteLine($"決議耗時:{stopwatch1.ElapsedMilliseconds}ms {stopwatch1.ElapsedMilliseconds / 1000.00}s 決議Sql字串:{result.SqlString}");
}
- 決議結果
決議耗時:14ms 0.014s 決議Sql字串:p.`ProductCode` IN ( @2dac7a1c4aa64036aeee858b86fbd3a4_0,@2dac7a1c4aa64036aeee858b86fbd3a4_1 )
決議耗時:0ms 0s 決議Sql字串:p.`ProductCode` IN ( @3b6b8fcb2f674cf490d44f97525c3c2b_0,@3b6b8fcb2f674cf490d44f97525c3c2b_1,@3b6b8fcb2f674cf490d44f97525c3c2b_2 )
決議耗時:0ms 0s 決議Sql字串:p.`ProductCode` IN ( @4447c5d65e8a49c9b04549b7aac868b2_0,@4447c5d65e8a49c9b04549b7aac868b2_1,@4447c5d65e8a49c9b04549b7aac868b2_2,@4447c5d65e8a49c9b04549b7aac868b2_3 )
- 動態運算式
var ex = DynamicWhereExp.Create<Product>().AndIF(1 == 1, a => a.DeleteMark == true).Build();
var data =https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Query().Where(ex).ToListAsync();
九、資料庫日志
db.Aop.DbLog = (sql, dp) =>
{
Console.WriteLine($"執行Sql:{sql}");
if (dp != null)
{
foreach (var item in dp)
{
Console.WriteLine($"引數名稱:{item.ParameterName} 引數值:{item.Value}");
}
}
};
十、事務
- 普通事務
try
{
await db.Ado.BeginTranAsync();//開啟事務
// 執行 CRUD
await db.Ado.CommitTranAsync();//提交事務
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
await db.Ado.RollbackTranAsync();//回滾事務
}
- 更大范圍的事務 使用微軟 TransactionScope 物件
using (var tran = new TransactionScope(TransactionScopeAsyncFlowOption.Enabled))
{
// 執行你的增刪改查
// 可使用原生Ado或DbContext物件的CURD方法
tran.Complete();//提交事務
}
十一、多租戶
- 改變資料庫
//資料庫配置可從Json組態檔加載
IDbContext db = new DbContext(new List<DbOptions>() {
new DbOptions()
{
DbId = "0",
DbType = Models.DbType.SQLServer,
ProviderName = "System.Data.SqlClient",
FactoryName = "System.Data.SqlClient.SqlClientFactory,System.Data",
ConnectionStrings = "server=localhost;database=Test;user=sa;pwd=123456789;min pool size=3;max pool size=100;connect timeout=30;"
},
new DbOptions()
{
DbId = "1",
DbType = Models.DbType.MySQL,
ProviderName = "MySqlConnector",
FactoryName = "MySqlConnector.MySqlConnectorFactory,MySqlConnector",
ConnectionStrings = "server=localhost;database=Test;user=root;pwd=123456789;port=3306;min pool size=3;max pool size=100;connect timeout=30;"
}});
db.ChangeDb("1");//切換到MySQL
十二、原生特性支持
/// <summary>
/// 產品
/// </summary>
[Table("ProductMain")]
public class Product
{
/// <summary>
/// 產品ID
/// </summary>
[Key]
public int ProductId { get; set; }
/// <summary>
/// 產品編號
/// </summary>
[Column("ProductCode")]//不標記默認取當前屬性名稱
public string ProductCode { get; set; }
/// <summary>
/// 自定義1
/// </summary>
[NotMapped]
public string Custom1 { get; set; }
}
十三、原生Ado使用
// 原始起步
// var conn = db.Ado.DbProviderFactory.CreateConnection();
// var cmd = conn.CreateCommand();
// 封裝的方法分別以Execute和Create開頭以及預處理 PrepareCommand 方法
// 該方法可以自動幫你處理執行的預操作,主要作用是代碼復用,
// 當有非常復雜的查詢 ORM不能滿足需求的時候可以使用原生Ado滿足業務需求
// 構建資料集核心擴展方法 分別有 FristBuildAsync ListBuildAsync DictionaryBuildAsync DictionaryListBuildAsync
var data = https://www.cnblogs.com/China-Mr-zhong/archive/2022/09/11/await db.Ado.ExecuteReaderAsync(CommandType.Text,"select * from product", null).ListBuildAsync<Product>();
轉載請註明出處,本文鏈接:https://www.uj5u.com/net/506097.html
標籤:.NET技术
上一篇:ORM增刪改查并發性能測驗
