10分鐘 Castle.Windsor 適配 Asp.Net Core 3.0

沉迷程式碼的萌新發表於2020-08-03

Asp.Net Core 3.0以上,不再能通過修改Starup.ConfigureServices返回值(IServiceProvider),所以只能呼叫IHostBuilder.UseServiceProviderFactory修改最外層的IServiceProviderFactory,達到替換DI容器的目的。 流程是IHostBuilder.UseServiceProviderFactory->IHostBuilder.ConfigureServices->Starup.ConfigureServices,而且IHostBuilder.UseServiceProviderFactory不支援依賴注入,所以只能例項化了,不能取巧的IHostBuilder.ConfigureServices內註冊IServiceProviderFactory

配置Windsor註冊模組

public interface IWindsorContainerModule
{
  void Configure(WindsorContainer container);
}

這塊不支援依賴注入,詳情參考上文

自定義DI容器

核心模組 WindsorRegistrationHelper.CreateServiceProvider ,引用Nuget Castle.Windsor.MsDependencyInjection

internal class WindsorContainerFactory : IServiceProviderFactory<IServiceCollection>
{
  private WindsorContainer container;
  private IServiceCollection services;

  public WindsorContainerFactory(IWindsorContainerModule containerModule)
  {
    container = new WindsorContainer();
    containerModule.Configure(container);
  }

  public IServiceCollection CreateBuilder(IServiceCollection services)
  {
    this.services = services;
    return services;
  }

  public IServiceProvider CreateServiceProvider(IServiceCollection containerBuilder)
  {
    return WindsorRegistrationHelper.CreateServiceProvider(container, services);
  }
}

這裡完成將WindsorMS DI註冊,統一轉換為根節點的IServiceProvider,內部流程獨立走Windsor,對外則是MS DI的流程。

IHostBuilder擴充套件方法

為了呼叫更簡潔易懂,在IHostBuilder上擴充套件一個UseWindsorContainer方法.

public static IHostBuilder UseWindsorContainer<TWindsorContainerModule>(this IHostBuilder hostBuilder, TWindsorContainerModule containerModule)
where TWindsorContainerModule : class, IWindsorContainerModule
{
  return hostBuilder
    .UseServiceProviderFactory(new WindsorContainerFactory(containerModule))
    .ConfigureServices(services =>
    {
      services.AddSingleton<IWindsorContainerModule, TWindsorContainerModule>();
    });
}

編寫示例程式碼

internal class Sample : IWindsorContainerModule
{
  public void Configure(WindsorContainer container)
  {
    Console.WriteLine("IWindsorContainerModule Instance Name:sample");

    container.Register(Component.For(typeof(IHelloInterface)).ImplementedBy(typeof(HelloInterface)).LifestyleSingleton());
  }
}

public interface IHelloInterface
{
  void Show();
}

internal class HelloInterface : IHelloInterface
{
  public void Show()
  {
    Console.WriteLine(nameof(HelloInterface));
  }
}

修改Program.CreateHostBuilder

public static IHostBuilder CreateHostBuilder(string[] args) =>
  Host.CreateDefaultBuilder(args)
    .UseWindsorContainer(new Sample()) //新增
    .ConfigureWebHostDefaults(webBuilder =>
    {
      webBuilder
        .UseStartup<Startup>();
});

修改Controllers.WeatherForecastController.Get

public IEnumerable<WeatherForecast> Get()
{
    Console.WriteLine(_helloInterface.GetType().FullName); //列印介面實際實現,驗證是否代理到Castle.Windsor
    _helloInterface.Show(); //介面呼叫方法
 
     var rng = new Random();
     return Enumerable.Range(1, 5).Select(index => new WeatherForecast
     {
       Date = DateTime.Now.AddDays(index),
       TemperatureC = rng.Next(-20, 55),
       Summary = Summaries[rng.Next(Summaries.Length)]
     })
     .ToArray();
}


後記

感謝Lemon大人的指點關於 IHostBuilder.UseServiceProviderFactory到IHostBuilder.ConfigureServices的細節和最初的預估差異不小,最早的程式碼實現版本是IHostBuilder.ConfigureServices內註冊了IServiceProviderFactory,希望更優雅的實現替換DI容器,現實是這塊是不可以的

如果對於內容有交流和學習的,歡迎加 .Net應用程式框架交流群,群號386092459

相關文章