EF Core實現dynamic動態查詢和EF Core注入多個上下文實例池

EF Core實現dynamic動態查詢和EF Core注入多個上下文實例池

無論是在EF 6.x還是EF Core中對於原始查詢的API都比較雞肋

最後更新 2022/5/4 下午4:43
Jeffcky
預計閱讀 5 分鐘
分類
EF Core
標籤
.NET C# EF Core ORM

前言

很長一段時間沒有寫部落格了,今天補上一篇吧。偶爾發現不太願意寫部落格,太花時間,不過還是在堅持當中,畢竟或許寫出來的東西能幫到一些夥伴吧,接下來我們直接進入主題。無論是在 EF 6.x 還是 EF Core 中,對於原始查詢的 API 都比較雞肋,例如我們只想查詢單個值,它們是不支援的;例如我們只想查詢某些欄位,它們也不支援;太多太多不支援,唯一支援的是只能返回資料表中所有欄位,也就是類別中的所有屬性。所以大部分情況下我都是寫原生 SQL,原始查詢幾乎沒用到過。最近有位熱愛 EF 的同仁問到怎麼利用 SqlQuery 實作動態查詢,我沒有答案,根本沒想過用這個方法。私下看了一下,還是給出一些思考吧。若對您有幫助就好,沒幫助就當我補上了一篇部落格。

EF 6.x 與 EF Core 實作動態查詢

public static IEnumerable<dynamic> SqlQueryDynamic(this DbContext db, string Sql, params SqlParameter[] parameters)
{
    using (var cmd = db.Database.Connection.CreateCommand())
    {
        cmd.CommandText = Sql;

        if (cmd.Connection.State != ConnectionState.Open)
        {
            cmd.Connection.Open();
        }

        foreach (var p in parameters)
        {
            var dbParameter = cmd.CreateParameter();
            dbParameter.DbType = p.DbType;
            dbParameter.ParameterName = p.ParameterName;
            dbParameter.Value = p.Value;
            cmd.Parameters.Add(dbParameter);
        }

        using (var dataReader = cmd.ExecuteReader())
        {
            while (dataReader.Read())
            {
                var row = new ExpandoObject() as IDictionary<string, object>;
                for (var fieldCount = 0; fieldCount < dataReader.FieldCount; fieldCount++)
                {
                    row.Add(dataReader.GetName(fieldCount), dataReader[fieldCount]);
                }
                yield return row;
            }
        }
    }
}

那麼最終如上查詢後返回動態集合,我們該如何轉換為集合物件呢?我想都沒想,直接先序列化再反序列化,若您有更好的解決方案,自行實作即可。

using (var ctx = new EfDbContext())
{
    ctx.Database.Log = Console.WriteLine;

    var dynamicOrders = ctx.SqlQueryDynamic("select * from dbo.Orders");
    var ordersJson = JsonConvert.SerializeObject(dynamicOrders);
    var orders = JsonConvert.DeserializeObject<List<Order>>(ordersJson);
};

當然上述我只是簡單查詢了一個資料表,若您有多個資料表也是可行的,最後反序列化為不同的物件即可,未經測試,您可自行測試。

EF Core 使用多個上下文執行個體池

有許多人無論是在 EF 6.x 還是在 EF Core 中,一直以來都是使用一個上下文,但是不知我們是否有想過使用多個上下文呢?比如在電商專案中,對於產品相關操作我們可以使用產品上下文,對於加入購物車操作使用購物車上下文,對於訂單操作使用訂單上下文。這麼做的好處是什麼呢?我們可以將資料庫表格,也就是將實體拆分成不同的業務。至今我還沒看到有人這麼做過,如果是我的話,至少我會這麼做。

//Add DbContext
var dbConnetionString = Configuration.GetConnectionString("DbConnection");
services.AddDbContextPool<ShopCartDbContext>(options =>
{
    options.UseSqlServer(dbConnetionString);
}).AddDbContextPool<BookDbContext>(options =>
{
    options.UseSqlServer(dbConnetionString);
}).AddDbContextPool<OrderDbContext>(options =>
{
    options.UseSqlServer(dbConnetionString);
});

在 EF Core 2.0 中有了上下文執行個體池,類似於 ADO.NET 中的連線池一樣,但是這玩意你若從表面理解那就大錯特錯了。有關上下文執行個體池(從去年開始我著手寫了一本關於 EF 6.x 和 EF Core 的書籍最近會出版)實作本質,只能說它和 ADO.NET 中的連線池是不一樣的哦。那麼如上述使用多個上下文執行個體池是否就一定可行呢?不好意思,這樣設定是錯誤的。但執行應用程式你會發現拋出類似如下例外:

Exception message:
System.ArgumentException: Expression of type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[MultiContext.Contexts.BContext]' cannot be used for constructor parameter of type 'Microsoft.EntityFrameworkCore.DbContextOptions`1[MultiContext.Contexts.AContext]' Parameter name: arguments[0]
Stack trace:
...........

在此特性出來時大家都在歡呼能夠提升效能,對不起,上下文執行個體池雖然可能在一定程度上提升效能,但是我只能說只能有可能的效能改進。如果你知道或者看過 EF Core 實作上下文執行個體池的原理,就會明白其實作的本質,從而恍然大悟我所說的「可能的效能改進」是什麼意思。至於為何不能註冊多個上下文執行個體池,我也是私下寫專案遇到的,具體請參閱 github:

https://github.com/aspnet/EntityFrameworkCore/issues/9433。

總結

好了今天就到這裡,沒有過多的解釋和敘述,上來就直接進入主題。最近思想放飛中,對寫部落格慢慢失去了很大的興趣,偶爾感性中,待我滿血復活調節好心情再來和大家繼續分享技術。我一直在,一段時間沒寫部落格可能是因為累了,又或者是私下在學習 IdentityServer 或其他技術中。幹我們這行的,除非轉行,那就老老實實累積經驗和多學點技術吧。年輕不奮鬥,那什麼時候奮鬥呢?今天說了什麼?胡思亂想中,莫見怪。

你所看到的並非事物本身,而是經過詮釋後所賦予的意義。

繼續探索

延伸閱讀

更多文章