ASP.NET Core 3.0 原生DI拓展實現IocManager

来源:https://www.cnblogs.com/lwc1st/archive/2019/09/25/11582662.html
-Advertisement-
Play Games

昨天 .NET Core 3.0 正式發佈,創建一個項目運行後發現:原來使用的Autofac在ConfigureServices返回IServiceProvider的這種寫法已經不再支持。 當然Autofac官方也給出了示例。.NET Core 本身內置DI,我決定不再使用Autofac,就使用原生 ...


昨天.NET Core 3.0正式發佈,創建一個項目運行後發現:原來使用的Autofac在ConfigureServices返回IServiceProvider的這種寫法已經不再支持。

當然Autofac官方也給出了示例。.NET Core 本身內置DI,我決定不再使用Autofac,就使用原生DI,拓展IServiceCollection實現一個IocManager,

實現批量註入,靜態獲取實例能。末尾處含有Autofac IocManager實現方式。

一、Autofac官方文檔

Program Class

Hosting changed in ASP.NET Core 3.0 and requires a slightly different integration. This is for ASP.NET Core 3+ and the .NET Core 3+ generic hosting support:

public class Program
{
  public static void Main(string[] args)
  {
    // ASP.NET Core 3.0+:
    // The UseServiceProviderFactory call attaches the
    // Autofac provider to the generic hosting mechanism.
    var host = Host.CreateDefaultBuilder(args)
        .UseServiceProviderFactory(new AutofacServiceProviderFactory())
        .ConfigureWebHostDefaults(webHostBuilder => {
          webHostBuilder
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>();
        })
        .Build();

    host.Run();
  }
}

Startup Class

In your Startup class (which is basically the same across all the versions of ASP.NET Core) you then use ConfigureContainer to access the Autofac container builder and register things directly with Autofac.

public class Startup
{
  public Startup(IHostingEnvironment env)
  {
    // In ASP.NET Core 3.0 env will be an IWebHostingEnvironment, not IHostingEnvironment.
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
        .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
        .AddEnvironmentVariables();
    this.Configuration = builder.Build();
  }

  public IConfigurationRoot Configuration { get; private set; }

  public ILifetimeScope AutofacContainer { get; private set; }

  // ConfigureServices is where you register dependencies. This gets
  // called by the runtime before the ConfigureContainer method, below.
  public void ConfigureServices(IServiceCollection services)
  {
    // Add services to the collection. Don't build or return
    // any IServiceProvider or the ConfigureContainer method
    // won't get called.
    services.AddOptions();
  }

  // ConfigureContainer is where you can register things directly
  // with Autofac. This runs after ConfigureServices so the things
  // here will override registrations made in ConfigureServices.
  // Don't build the container; that gets done for you. If you
  // need a reference to the container, you need to use the
  // "Without ConfigureContainer" mechanism shown later.
  public void ConfigureContainer(ContainerBuilder builder)
  {
      builder.RegisterModule(new AutofacModule());
  }

  // Configure is where you add middleware. This is called after
  // ConfigureContainer. You can use IApplicationBuilder.ApplicationServices
  // here if you need to resolve things from the container.
  public void Configure(
    IApplicationBuilder app,
    ILoggerFactory loggerFactory)
  {
    // If, for some reason, you need a reference to the built container, you
    // can use the convenience extension method GetAutofacRoot.
    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();

    loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
    loggerFactory.AddDebug();
    app.UseMvc();
  }
}

二、IocManager實現

1、創建IocManager

IIocManager介面

public interface IIocManager
{
    IServiceProvider ServiceProvider { get; set; }
}

IocManager實現

public class IocManager : IIocManager
{
    static IocManager()
    {
        Instance = new IocManager();
    }
    public static IocManager Instance { get; private set; }
    public IServiceProvider ServiceProvider { get; set; }
}

2、創建生命周期介面

    /// <summary>
    ///    標記依賴項生命周期的介面
    ///     <see cref="ILifetimeScopeDependency" />,
    ///     <see cref="ITransientDependency" />,
    ///     <see cref="ISingletonDependency" />
    /// </summary>
    public interface ILifetime { }
    /// <summary>
    /// 確定介面或類的生存期
    /// 作用域模式,服務在每次請求時被創建,整個請求過程中都貫穿使用這個創建的服務。
    /// </summary>
    public interface ILifetimeScopeDependency : ILifetime { }
    /// <summary>
    /// 確定介面或類的生存期
    /// 作用域模式,服務在每次請求時被創建,整個請求過程中都貫穿使用這個創建的服務。
    /// </summary>
    public interface ILifetimeScopeDependency : ILifetime { }
    /// <summary>
    /// 確定介面或類的生存期
    /// 瞬態模式,每次請求時都會創建。
    /// </summary>
    public interface ITransientDependency : ILifetime { }

