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還有更多細節優化並不是路人一眼能看透,如果覺得寫得不錯麻煩點個贊,這也是我繼續寫下去的源動力。
一遍看不明白的話,建議多看幾遍。謝謝觀看!