FreeSql 是 .NetFramework 4.6+、.NetCore 下的 ORM 功能庫,提供了豐富的功能,支援五種流行資料庫 MySql/SqlServer/PostgreSQL/Oracle/Sqlite。
正常的資料庫都支援跨庫,然而 Sqlite 預設不支援,或者說支援起來較為麻煩,FreeSql 最關心的是通用、易用性,本文介紹 FreeSql 如何實作 Sqlite 跨庫操作。
故事發生在 CodeFirst 自由開發
FreeSql 支援并推薦使用 CodeFirst 方式開發項目,這種開發方式非常自由,如同 FreeSql 的命名一般。
如下定義兩個實體(文章、評論):
class Topic {
public Guid Id { get; set; }
public string Title { get; set; }
public string Content { get; set; }
public DateTime CreateTime { get; set; }
}
[Table(Name = "xxxtb.Comment")]
class Comment {
public Guid Id { get; set; }
public Guid TopicId { get; set; }
public Topic Topic { get; set; }
public string Nickname { get; set; }
public string Content { get; set; }
public DateTime CreateTime { get; set; }
}
我們希望将 Topic 的資料存到主庫,Comment 的資料存到别外一個庫(xxxtb)。比如使用 mysql,不指定資料庫情況下,将操作目前資料庫下的表。
其他 ado.net 或 ORM 将面臨的問題(預設情況下):
1、驅動隻打開了一個庫,或者需要手工調用驅動的方法對其他庫進行附加;
2、項目中可能需要定義多個 orm,實作對多個資料庫的存儲和查詢;
3、無法使用跨庫聯表查詢;
解決使用者使用問題
使用習慣是 FreeSql 主要攻克的難題,其他資料庫都行,【吐槽】就你丫的 Sqlite 奇葩,同連接配接下預設做不到跨庫操作(或者說使用不友善)。
好在如今的 .NET 庫大多數都已經開源,于是翻閱 System.Data.SQLite.Core 源碼做了總結:
SQLiteConnection 連接配接對象在 Open 後,執行如下指令可附加多個資料庫;
attach database [xxxtb.db] as [xxxtb];
于是,
第一步:實作一個擴充方法,使用 OpenAndAttach 代替 Open:
public static void OpenAndAttach(this DbConnection that, string[] attach) {
that.Open();
if (attach?.Any() == true) {
var sb = new StringBuilder();
foreach(var att in attach)
sb.Append($"attach database [{att}] as [{att.Split('.').First()}];\r\n");
var cmd = that.CreateCommand();
cmd.CommandText = sb.ToString();
cmd.ExecuteNonQuery();
}
}
//異步方法的實作省略...
第二步:增加 ConnectionString 參數 Attachs
Data Source=|DataDirectory|\document.db;Attachs=xxxtb.db;Pooling=true;Max Pool Size=10
第三步:SqliteConnectionPool 實施
1、解析 Attachs;
var att = Regex.Split(_connectionString, @"Attachs\s*=\s*", RegexOptions.IgnoreCase);
if (att.Length == 2) {
//此條件說明存在,找到 Attachs 配置的值,并且它支援以逗号分割
var idx = att[1].IndexOf(';');
Attaches = (idx == -1 ? att[1] : att[1].Substring(0, idx)).Split(',');
}
2、将原有 Open 方法替換成 OpenAndAttach;
大功告成
編碼與實施過程完成,接下來測試結果,仍然使用上方給出的兩個實體類型。
static IFreeSql sqlite = new FreeSql.FreeSqlBuilder()
.UseConnectionString(FreeSql.DataType.Sqlite, @"Data Source=|DataDirectory|\document.db;Attachs=xxxtb.db;Pooling=true;Max Pool Size=10")
.UseAutoSyncStructure(true)
.UseLazyLoading(true)
.Build();
//秀一波 FreeSql.Repository 擴充包
//安裝方法:dotnet add package FreeSql.Repository
var topicRepository = sqlite.GetGuidRepository<Topic>();
var commentRepository = sqlite.GetGuidRepository<Comment>();
//添加測試文章
Guid topicId = FreeUtil.NewMongodbId();
topicRepository.Insert(new Topic {
Id = FreeUtil.NewMongodbId(),
Title = "文章标題1",
Content = "文章内容1",
CreateTime = DateTime.Now
});
//添加10條測試評論
var comments = Enumerable.Range(0, 10).Select(a => new Comment {
Id = FreeUtil.NewMongodbId(),
TopicId = topicId,
Nickname = $"昵稱{a}",
Content = $"評論内容{a}",
CreateTime = DateTime.Now
});
var affrows = commentRepository.Insert(comments);
var find = commentRepository.Select.Where(a => a.Topic.Title == "文章标題1").ToList();
//SELECT a."Id", a."TopicId", a."Nickname", a."Content", a."CreateTime"
//FROM "xxxtb"."Comment" a, "Topic" a__Topic
//WHERE (a__Topic."Title" = '文章标題1')
//find 查詢出了10條資料
然後我們使用 navicat 附加兩個資料庫檢視:

其他說明
1、FreeUtil.NewMongodbId 是生成有序的 Guid 值,在 FreeSql 中實作,其實可以使用 Guid.NewGuid,這裡我承認有秀的嫌疑;
2、文章中的代碼沒有過多的依賴,在 vs2017+.netcore2.2 測試一次運作通過,sqlite 的優勢免安裝服務;
3、FreeSql 支援 CodeFirst,即建好實體運作程式,表就會建立;
4、FreeSql.Repository 是擴充包,實作通用 CURD 倉儲層功能,文檔:https://github.com/2881099/FreeSql/wiki/Repository
這個功能其實在 FreeSql 早期就已經實作了,但是一直沒能有時間将過程經驗整理成文章,今天把文章寫完寫全,希望能給大家帶來一點“驚吓”。FreeSql還有更多細節優化并不是路人一眼能看透,如果覺得寫得不錯麻煩點個贊,這也是我繼續寫下去的源動力。
一遍看不明白的話,建議多看幾遍。謝謝觀看!