【純技術貼】.NETStandard FreeSql v0.0.9 功能預覽

年關將至,首頁技術含量文章真是愈來愈少,理解你們盼着放假過年,哥們我未嘗不是,先給你們拜個早年。git

兄弟我從11月底發了神經,開啓了 ORM 功能庫的開發之旅,歷時兩個月編碼和文檔整理,目前預覽版本更新到 v0.0.9 還是一個初級版本,怎奈今天把 wiki 文檔更新到一半,忽然想寫一篇文章提早向你們介紹項目。程序員

快過年還逼着您來了解新的技術,實在是抱歉了。介紹一下本身,有一些朋友可能知道我本人,我常常會在羣裏放開源項目,好比 dotnetGen、csredis 等,反感之餘仍然但願能夠真的幫助到有須要的人,CSRedisCore 正是本人維護的項目,本文介紹的不是它。github

簡單點介紹,FreeSql 是一個NETStandard ORM 功能庫,採用 MIT 開源協議部署在 github。redis

它實現了的功能特性

  • [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(甚至能夠作到開發階段自動同步)。sql

[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 的複雜度較大,有興趣的朋友能夠翻閱源代碼。json

返回單條記錄

Topic t1 = select.Where(a => a.Id == 1).ToOne();

FreeSql有兩個約定,ToOne 永遠返回 null 或 有數據的實體對象,ToList 永遠返回非 null 的 List<實體類型>c#

返回 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);
    });

類型映射

csharp MySql SqlServer PostgreSQL Oracle Sqlite
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 的使命。

相關文章
相關標籤/搜索