3、拓展IServiceCollection

/// <summary>
/// .NET Core 依賴註入拓展
/// </summary>
public static class DependencyInjectionExtensions
{
    /// <summary>
    /// 註冊程式集組件
    /// </summary>
    /// <param name="services"></param>
    /// <param name="assemblies"></param>
    /// <returns></returns>
    public static IServiceCollection AddAssembly(this IServiceCollection services, params Assembly[] assemblies)
    {
        if (assemblies==null|assemblies.Count()==0)
        {
            throw new Exception("assemblies cannot be empty.");
        }
        foreach (var assembly in assemblies)
        {
            RegisterDependenciesByAssembly<ISingletonDependency>(services, assembly);
            RegisterDependenciesByAssembly<ITransientDependency>(services, assembly);
            RegisterDependenciesByAssembly<ILifetimeScopeDependency>(services, assembly);
        }
        return services;
    }

    public static void RegisterDependenciesByAssembly<TServiceLifetime>(IServiceCollection services, Assembly assembly)
    {            
        var types = assembly.GetTypes().Where(x => typeof(TServiceLifetime).GetTypeInfo().IsAssignableFrom(x) && x.GetTypeInfo().IsClass && !x.GetTypeInfo().IsAbstract && !x.GetTypeInfo().IsSealed).ToList();
        foreach (var type in types)
        {
            var itype = type.GetTypeInfo().GetInterfaces().FirstOrDefault(x => x.Name.ToUpper().Contains(type.Name.ToUpper()));
            if (itype!=null)
            {
                var serviceLifetime = FindServiceLifetime(typeof(TServiceLifetime));
                services.Add(new ServiceDescriptor(itype, type, serviceLifetime));
            }
        }
    }

    private static ServiceLifetime FindServiceLifetime(Type type)
    {
        if (type == typeof(ISingletonDependency))
        {
            return ServiceLifetime.Singleton;
        }
        if (type == typeof(ITransientDependency))
        {
            return ServiceLifetime.Singleton;
        }
        if (type == typeof(ILifetimeScopeDependency))
        {
            return ServiceLifetime.Singleton;
        }

        throw new ArgumentOutOfRangeException($"Provided ServiceLifetime type is invalid. Lifetime:{type.Name}");
    }

    /// <summary>
    /// 註冊IocManager
    /// 在ConfigureServices方法最後一行使用
    /// </summary>
    /// <param name="services"></param>
    public static void AddIocManager(this IServiceCollection services)
    {
        services.AddSingleton<IIocManager, IocManager>(provide =>
        {
            IocManager.Instance.ServiceProvider = provide;
            return IocManager.Instance;
        });
    }
}

4、IocManager使用實例:

4.1、示常式序集

namespace Service
{
    public interface IUserService
    {
        string GetUserNameById(string Id);
    }
    public interface UserService:IUserService,ISingletonDependency
    {
        public string GetUserNameById(string Id)
        {
         return "劉大大";
        }
    }
}

4.2、為程式集寫一個拓展類

public static class ServiceExtensions
{
    public static IServiceCollection UseService(this IServiceCollection services)
    {
        var assembly = typeof(ServiceExtensions).Assembly;
        services.AddAssembly(assembly);
        return services;
    }
}

4.3、Web層使用

Startup class
       public void ConfigureServices(IServiceCollection services)
        {
           services.UseService();
           
           services.AddControllersWithViews();
           
           services.AddIocManager();
        }
Controller

IIocManager實現了單例模式,可以通過構造器註入獲取實例,也可以通過通過IocManager.Instance獲取實例

    public class HomeController : Controller
    {
        private readonly IIocManager _iocManager;
        public HomeController(IIocManager iocManager)
        {
            _iocManager = iocManager;
        }
        public string test1()
        {
         //通過註入獲取IocManager實例
         var _userService=_iocManager.ServiceProvider.GetService<IUserService>(); 
         var userName=_userService.GetUserNameById("1");
         return userName;
        }
        
        public string test2()
        {
         //通過IocManager獲取IIocManager實例
         var _userService=IocManager.Instance.ServiceProvider.GetService<IUserService>(); 
         var userName=_userService.GetUserNameById("1");
         return userName;
        }
        
        public string test3([FromServices]IUserService _userService)
        {
         //通過註入獲取Service實例
         var userName=_userService.GetUserNameById("1");
         return userName;
        }
    }

5、Autofac IocManager實現

5.1、安裝 Autofac.Extensions.DependencyInjection包

