【纯技术贴】.NETStandard FreeSql v0.0.9 功能预览
年關將至,首頁技術含量文章真是越來越少,理解大家盼著放假過年,哥們我何嘗不是,先給大家拜個早年。
兄弟我從11月底發了神經,開啟了 ORM 功能庫的開發之旅,歷時兩個月編碼和文檔整理,目前預覽版本更新到 v0.0.9 仍是一個初級版本,怎奈今天把 wiki 文檔更新到一半,突然想寫一篇文章提前向大家介紹項目。
快過年還逼著您來了解新的技術,實在是抱歉了。介紹一下自己,有一些朋友可能知道我本人,我經常會在群里放開源項目,比如 dotnetGen、csredis 等,反感之余仍然希望可以真的幫助到有需要的人,CSRedisCore 正是本人維護的項目,本文介紹的不是它。
簡單點介紹,FreeSql 是一個NETStandard ORM 功能庫,采用 MIT 開源協議部署在 github。
它實現了的功能特性
- [x] CodeFirst 遷移。
- [x] DbFirst 從數據庫導入實體類,支持三種模板生成器。
- [x] 采用 ExpressionTree 高性能讀取數據。
- [x] 類型映射深入支持,比如pgsql的數組類型。
- [x] 支持豐富的表達式函數。
- [x] 支持導航屬性查詢,和延時加載。
- [x] 支持同步/異步數據庫操作方法,豐富多彩的鏈式查詢方法。
- [x] 支持事務。
- [x] 支持讀寫分離。
- [x] 支持多種數據庫,MySql/SqlServer/PostgreSQL/Oracle/Sqlite。
快速開始
var connstr = "Data Source=127.0.0.1;Port=3306;User ID=root;Password=root;" + "Initial Catalog=cccddd;Charset=utf8;SslMode=none;Max pool size=10";IFreeSql fsql = new FreeSql.FreeSqlBuilder().UseConnectionString(FreeSql.DataType.MySql, connstr).UseSlave("connectionString1", "connectionString2") //使用從數據庫,支持多個.UseMonitorCommand(cmd => Console.WriteLine(cmd.CommandText), //監聽SQL命令對象,在執行前(cmd, traceLog) => Console.WriteLine(traceLog)) //監聽SQL命令對象,在執行后.UseLogger(null) //使用日志,不指定默認輸出控制臺 ILogger.UseCache(null) //使用緩存,不指定默認使用內存 IDistributedCache.UseAutoSyncStructure(true) //自動同步實體結構到數據庫.UseSyncStructureToLower(true) //轉小寫同步結構.UseLazyLoading(true) //延時加載導航屬性對象,導航屬性需要聲明 virtual.Build();定義實體
可從現有數據庫生成實體模型,提供 IDbFirst 生成實體模型。或者手動創建模型,基于模型創建或修改數據庫,提供 ICodeFirst 同步結構的 API(甚至可以做到開發階段自動同步)。
[Table(Name = "tb_topic")] class Topic {[Column(IsIdentity = true, IsPrimary = true)]public int Id { get; set; }public int Clicks { get; set; }public int TestTypeInfoGuid { get; set; }public TestTypeInfo Type { get; set; }public string Title { get; set; }public DateTime CreateTime { get; set; } } class TestTypeInfo {public int Guid { get; set; }public int ParentId { get; set; }public TestTypeParentInfo Parent { get; set; }public string Name { get; set; } } class TestTypeParentInfo {public int Id { get; set; }public string Name { get; set; } }ISelect<Topic> select => fsql.Select<Topic>();查詢
普通查詢
var sql = select.Where(a => a.Id == 10).ToSql(); ///SELECT ... FROM `tb_topic` a WHERE (a.`Id` = 10)sql = select.Where(a => a.Id == 10 && a.Id > 10 || a.Clicks > 100).ToSql(); ///SELECT ... FROM `tb_topic` a WHERE (a.`Id` = 10 AND a.`Id` > 10 OR a.`Clicks` > 100)sql = select.Where(a => new []{1,2,3}.Contains(a.Id)).ToSql(); //SELECT ... FROM `tb_topic` a WHERE (a.`Id` in (1,2,3))每頁20條數據,查詢第1頁
var sql = select.Page(1, 20).ToSql(); ///SELECT ... FROM `tb_topic` a LIMIT 0,20利用導航屬性聯表
sql = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid).ToSql(); //SELECT a.`Id`, a.`Clicks`, a.`TestTypeInfoGuid`, a__Type.`Guid`, a__Type.`ParentId`, a__Type.`Name`, a.`Title`, a.`CreateTime` //FROM `tb_topic` a //LEFT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid`sql = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid).LeftJoin(a => a.Type.Parent.Id == a.Type.ParentId).ToSql(); //SELECT ... //FROM `tb_topic` a //LEFT JOIN `TestTypeInfo` a__Type ON a__Type.`Guid` = a.`TestTypeInfoGuid` //LEFT JOIN `TestTypeParentInfo` a__Type__Parent ON a__Type__Parent.`Id` = a__Type.`ParentId`子表 Exists 查詢
var sql2222 = select.Where(a => select.Where(b => b.Id == a.Id).Any()).ToList(); // SELECT ... FROM `xxx` a WHERE (exists(SELECT 1 FROM `xxx` b WHERE (b.`Id` = a.`Id`)))//兩級相同的子表查詢 sql2222 = select.Where(a =>select.Where(b => b.Id == a.Id && select.Where(c => c.Id == b.Id).Where(d => d.Id == a.Id).Where(e => e.Id == b.Id).Offset(a.Id).Any()).Any() ).ToList(); // SELECT ... FROM `xxx` a WHERE (exists(SELECT 1 FROM `xxx` b WHERE (b.`Id` = a.`Id` AND exists(SELECT 1 FROM `xxx` c WHERE (c.`Id` = b.`Id`) AND (c.`Id` = a.`Id`) AND (c.`Id` = b.`Id`) limit 0,1)) limit 0,1))查找今天創建的數據
select.Where(a => a.CreateTime.Date == DateTime.Now.Date).ToSql();支持功能豐富的表達式函數解析,包括(字符串、日期、時間、數學、類型轉換)等函數,方便程序員在不了解數據庫函數的情況下編寫代碼。這是非常特色的功能之一,深入細化函數解析盡量做到滿意,所支持的類型基礎都可以使用對應的表達式函數,例如 日期、字符串、IN查詢、數組(PostgreSQL的數組)、字典(PostgreSQL HStore)等等。
獲得查詢返回的數據
采用 ExpressionTree 優化讀取速讀,如果懂技術的你一定知道 .NETCore 技術下除了原生代碼,最快就是 Emit 和 ExpressionTree。項目在初期使用的反射+緩存,雖然 .NETCore 優化了反射性能,但經過與Dapper性能測試對比之后,發現仍然有一定差距,改成 ExpresstionTree 后才與 Dapper 性能相當。FreeSql 支持的類型較多,現實 ExpressionTree 的復雜度較大,有興趣的朋友可以翻閱源代碼。
返回單條記錄
Topic t1 = select.Where(a => a.Id == 1).ToOne();FreeSql有兩個約定,ToOne 永遠返回 null 或 有數據的實體對象,ToList 永遠返回非 null 的 List<實體類型>
返回 List
List<Topic> t1 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList();返回 List + 導航屬性的數據
List<Topic> t2 = select.LeftJoin(a => a.Type.Guid == a.TestTypeInfoGuid).ToList(); //此時會返回普通字段 + 導航對象 Type 的數據指定字段返回
//返回一個字段 List<int> t3 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList(a => a.Id);//返回匿名類 List<匿名類> t4 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList(a => new { a.Id, a.Title });//返回元組 List<(int, string)> t5 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList<(int, string)>("id, title");//返回SQL字段 List<匿名類> t6 = select.Where(a => a.Id > 0).Skip(100).Limit(200).ToList(a => new {a.Id,a.Title,cstitle = "substr(a.title, 0, 2)", //將 substr(a.title, 0, 2) 作為查詢字段csnow = Convert.ToDateTime("now()"), //將 now() 作為查詢字段//奇思妙想:怎么查詢開窗函數的結果});//返回更為復雜的結構 List<匿名類> t7 = select.From<TestTypeInfo, TestTypeParentInfo>((s, b, c) => s.InnerJoin(a => a.TypeGuid == b.Guid).LeftJoin(a => c.Id == b.ParentId).Where(a => b.Name != "xxx")).ToList((a, b, c) => new {a.Id,a.Title,a.Type,ccc = new { a.Id, a.Title },tp = a.Type,tp2 = new {a.Id,tp2 = a.Type.Name},tp3 = new {a.Id,tp33 = new {a.Id}}});執行SQL返回數據
class xxx {public int Id { get; set; }public string Path { get; set; }public string Title2 { get; set; } }List<xxx> t8 = fsql.Ado.Query<xxx>("select * from song"); List<(int, string ,string)> t7 = fsql.Ado.Query<(int, string, string)>("select * from song"); List<dynamic> t9 = fsql.Ado.Query<dynamic>("select * from song");CodeFirst
支持 CodeFirst 遷移結構至數據庫,這應該是(O/RM)必須標配的一個功能。
與其他(O/RM)不同FreeSql支持更多的數據庫特性,而不只是支持基礎的數據類型,這既是優點也是缺點,優點是充分利用數據庫特性輔助開發,缺點是切換數據庫變得困難。不同程序員的理念可能不太一致,作為功能庫FreeSql支持到了極致,至于是否使用是項目組技術衡量的另一個問題。
盡管多種數據庫適配邏輯非常復雜,FreeSql始終秉承優化程序開發習慣的原則盡量去現實,中間碰到了一些非技術無法攻克的難題,比如數據庫的自定義類型,和實體類本身就是一種沖突,為了減少使用成本,諸如此類的數據庫功能沒有得到支持。
除了在實體上標注特性,也支持實體以外配置
fsql.CodeFirst.ConfigEntity<TestFluenttb1>(a => {a.Name("xxdkdkdk1").SelectFilter("a.Id22 > 0");a.Property(b => b.Id).Name("Id22").IsIdentity(true);a.Property(b => b.name).DbType("varchar(100)").IsNullable(true);}).ConfigEntity<TestFluenttb2>(a => {a.Name("xxdkdkdk2").SelectFilter("a.Idx > 0");a.Property(b => b.Id).Name("Id22").IsIdentity(true);a.Property(b => b.name).DbType("varchar(100)").IsNullable(true);});類型映射
| bool | bool? | bit(1) | bit | bool | number(1) | boolean |
| sbyte | sbyte? | tinyint(3) | smallint | int2 | number(4) | smallint |
| short | short? | smallint(6) | smallint | int2 | number(6) | smallint |
| int | int? | int(11) | int | int4 | number(11) | integer |
| long | long? | bigint(20) | bigint | int8 | number(21) | integer |
| byte | byte? | tinyint(3) unsigned | tinyint | int2 | number(3) | int2 |
| ushort | ushort? | smallint(5) unsigned | int | int4 | number(5) | unsigned |
| uint | uint? | int(10) unsigned | bigint | int8 | number(10) | decimal(10,0) |
| ulong | ulong? | bigint(20) unsigned | decimal(20,0) | numeric(20,0) | number(20) | decimal(21,0) |
| double | double? | double | float | float8 | float(126) | double |
| float | float? | float | real | float4 | float(63) | float |
| decimal | decimal? | decimal(10,2) | decimal(10,2) | numeric(10,2) | number(10,2) | decimal(10,2) |
| Guid | Guid? | char(36) | uniqueidentifier | uuid | char(36 CHAR) | character(36) |
| TimeSpan | TimeSpan? | time | time | time | interval day(2) to second(6) | bigint |
| DateTime | DateTime? | datetime | datetime | timestamp | timestamp(6) | datetime |
| DateTimeOffset | DateTimeOffset? | - | - | datetimeoffset | timestamp(6) with local time zone | - |
| Enum | Enum? | enum | int | int4 | number(16) | mediumint |
| FlagsEnum | FlagsEnum? | set | bigint | int8 | number(32) | bigint |
| byte[] | varbinary(255) | varbinary(255) | bytea | blob | blob |
| string | varchar(255) | nvarchar(255) | varchar(255) | nvarchar2(255) | nvarchar(255) |
| MygisPoint | point | - | - | - | - |
| MygisLineString | linestring | - | - | - | - |
| MygisPolygon | polygon | - | - | - | - |
| MygisMultiPoint | multipoint | - | - | - | - |
| MygisMultiLineString | multilinestring | - | - | - | - |
| MygisMultiPolygon | multipolygon | - | - | - | - |
| BitArray | - | - | varbit(64) | - | - |
| NpgsqlPoint | - | - | point | - | - |
| NpgsqlLine | - | - | line | - | - |
| NpgsqlLSeg | - | - | lseg | - | - |
| NpgsqlBox | - | - | box | - | - |
| NpgsqlPath | - | - | path | - | - |
| NpgsqlPolygon | - | - | polygon | - | - |
| NpgsqlCircle | - | - | circle | - | - |
| (IPAddress Address, int Subnet) | - | - | cidr | - | - |
| IPAddress | - | - | inet | - | - |
| PhysicalAddress | - | - | macaddr | - | - |
| NpgsqlRange<int> | - | - | int4range | - | - |
| NpgsqlRange<long> | - | - | int8range | - | - |
| NpgsqlRange<decimal> | - | - | numrange | - | - |
| NpgsqlRange<DateTime> | - | - | tsrange | - | - |
| PostgisPoint | - | - | geometry | - | - |
| PostgisLineString | - | - | geometry | - | - |
| PostgisPolygon | - | - | geometry | - | - |
| PostgisMultiPoint | - | - | geometry | - | - |
| PostgisMultiLineString | - | - | geometry | - | - |
| PostgisMultiPolygon | - | - | geometry | - | - |
| PostgisGeometry | - | - | geometry | - | - |
| PostgisGeometryCollection | - | - | geometry | - | - |
| Dictionary<string, string> | - | - | hstore | - | - |
| JToken | - | - | jsonb | - | - |
| JObject | - | - | jsonb | - | - |
| JArray | - | - | jsonb | - | - |
| 數組 | - | - | 以上所有類型都支持 | - | - |
以上類型和長度是默認值,可手工設置,如 string 屬性可指定 [Column(DbType = "varchar(max)")]
DbFirst
FreeSql 同樣支持 DbFirst 的開發模式,即先有數據庫再有項目,這種傳統的開發模式永遠不會過時,特別適合老項目仍然在維護,或者數據庫重構成本過高的項目。關系型數據庫有許多成熟的運維和開發類配套工具,例如我們所關注的ER圖工具 PowerdeSigner,使用它可以更直觀的查看表之間的關系。除了FreeSql作者同時在維護超過十年的代碼生成器dotnetGen項目,它是一款支持超快速開發且高度可控的解決方案,非常喜歡 DbFirst 的開發模式,因此在 FreeSql for DbFirst 功能上會有不錯的支持。
DbFirst 模式開發主要提供了不同數據庫的表結構查詢適配,配合模板生成器現實從數據庫導入模型到c#代碼中。
生成器是基于 DbFirst 開發的輔助工具,適用老項目一鍵生成實體。生成器采用模板的方式,實現了三種生成模板:
| simple-entity | ../Templates/MySql/simple-entity | √ | X | X | √ | X | X |
| simple-entity-navigation-object | ../Templates/MySql/simple-entity-navigation-object | √ | √ | X | √ | X | X |
| rich-entity-navigation-object | ../Templates/MySql/rich-entity-navigation-object | √ | √ | √ | X | √ | X |
結語
項目功能與文檔較多,許多細節不適合在一篇文章中展示詳盡,更多的功能介紹請移步 github wiki 中心。借此文章介紹FreeSql這個國產ORM,希望能得到大家的支持。
FreeSql 口號:打造 .NETCore 最方便的 ORM!
項目地址:https://github.com/2881099/FreeSql
文檔中心:https://github.com/2881099/FreeSql/wiki
最后誠心邀請您的參與加入,一起完成 FreeSql 的使命。
轉載于:https://www.cnblogs.com/kellynic/p/10310484.html
總結
以上是生活随笔為你收集整理的【纯技术贴】.NETStandard FreeSql v0.0.9 功能预览的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 设置联想笔记本从u盘启动怎么设置 联想笔
- 下一篇: 注册阿里云域名