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

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

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

      Util應用框架基礎(一) - 依賴注入

      本節介紹Util應用框架依賴注入的使用和配置擴展.

      文章分為多個小節,如果對設計原理不感興趣,只需閱讀基礎用法部分即可.

      概述

      當你想調用某個服務的方法完成特定功能時,首先需要得到這個服務的實例.

      最簡單的辦法是直接 new 一個服務實例,不過這樣就把服務的實現牢牢綁死了,當你需要更換實現,除了直接修改它沒有別的辦法.

      依賴注入是一種獲取服務實例更好的方法.

      通常需要先定義服務接口,然后在你的構造方法聲明這些接口參數.

      服務實例不是你創建的,而是從外部傳入的.

      你只跟服務接口打交道,所以不會被具體的實現類綁死.

      依賴注入框架

      現在每個服務都在自己的構造方法定義參數接收依賴項,但是最終必須在某處真正創建這些服務實例.

      使用new手工創建服務實例是不可行的,因為存在依賴鏈,比如使用 new A() 創建服務A的實例時,服務A可能依賴服務B,需要先創建服務B的實例,而服務B可能還有依賴.

      另外,某些服務可能需要特定的生命周期,比如工作單元服務,在單個請求過程,每次注入的工作單元實例必須是同一個.

      我們需要一種機制,能夠自動創建具有依賴的服務實例,并管理實例的生命周期.

      Asp.Net Core 內置了構造方法依賴注入能力.

      通過構造方法注入服務實例,是依賴注入最常見的形式.

      一些專門的依賴注入框架,比如 autofac 支持屬性注入等高級功能.

      Util應用框架使用Asp.Net Core內置的依賴注入,對于大部分業務場景,構造方法注入已經足夠了.

      依賴注入生命周期

      依賴注入有三種生命周期.

      • Singleton 單例

        在整個系統只創建一個實例.

        無狀態或不可變的服務才能設置成單例.

      • Scope 每個請求創建一個實例

        對于 Asp.Net Core 環境,每個請求創建一個實例,在整個請求過程,獲取的是同一個實例,在請求結束時銷毀.

        注意: 對于非 Asp.Net Core 環境,Scope 生命周期與 Singleton 相同.

        在Util項目中,與工作單元相關的服務都需要設置成 Scope 生命周期,比如 工作單元,倉儲,領域服務,應用服務等.

      • Transient 每次調用創建一個新實例

        每次注入都會創建一個新的服務實例.

      依賴注入最佳實踐

      一個接口配置一個實現

      定義接口的目的是為了方便切換實現.

      一個接口可能有多個實現類,但是在同一時間,應該只有一個實現類生效.

      舉個例子,倉儲接口有兩個實現類.

      /// <summary>
      /// 倉儲
      /// </summary>
      public interface IRepository {
      }
      
      /// <summary>
      /// 倉儲1
      /// </summary>
      public class Repository1 : IRepository {
      }
      
      /// <summary>
      /// 倉儲2
      /// </summary>
      public class Repository2 : IRepository {
      }
      

      有兩個應用服務,服務1需要倉儲1的實例,服務2需要倉儲2的實例.

      /// <summary>
      /// 服務1
      /// </summary>
      public class Service1 {
          public Service1( IRepository repository ) {
          }
      }
      
      /// <summary>
      /// 服務2
      /// </summary>
      public class Service2 {
          public Service2( IRepository repository ) {
          }
      }
      

      現在, IRepository有兩個實例,并且這兩個實例都處于使用狀態.

      兩個服務都注入了 IRepository 接口, 如何把正確的倉儲實例注入到指定的服務中?

      一些依賴注入框架可以為特定實現類命名,然后為服務傳遞特定命名的依賴項,不過這種方法復雜且容易出錯.

      一種簡單有效的方法是創建更具體的接口,從而讓每種生效的實現類只有一個.

      /// <summary>
      /// 倉儲
      /// </summary>
      public interface IRepository {
      }
      
      /// <summary>
      /// 倉儲1
      /// </summary>
      public interface IRepository1 : IRepository {
      }
      
      /// <summary>
      /// 倉儲2
      /// </summary>
      public interface IRepository2 : IRepository {
      }
      
      /// <summary>
      /// 倉儲1
      /// </summary>
      public class Repository1 : IRepository1 {
      }
      
      /// <summary>
      /// 倉儲2
      /// </summary>
      public class Repository2 : IRepository2 {
      }
      
      /// <summary>
      /// 服務1
      /// </summary>
      public class Service1 {
          public Service1( IRepository1 repository ) {
          }
      }
      
      /// <summary>
      /// 服務2
      /// </summary>
      public class Service2 {
          public Service2( IRepository2 repository ) {
          }
      }
      

      由于注入了更具體的接口,所以不需要特定的依賴配置方法.

      不要奇怪,雖然現在每個接口只有一個實現,但你在任何時候都可以增加實現類進行切換.

      唯一需要記住的是,任何時候,生效的實現類應該只有一個.

      依賴注入的使用范圍

      通常對服務類型使用依賴注入,比如控制器,應用服務,領域服務,倉儲等.

      實體可能也包含某些依賴項,但不能使用依賴注入框架創建實體.

      簡單實體使用 new 創建,更復雜的實體創建過程使用工廠進行封裝.

      基礎用法

      通過構造方法獲取依賴服務

      只需在構造方法定義需要的服務參數即可.

      范例:

      /// <summary>
      /// 測試服務
      /// </summary>
      public class TestService {
          public TestService( ITestRepository repository ) {
          }
      }
      

      配置依賴服務

      Asp.Net Core 標準的依賴配置方法是調用 IServiceCollection 擴展方法.

      范例:

      配置 ITestService 接口的實現類為 TestService,生命周期為 Scope.

      var builder = WebApplication.CreateBuilder( args );
      builder.Services.AddScoped<ITestService, TestService>();
      

      不過,大部分時候,你都不需要手工配置依賴服務,它由Util應用框架自動掃描配置.

      依賴配置擴展

      Util應用框架提供了三個接口,用于自動配置相應生命周期的依賴服務.

      • Util.Dependency.ISingletonDependency
        配置生命周期為 Singleton 的服務.
      • Util.Dependency.IScopeDependency
        配置生命周期為 Scope 的服務.
      • Util.Dependency.ITransientDependency
        配置生命周期為 Transient 的服務.

      限制: 必須把 ISingletonDependency 這三個接口放在需要配置的接口上,不能放在實現類上.

      范例:

      服務基接口 IService 繼承了 IScopeDependency 接口.

      所有繼承了 IService 的服務接口,在啟動時自動查找相應的實現類,并設置為 Scope 服務.

      /// <summary>
      /// 服務
      /// </summary>
      public interface IService : IScopeDependency {
      }
      

      更改實現類依賴配置優先級

      當使用 ISingletonDependency 等接口自動配置依賴關系時,如果服務接口有多個實現類,究竟哪個生效?

      Util應用框架提供了 Util.Dependency.IocAttribute 特性,用于更改依賴優先級,從而精確指定實現類.

      范例:

      服務 Service1 實現了服務接口 IService, IService 從 IScopeDependency 繼承.

      實現類的默認優先級為 0.

      IocAttribute 特性接收一個表示優先級的整數,值越大,表示優先級越高.

      服務 Service2 的依賴優先級設置為 1,比 Service1 大,所以注入 IService 接口的實現類是 Service2.

      /// <summary>
      /// 服務1
      /// </summary>
      public class Service1 : IService {
      }
      
      /// <summary>
      /// 服務2
      /// </summary>
      [Ioc(1)]
      public class Service2 : IService {
      }
      

      服務定位器

      構造方法依賴注入簡單清晰,只需查看構造方法就能了解依賴的服務.

      不過它也帶來了一些問題.

      如果服務基類使用了構造方法依賴注入,每當依賴服務發生變化,都需要修改所有子類的構造方法,這會導致架構的脆弱性.

      另一個問題是無法通過依賴注入為靜態方法提供依賴項.

      在業務場景使用靜態方法是一種陋習,需要堅決抵制.

      但是某些工具類使用靜態方法可能更方便.

      服務定位器概述

      服務定位器從對象容器中主動拉取依賴服務.

      依賴注入和服務定位器都從對象容器獲取依賴項,但依賴注入的依賴項是從外部被動推入的.

      服務定位器比依賴注入的耦合度高,也更難測試,不過它能解決之前提到的問題.

      為了讓服務基類穩定,可以在基類構造方法獲取 IServiceProvider 參數.

      IServiceProvider 是 .Net 服務提供程序,可以調用它獲取依賴服務.

      下面來看看Util應用服務基類.

      /// <summary>
      /// 應用服務
      /// </summary>
      public abstract class ServiceBase : IService {
          /// <summary>
          /// 初始化應用服務
          /// </summary>
          /// <param name="serviceProvider">服務提供器</param>
          protected ServiceBase( IServiceProvider serviceProvider ) {
              ServiceProvider = serviceProvider ?? throw new ArgumentNullException( nameof( serviceProvider ) );
              Session = serviceProvider.GetService<ISession>() ?? NullSession.Instance;
              IntegrationEventBus = serviceProvider.GetService<IIntegrationEventBus>() ?? NullIntegrationEventBus.Instance;
              var logFactory = serviceProvider.GetService<ILogFactory>();
              Log = logFactory?.CreateLog( GetType() ) ?? NullLog.Instance;
          }
      
          /// <summary>
          /// 服務提供器
          /// </summary>
          protected IServiceProvider ServiceProvider { get; }
      
          /// <summary>
          /// 用戶會話
          /// </summary>
          protected ISession Session { get; }
      
          /// <summary>
          /// 集成事件總線
          /// </summary>
          protected IIntegrationEventBus IntegrationEventBus { get; }
      
          /// <summary>
          /// 日志操作
          /// </summary>
          protected ILog Log { get; }
      }
      

      應用服務基類定義了用戶會話和日志操作等依賴項,但不是從構造方法獲取的,而是調用服務提供程序 IServiceProviderGetService 方法.

      通過傳遞 IServiceProvider 參數,服務子類不需要在構造方法聲明用戶會話等其它依賴項,減輕了負擔.

      當依賴項發生變化時,不需要修改基類的構造方法參數,直接通過服務提供程序獲取依賴.

      構造方法獲取 IServiceProvider 參數解決了服務基類的問題,但 IServiceProvider 參數本身還是通過依賴注入方式提供的.

      無法通過依賴注入為靜態工具類傳遞參數,在靜態工具方法中傳遞 IServiceProvider 參數又會導致API難用.

      服務定位器工具類

      一個常見的需求是在靜態工具方法中獲取當前 HttpContext 實例,并訪問它的某些功能.

      在更早的 Asp.Net 中, 我們可以通過 HttpContext.Current 靜態屬性來獲取當前Http上下文.

      但 Asp.Net Core 已經拋棄這種用法,現在需要先依賴注入 IHttpContextAccessor 實例,并使用它獲取當前Http上下文.

      Util提供了一個服務定位器工具類 Util.Helpers.Ioc .

      通過調用 Ioc 靜態方法 Create 就能獲取依賴服務.

      范例:

      下面的例子演示了如何在靜態方法中獲取遠程IP地址.

      先通過 Ioc.Create 獲取Http上下文訪問器, 然后得到當前Http上下文,調用它的 Connection.RemoteIpAddress 獲取遠程IP地址.

      public static class Tool {
          /// <summary>
          /// 獲取客戶端Ip地址
          /// </summary>
          public static string GetIp() {
              var httpContext = Ioc.Create<IHttpContextAccessor>()?.HttpContext;
              return httpContext?.Connection.RemoteIpAddress?.ToString();
          }
      }
      

      使用 Ioc.Create 方法獲取依賴項要小心,只有在 Asp.Net Core 環境中才能安全使用.

      在后臺任務等其它環境中, Ioc.Create 與依賴注入使用的對象容器可能不同.

      由于它具有副作用, Util靜態工具方法已經很少使用它.

      Util.Helpers.Ioc 現在用在不太重要的一些場景,業務開發中應嚴格使用依賴注入獲取依賴.

      Util應用框架提供了另一個工具類 Util.Helpers.Web 來支持 Asp.Net Core 靜態工具方法.

      使用 Util.Helpers.Web 改造上面的例子.

      public static class Tool {
          /// <summary>
          /// 獲取客戶端Ip地址
          /// </summary>
          public static string GetIp() {
              return Web.HttpContext?.Connection.RemoteIpAddress?.ToString();
          }
      }
      

      你可以通過 Web.HttpContext 獲取當前Http上下文,比使用 Ioc.Create 方便得多.

      源碼解析

      DependencyServiceRegistrar 依賴服務注冊器

      依賴服務注冊器提供對 Util.Dependency.ISingletonDependency 等接口的依賴配置擴展支持.

      通過類型查找器分別查找實現了 ISingletonDependency,IScopeDependency,ITransientDependency 三個接口的所有class.

      對每個class類,查找它們的接口,并注冊相應生命周期的依賴關系.

      /// <summary>
      /// 依賴服務注冊器 - 用于掃描注冊ISingletonDependency,IScopeDependency,ITransientDependency
      /// </summary>
      public class DependencyServiceRegistrar : IServiceRegistrar {
          /// <summary>
          /// 獲取服務名
          /// </summary>
          public static string ServiceName => "Util.Infrastructure.DependencyServiceRegistrar";
      
          /// <summary>
          /// 排序號
          /// </summary>
          public int OrderId => 100;
      
          /// <summary>
          /// 是否啟用
          /// </summary>
          public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName );
      
          /// <summary>
          /// 注冊服務
          /// </summary>
          /// <param name="serviceContext">服務上下文</param>
          public Action Register( ServiceContext serviceContext ) {
              return () => {
                  serviceContext.HostBuilder.ConfigureServices( ( context, services ) => {
                      RegisterDependency<ISingletonDependency>( services, serviceContext.TypeFinder, ServiceLifetime.Singleton );
                      RegisterDependency<IScopeDependency>( services, serviceContext.TypeFinder, ServiceLifetime.Scoped );
                      RegisterDependency<ITransientDependency>( services, serviceContext.TypeFinder, ServiceLifetime.Transient );
                  } );
              };
          }
      
          /// <summary>
          /// 注冊依賴
          /// </summary>
          private void RegisterDependency<TDependencyInterface>( IServiceCollection services, ITypeFinder finder, ServiceLifetime lifetime ) {
              var types = GetTypes<TDependencyInterface>( finder );
              var result = FilterTypes( types );
              foreach ( var item in result )
                  RegisterType( services, item.Item1, item.Item2, lifetime );
          }
      
          /// <summary>
          /// 獲取接口類型和實現類型列表
          /// </summary>
          private List<(Type, Type)> GetTypes<TDependencyInterface>( ITypeFinder finder ) {
              var result = new List<(Type, Type)>();
              var classTypes = finder.Find<TDependencyInterface>();
              foreach ( var classType in classTypes ) {
                  var interfaceTypes = Util.Helpers.Reflection.GetInterfaceTypes( classType, typeof( TDependencyInterface ) );
                  interfaceTypes.ForEach( interfaceType => result.Add( (interfaceType, classType) ) );
              }
              return result;
          }
      
          /// <summary>
          /// 過濾類型
          /// </summary>
          private List<(Type, Type)> FilterTypes( List<(Type, Type)> types ) {
              var result = new List<(Type, Type)>();
              foreach ( var group in types.GroupBy( t => t.Item1 ) ) {
                  if ( group.Count() == 1 ) {
                      result.Add( group.First() );
                      continue;
                  }
                  result.Add( GetTypesByPriority( group ) );
              }
              return result;
          }
      
          /// <summary>
          /// 獲取優先級類型
          /// </summary>
          private (Type, Type) GetTypesByPriority( IGrouping<Type, (Type, Type)> group ) {
              int? currentPriority = null;
              Type classType = null;
              foreach ( var item in group ) {
                  var priority = GetPriority( item.Item2 );
                  if ( currentPriority == null || priority > currentPriority ) {
                      currentPriority = priority;
                      classType = item.Item2;
                  }
              }
              return ( group.Key, classType );
          }
      
          /// <summary>
          /// 獲取優先級
          /// </summary>
          private int GetPriority( Type type ) {
              var attribute = type.GetCustomAttribute<IocAttribute>();
              if ( attribute == null )
                  return 0;
              return attribute.Priority;
          }
      
          /// <summary>
          /// 注冊類型
          /// </summary>
          private void RegisterType( IServiceCollection services, Type interfaceType, Type classType, ServiceLifetime lifetime ) {
              services.TryAdd( new ServiceDescriptor( interfaceType, classType, lifetime ) );
          }
      }
      

      Ioc 服務定位器工具類

      Ioc 工具類內置了一個對象容器,如果沒有為它設置服務提供器,它將從內置對象容器獲取依賴,這是導致副作用的根源.

      /// <summary>
      /// 容器操作
      /// </summary>
      public static class Ioc {
          /// <summary>
          /// 容器
          /// </summary>
          private static readonly Util.Dependency.Container _container = Util.Dependency.Container.Instance;
          /// <summary>
          /// 獲取服務提供器操作
          /// </summary>
          private static Func<IServiceProvider> _getServiceProviderAction;
      
          /// <summary>
          /// 服務范圍工廠
          /// </summary>
          public static IServiceScopeFactory ServiceScopeFactory { get; set; }
      
          /// <summary>
          /// 創建新容器
          /// </summary>
          public static Util.Dependency.Container CreateContainer() {
              return new Util.Dependency.Container();
          }
      
          /// <summary>
          /// 獲取服務集合
          /// </summary>
          public static IServiceCollection GetServices() {
              return _container.GetServices();
          }
      
          /// <summary>
          /// 設置獲取服務提供器操作
          /// </summary>
          /// <param name="action">獲取服務提供器操作</param>
          public static void SetServiceProviderAction( Func<IServiceProvider> action ) {
              _getServiceProviderAction = action;
          }
      
          /// <summary>
          /// 獲取
          /// </summary>
          public static IServiceProvider GetServiceProvider() {
              var provider = _getServiceProviderAction?.Invoke();
              if ( provider != null )
                  return provider;
              return _container.GetServiceProvider();
          }
      
          /// <summary>
          /// 創建對象
          /// </summary>
          /// <typeparam name="T">對象類型</typeparam>
          public static T Create<T>() {
              return Create<T>( typeof( T ) );
          }
      
          /// <summary>
          /// 創建對象
          /// </summary>
          /// <typeparam name="T">返回對象類型</typeparam>
          /// <param name="type">對象類型</param>
          public static T Create<T>( Type type ) {
              var service = Create( type );
              if( service == null )
                  return default;
              return (T)service;
          }
      
          /// <summary>
          /// 創建對象
          /// </summary>
          /// <param name="type">對象類型</param>
          public static object Create( Type type ) {
              if( type == null )
                  return null;
              var provider = GetServiceProvider();
              return provider.GetService( type );
          }
      
          /// <summary>
          /// 創建對象集合
          /// </summary>
          /// <typeparam name="T">返回類型</typeparam>
          public static List<T> CreateList<T>() {
              return CreateList<T>( typeof( T ) );
          }
      
          /// <summary>
          /// 創建對象集合
          /// </summary>
          /// <typeparam name="T">返回類型</typeparam>
          /// <param name="type">對象類型</param>
          public static List<T> CreateList<T>( Type type ) {
              Type serviceType = typeof( IEnumerable<> ).MakeGenericType( type );
              var result = Create( serviceType );
              if( result == null )
                  return new List<T>();
              return ( (IEnumerable<T>)result ).ToList();
          }
      
          /// <summary>
          /// 創建服務范圍
          /// </summary>
          public static IServiceScope CreateScope() {
              var provider = GetServiceProvider();
              return provider.CreateScope();
          }
      
          /// <summary>
          /// 清理
          /// </summary>
          public static void Clear() {
              _container.Clear();
          }
      }
      

      Ioc 工具類需要獲取正確的服務提供器,可以通過 SetServiceProviderAction 方法進行設置.

      對于 Asp.Net Core 環境, AspNetCoreServiceRegistrar 服務注冊器已經正確設置Ioc工具類的服務提供器.

      但對于非 Asp.Net Core 環境, 設置正確的服務提供器可能非常困難.

      /// <summary>
      /// AspNetCore服務注冊器
      /// </summary>
      public class AspNetCoreServiceRegistrar : IServiceRegistrar {
          /// <summary>
          /// 獲取服務名
          /// </summary>
          public static string ServiceName => "Util.Infrastructure.AspNetCoreServiceRegistrar";
      
          /// <summary>
          /// 排序號
          /// </summary>
          public int OrderId => 200;
      
          /// <summary>
          /// 是否啟用
          /// </summary>
          public bool Enabled => ServiceRegistrarConfig.IsEnabled( ServiceName );
      
          /// <summary>
          /// 注冊服務
          /// </summary>
          /// <param name="serviceContext">服務上下文</param>
          public Action Register( ServiceContext serviceContext ) {
              serviceContext.HostBuilder.ConfigureServices( ( context, services ) => {
                  RegisterHttpContextAccessor( services );
                  RegisterServiceLocator();
              } );
              return null;
          }
      
          /// <summary>
          /// 注冊Http上下文訪問器
          /// </summary>
          private void RegisterHttpContextAccessor( IServiceCollection services ) {
              var httpContextAccessor = new HttpContextAccessor();
              services.TryAddSingleton<IHttpContextAccessor>( httpContextAccessor );
              Web.HttpContextAccessor = httpContextAccessor;
          }
      
          /// <summary>
          /// 注冊服務定位器
          /// </summary>
          private void RegisterServiceLocator() {
              Ioc.SetServiceProviderAction( () => Web.ServiceProvider );
          }
      }
      

      禁用依賴服務注冊器

      如果你不想自動掃描注冊 ISingletonDependency,IScopeDependency,ITransientDependency 相關依賴,可以禁用它.

      ServiceRegistrarConfig.Instance.DisableDependencyServiceRegistrar();
      builder.AsBuild().AddUtil();
      
      posted @ 2023-11-02 11:53  何鎮汐  閱讀(659)  評論(0)    收藏  舉報
      主站蜘蛛池模板: 狠狠色噜噜狠狠狠狠色综合久av| 久久精品人人槡人妻人人玩| 精品无码一区在线观看| 日韩中文字幕高清有码| 札达县| 国产中文字幕日韩精品| 乌海市| 狠狠躁日日躁夜夜躁欧美老妇| 中国女人熟毛茸茸A毛片| 国产精品无码久久久久| 成人自拍小视频在线观看| 久久国产成人av蜜臀| 我国产码在线观看av哈哈哈网站| 精品一区二区中文字幕| 香蕉久久久久久久av网站| 日韩国产亚洲欧美成人图片| 亚洲性日韩一区二区三区| 色欲av亚洲一区无码少妇| 开心五月激情五月俺亚洲| 成人免费A级毛片无码片2022| 无套内射视频囯产| 99精品热在线在线观看视| 18禁网站免费无遮挡无码中文| 亚洲国产精品第一二三区| 欧美人与动牲交A免费观看| 精品无码成人久久久久久| 扎囊县| av在线播放观看国产| 91老肥熟女九色老女人| 无码国产精品一区二区免费3p| 中文字幕久无码免费久久| 日日噜噜噜夜夜爽爽狠狠视频 | 国产av无码专区亚洲草草| aa级毛片毛片免费观看久| 日韩精品国产另类专区| 亚洲国产av无码综合原创国产| 精品乱码一区二区三四五区 | 人妻在线中文字幕| 中文字幕久久久久人妻中出| 亚洲欧美日韩久久一区二区| 日日躁夜夜躁狠狠久久av|