5.2、 IocManager實現

IIocManager介面

public interface IIocManager
{
    ILifetimeScope ServiceProvider { get; set; }
}

IocManager實現

public class IocManager : IIocManager
{
    static IocManager()
    {
        Instance = new IocManager();
    }
    public static IocManager Instance { get; private set; }
    public ILifetimeScope ServiceProvider { get; set; }
}

靜態類 DependencyInjectionExtensions 添加UseIocManager方法。使用Autofac時可以在ConfigureContaine中直接註冊內容,ConfigureContainer在ConfigureServices之後運行,

所以不能使用在ConfigureServices里註入IocManager,要在Configure方法中引用IocManager。

/// <summary>
/// 註冊IocManager
/// 在Configure方法使用
/// </summary>
/// <param name="services"></param>
public static IApplicationBuilder UseIocManager(this IApplicationBuilder app)
{
    services.AddSingleton<IIocManager, IocManager>(provide =>
    {
        IocManager.Instance.ServiceProvider = app.ApplicationServices.GetAutofacRoot();
        return app;
    });
}

使用示例:

Startup class
  
  public void ConfigureContainer(ContainerBuilder builder)
  {
      builder.RegisterModule(new AutofacModule());
  }

  public void Configure(IApplicationBuilder app,ILoggerFactory loggerFactory)
  {
      app.UseIocManager();
  }
Controller
public class HomeController : Controller
{
    private readonly IIocManager _iocManager;
    public HomeController(IIocManager iocManager)
    {
        _iocManager = iocManager;
    }
    public string test1()
    {
     //通過註入獲取IocManager實例
     var _userService=_iocManager.ServiceProvider.Resolve<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
    
    public string test2()
    {
     //通過IocManager獲取IIocManager實例
     var _userService=IocManager.Instance.ServiceProvider.Resolve<IUserService>(); 
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
    
    public string test3([FromServices]IUserService _userService)
    {
     //通過註入獲取Service實例
     var userName=_userService.GetUserNameById("1");
     return userName;
    }
}


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 問題 前同事編寫的對中控考勤機數據集成項目當中,打卡數據不能實時進行上傳到平臺當中,一直靠定時全量上傳來同步數據。 閱讀代碼後,發現代碼中有實時上傳數據的邏輯,但是運行一段時間後,中控zkemkeeper SDK中的事件失效,導致員工打卡數據沒有實時上傳。 原因 查看中控SDK Demo中的示例代碼 ...
  • 場景 Winforn中設置ZedGraph曲線圖的屬性、坐標軸屬性、刻度屬性: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/100112573 初次載入ZedGraph後,可以進行一些曲線圖屬性的設置,使曲線圖重新載入。 思路: ...
  • 場景 Winforn中設置ZedGraph曲線圖的屬性、坐標軸屬性、刻度屬性: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/100112573 初次載入ZedGraphControl時可以通過其屬性設置其Size大小,然後實現一 ...
  • 前提 入行已經7,8年了,一直想做一套漂亮點的自定義控制項,於是就有了本系列文章。 GitHub:https://github.com/kwwwvagaa/NetWinformControl 碼雲:https://gitee.com/kwwwvagaa/net_winform_custom_contr ...
  • 場景 Winform中自定義xml配置文件後對節點進行讀取與寫入: https://blog.csdn.net/BADAO_LIUMANG_QIZHI/article/details/100532137 在上面實現對xml配置文件進行節點的讀取和寫入時,發現一個問題, 就是節點的內容為空相應的xml ...
  • 直接上效果圖和源碼: 1.先全選數據,然後點擊導出 2.前端ui使用取值 不懂使用Layui的可以去官網有詳細文檔連接:https://www.layui.com 3.這是後臺控制器必須要引入的文件,可以在vs工具->NuGet包管理->程式包管理設置 (裡面搜索) 4.這是後臺代碼,事件,從dal ...
  • 隨著項目的不斷增多,最開始單體項目手動執行 命令,手動發佈項目就不再適用了。一兩個項目可能還吃得消,10 多個項目每天讓你構建一次還是夠嗆。即便你的項目少,每次花費在發佈上面的時間累計起來都夠你改幾個 BUG 了。 所以我們需要自動化這個流程,讓項目的發佈和測試不再這麼繁瑣。在這裡我使用了 Jenk ...
  • 一、手寫 1.如何在JavaScript訪問C#函數? 2.如何在JavaScript訪問C#變數? 3.如何在C#中訪問JavaScript的已有變數? 4.如何在C#中訪問JavaScript函數?ep:var a = "<%=ss()%>";//JavaScript中調用C#後臺的函數,ss是 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...