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 或者其他技術中,干咱這行的,除非轉行那就老老實實積累經驗和多學點技術吧,年輕不奮鬥,那什麼時候奮鬥呢。今天說了啥,胡思亂想中,莫見怪。

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

Keep Exploring

延伸阅读

更多文章