FreeSql 如何實現 Sqlite 跨庫查詢


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

一遍看不明白的話,建議多看幾遍。謝謝觀看!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM