<output id="qn6qe"></output>

    1. <output id="qn6qe"><tt id="qn6qe"></tt></output>
    2. <strike id="qn6qe"></strike>

      亚洲 日本 欧洲 欧美 视频,日韩中文字幕有码av,一本一道av中文字幕无码,国产线播放免费人成视频播放,人妻少妇偷人无码视频,日夜啪啪一区二区三区,国产尤物精品自在拍视频首页,久热这里只有精品12

      【EF Core】通過 DbContext 選項擴展框架

      本來老周計劃在 10 月 1 日或 2 日寫這篇水文的,沒打算出去玩(確實沒啥好玩)。不過因為買的運動相機到手,急著想試試效果,于是就備了些干糧,騎著山地車在外面鬼混了一天。10 月 2 日,家里來了三位熱愛學習的小妹妹,必須傳道授業解惑。10 月 3 日去表弟家里挑一只戰斗力強的貍花貓,負責家里的治安。4、5 日清洗電風扇和一臺有霉味的圓柱空調,順便把家里的門窗都清洗一下。只好等到中秋節才來寫文章。

      EF Core 內部使用了 IoC 容器,使其支持依賴注入,理論上也很容易擴展。不過,框架有緩存自己的服務列表,咱們無法直接訪問服務容器。目前階段,EF Core 還不能傳遞咱們自己的 App Services——初始化時它會直接改為 null。

      var cacheKey = options;
      var extension = options.FindExtension<CoreOptionsExtension>();
      if (extension?.ApplicationServiceProvider != null)
      {
          cacheKey = ((DbContextOptions)options).WithExtension(extension.WithApplicationServiceProvider(null));
      }

      所以,就算你有本事往 Options 里面塞 App Services 也不起作用,人家直接給干成 null 了。微軟社區團隊表示將來會支持的。

      先不要灰心,并不是不能擴展的,還有一個擴展點可以利用—— DbContext 的選項類。

      其實,DbContext 選項類是由一組 IDbContextOptionsExtension 服務構成的。所以,咱們如果實現這個服務接口,然后放進選項類的擴展列表中,也能實現擴展 EF Core 的功能。先來認識一下,IDbContextOptionsExtension 接口規定了哪些成員。

      1、Info 屬性:返回類型為 DbContextOptionsExtensionInfo。注意各位,這是個抽象類,所以你必須實現自己的 Info,主要用于返回你正在編寫的擴展的相關信息。這個類咱們后面再討論。

      2、ApplyDefaults 方法:這個方法有個默認實現,就是 return this。其作用是根據參數傳入的 DbContextOptions(另一個選項類實例),給當前擴展設置一些默認值。這個一般用于:需要根據選項來設置某些參數值,比如,SqlServerOptionsExtension 類。

       public virtual IDbContextOptionsExtension ApplyDefaults(IDbContextOptions options)
       {
           if (ExecutionStrategyFactory == null
               && (EngineType == SqlServerEngineType.AzureSql
                   || EngineType == SqlServerEngineType.AzureSynapse
                   || UseRetryingStrategyByDefault))
           {
               return WithExecutionStrategyFactory(c => new SqlServerRetryingExecutionStrategy(c));
           }
      
           return this;
       }

      3、ApplyServices 方法:重點來了。對就是它,實現它,你就能向服務容器添加自定義的服務了。

      4、Validate 方法:驗證一下當前 DbContextOptions 的值是否符合你的要求,如果驗證不通過,直接拋異常就行了。如果不需要驗證,留空即可。

       

      現在咱們再來認識一下 DbContextOptionsExtensionInfo 抽象類。

      1、LogFragment  屬性:返回一個字符串,在記錄日志時,這個字符串會出現在日志里。至于說是什么字符串,你可自己決定。

      2、Extension 屬性:返回與當前信息類相關的 IDbContextOptionsExtension 對象。

      3、ShouldUseSameServiceProvider 方法:這個方法其實是在 DbContextOptions 類的 Equals 方法中作為判斷兩個 DbContextOptions 實例的配置是否相同的條件之一。意思就是如果結果是 true,表明所有配置相同的 DbContextOptions 不需要初始化新的服務容器。

      4、GetServiceProviderHashCode 與 PopulateDebugInfo 方法:.NET CLR 對象的相等判斷除了 Equals 方法,還有 GetHashCode 方法,看看是否返回相同的哈希。GetServiceProviderHashCode 方法你可以自定義返回的哈希值,DbContextOptions 類的 GetHashCode 方法中也調用了此方法。

      public override int GetHashCode()
      {
          var hashCode = new HashCode();
      
          foreach (var (type, value) in _extensionsMap)
          {
              hashCode.Add(type);
              hashCode.Add(value.Extension.Info.GetServiceProviderHashCode());
          }
      
          return hashCode.ToHashCode();
      }

      可見,如果 GetServiceProviderHashCode 方法返回的值改變,就會影響到 DbContextOptions 對象的相等判斷。同樣,GetServiceProviderHashCode 方法會和 PopulateDebugInfo 方法搭配用。PopulateDebugInfo 方法的參數是一個字典,開發者可以往里面設置一些自定義的 Key-Value 元素,這些元素通常表示當前擴展中被更改的值。

      PopulateDebugInfo 方法中向字典添加的值也會被傳遞給 ServiceProviderDebugInfo 事件,事件相關的數據被封裝到 ServiceProviderDebugInfoEventData  類中。

      不過,但是,當你開啟日志功能后,你會發現,ServiceProviderDebugInfo 事件根本不會輸出到日志中。Github 上有人提過這事,但沒人回答。在本文后面,老周會告訴你如何解決此問題。

      ----------------------------------------------------------------------------------------------------------------------------------------------------

      上面是對知識點的簡單理論介紹。說簡單一點,就是你想擴展 EF Core,就是實現 IDbContextOptionsExtension 接口,然后在 ApplyServices 方法中把你的服務放進容器

      理論總是抽象的,咱們動手練一練就好了。

      第一步,老規矩,隨便寫個實體,然后從 DbContext 繼承一個你的上下文。

      public class Pet
      {
          public int Id { get; set; }
          public string Name { get; set; } = "未知生物";
          public int? Age { get; set; }
      }
      
      
      public class DemoDbContext : DbContext
      {
          // 公共屬性:數據集
          public DbSet<Pet> Pets { get; set; }
      
          protected override void OnConfiguring(DbContextOptionsBuilder opBuilder)
          {
              opBuilder.UseSqlite("data source=:memory:")
                              // 開啟日志
                              .LogTo(log => Console.WriteLine(log));
          }
      }

      第二步,既然咱們要擴展,當然要寫服務類型了。

      public interface IHelloWorld
      {
          void SayHello(string? who);
      }
      
      public class DemoHelloWorld : IHelloWorld
      {
          public void SayHello(string? who)
          {
              Console.WriteLine("你好,{0}", who ?? "宇宙人");
          }
      }

      當然了,通過構造函數的依賴注入,你是可以訪問 EF Core 內部的服務的。這里為了演示的簡單,就沒有注入任何東西。

      第三步,重點來了,實現那個,那個很辣眼睛的接口。

      public class DemoDbContextOptionsExtension : IDbContextOptionsExtension
      {
          // 擴展信息
          private MyExtInfo? _info;
      
          // 這個屬性用于返回擴展信息。
          // 返回的類型是 DbContextOptionsExtensionInfo,不需要訪問 MyExtInfo 類
          public DbContextOptionsExtensionInfo Info => _info ??= new MyExtInfo(this);
      
          public void ApplyServices(IServiceCollection services)
          {
              // 這里,添加你的服務
              services.AddScoped<IHelloWorld, DemoHelloWorld>();
          }
      
          public void Validate(IDbContextOptions options)
          {
              // 不需要驗證,空方法
          }
      
          // 一個隨機整數,模擬選項改變
          public int RandValue { get; set; }
      
          // 這個類私有化就可以了,因為對外公開的是 DbContextOptionsExtensionInfo 類型
          private class MyExtInfo : DbContextOptionsExtensionInfo
          {
              // 構造函數
              public MyExtInfo(IDbContextOptionsExtension extension) : base(extension)
              {
              }
      
              // 替換一下基類成員,方便獲取
              new public DemoDbContextOptionsExtension Extension => (DemoDbContextOptionsExtension)base.Extension;
      
              // 此處要返回 false,因為咱們這個不是數據庫提供者
              public override bool IsDatabaseProvider => false;
      
              // 自定義日志輸出
              public override string LogFragment => $"這是個大擴展 - { Extension.RandValue.ToString()}";
      
              // 使用 _myRandValue 的哈希,
              public override int GetServiceProviderHashCode()
              {
                  return Extension.RandValue.GetHashCode();
              }
      
              public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
              {
                  // 設置調試信息
                  debugInfo["MyExtension:RandomValue"] = Extension.RandValue.ToString();
              }
      
              public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
              {
                  // 可以直接 retrun true
                  return other is MyExtInfo;
              }
          }
      }

      第四步,將自定義擴展添加到 DbContextOptions 的擴展集合中有點麻煩,所以一般要封裝一個擴展方法,方便調用。

      public static class DemoDbContextOptionsBuilderExtensions
      {
          public static DbContextOptionsBuilder UseDemoExt(
              this DbContextOptionsBuilder builder)
          {
              // 在添加前應當查找一下,避免重復添加
              var myext = builder.Options.FindExtension<DemoDbContextOptionsExtension>();
              // 如果沒有,就new一個
              myext ??= new DemoDbContextOptionsExtension();
              // 設置一下隨機數據,模擬配置改變
              myext.RandValue = Random.Shared.Next(100, 9999999);
              // 添加到擴展集合中(注意類型轉換)
              ((IDbContextOptionsBuilderInfrastructure)builder).AddOrUpdateExtension(myext);
      
              return builder;
          }
      }

      a、要養成先找后加的習慣,即先 Find 一下擴展是不是已在集合中,然后再調用 AddOrUpdateExtension 方法添加到擴展集合中。

      b、由于此方法是顯式實現了 IDbContextOptionsBuilderInfrastructure 接口,所以要先把 builder 轉換為 IDbContextOptionsBuilderInfrastructure 接口類型再調用 AddOrUpdateExtension 方法。

      第五步,回過頭去修改 DemoDbContext 類。

      public class DemoDbContext : DbContext
      {
          ……
      
          protected override void OnConfiguring(DbContextOptionsBuilder opBuilder)
          {
              opBuilder.UseSqlite("data source=:memory:")
                              // 開啟日志
                              .LogTo(log => Console.WriteLine(log))
                              // 使用自定義的擴展
                              .UseDemoExt();
          }
      
          // 測試服務
          public void Greeting(string who)
          {
              IHelloWorld sv = this.GetService<IHelloWorld>();
              sv.SayHello(who);
          }
      }

      第六步,實例化上下文對象,運行,實驗一下。

      static void Main(string[] args)
      {
          using var ctx = new DemoDbContext();
          ctx.Greeting("小王");
      }

       

      運行結果如下圖所示。

      image

       很顯然,ServiceProviderDebugInfo 事件沒有日志輸出的。

       

      現在,老周就說一下如何讓它輸出這個事件。

      方法:使用 .NET Logging API。比如,咱們要使日志輸出到控制臺,需要添加 Microsoft.Extensions.Logging.Console 包的引用。

      在上下文類中,定義 ILoggerFactory 類型的字段,并用 LoggerFactory.Create 方法創建實例。

      public class DemoDbContext : DbContext
      {
          // 靜態成員
          static  ILoggerFactory logFac = LoggerFactory.Create(lb => {
              lb.AddConsole();
              lb.SetMinimumLevel(LogLevel.Trace);
           });
      
          ……
      
          protected override void OnConfiguring(DbContextOptionsBuilder opBuilder)
          {
              opBuilder.UseSqlite("data source=:memory:")
                              // 開啟日志
                              //.LogTo(log => Console.WriteLine(log))
                              .UseLoggerFactory(logFac)
                              // 使用自定義的擴展
                              .UseDemoExt();
          }
      
          ……
      }

       SetMinimumLevel 方法將日志級別設置為 Debug 或 Trace。

      在 OnConfiguring 方法中,使用 UseLoggerFactory 方法應用 LoggerFactory 對象。

      修改之后,重新運行程序。結果如下。

      image

       

      好了,今天就水到這里吧。

      posted @ 2025-10-06 12:07  東邪獨孤  閱讀(645)  評論(1)    收藏  舉報
      主站蜘蛛池模板: 国产乱色熟女一二三四区| 最近2019中文字幕大全第二页| 国产女同一区二区在线| 久久99精品久久久久麻豆| 精品一区二区不卡无码AV| 精品人妻系列无码人妻免费视频| 精品一区二区三区四区色| 精品国产一区二区亚洲人| 成人做爰www网站视频| 精品国偷自产在线视频99| 一个色综合亚洲热色综合| 日产国产一区二区不卡| 裸身美女无遮挡永久免费视频| 97精品尹人久久大香线蕉| 国产在线精彩自拍视频| 欧美性群另类交| 虎白女粉嫩尤物福利视频| 亚洲色最新高清AV网站| 精品卡通动漫亚洲AV第一页| 成人欧美一区二区三区在线| 东京热tokyo综合久久精品| 亚洲夂夂婷婷色拍ww47| 国产在线精品一区二区三区不卡| 亚洲国产精品日韩专区av| 午夜AAAAA级岛国福利在线| 男人猛躁进女人免费播放| 久久热99这里只有精品| 日本三级理论久久人妻电影| 欧美色欧美亚洲高清在线视频| 国产熟女丝袜av一二区| 国产精品毛片一区二区| 中文字幕午夜福利片午夜福利片97| 久久一日本道色综合久久| 精品一区二区三区女性色| 国产99视频精品免费观看9| 色欲狠狠躁天天躁无码中文字幕 | 国产一区二区三区黄色大片 | 在线中文字幕国产一区| 国产一区二区四区不卡| 天堂网www在线资源网| 欧美一区二区三区激情|