ASP.NET Core - 利用Windsor Castle實現通用註冊

ASP.NET Core - 利用Windsor Castle實現通用註冊

  問題引入

  在ASP.NET Core - 依賴注入這篇文章裏面,我們知道了如何利用ASP.NET Core原生的容器來實現依賴注入的,那我們爲什麼要替換掉默認的 IoC容器呢?從ASP.NET Core - 依賴注入這篇文章來看的話,默認的IoC容器對於一些小型的項目基本夠用,它提供了基本的AddXXXX方法來綁定實例關係,但是對於大型項目來說,還是挺困難的,大型的項目需要的是通用的註冊,不可能手動添加每個對象的解析關係,這纔是我們面臨的痛點。

  解決方案

  這裏不打算詳解Castle的內容,專注於如何使用Castle實現通用註冊和替換掉原生的IoC容器。

  首先我們需要一個通用的註冊類,是基於我們自定義規則來註冊的,比如說在程序集中滿足繼承ITransientDependency的話我們會給實現類註冊爲Transient的生命週期,ISingletonDependency會給實現類註冊爲Singleton的生命週期。

複製代碼

    /// <summary>
    /// 通過輸入的程序集來註冊滿足約定的所有類
    /// </summary>
    public class BasicConventionalRegistrar
    {
        private readonly WindsorContainer _container = new WindsorContainer();

        /// <summary>
        /// 註冊程序集中滿足約定的類
        /// </summary>
        /// <param name="assemblies"></param>
        /// <returns></returns>
        public WindsorContainer RegisterAssembly(List<Assembly> assemblies)
        {
            foreach (var assembly in assemblies)
            {
                //Transient
                _container.Register(
                    Classes.FromAssembly(assembly)
                           .IncludeNonPublicTypes()
                           .BasedOn<ITransientDependency>()
                           .If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
                           .WithService.Self()
                           .WithService.DefaultInterfaces()
                           .LifestyleTransient()
                );

                //Singleton
                _container.Register(
                    Classes.FromAssembly(assembly)
                           .IncludeNonPublicTypes()
                           .BasedOn<ISingletonDependency>()
                           .If(type => !type.GetTypeInfo().IsGenericTypeDefinition)
                           .WithService.Self()
                           .WithService.DefaultInterfaces()
                           .LifestyleSingleton()
                );
            }
            return _container;
        }
    }

複製代碼

  如何使用?   

  有個這個類,我們需要把各個程序集中滿足條件的類註冊進來,首先需要在NuGet下載Castle.Windsor.MsDependencyInjection包,並且在ConfigureServices方法中修改返回類型爲IServiceProvider,然後通過RegistBasicConventionalRegistrar把各個需要註冊的程序集註冊進來。

  這裏需要非常注意的是,在ASP.NET Core - 依賴注入這篇文章裏面提到ConfigureServices是可以返回一個IServiceProvider對象的,這裏是基於這個返回對象引入了第三方容器替換。

複製代碼

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    services.AddMvc();

    var container = RegistBasicConventionalRegistrar();
  
    //替換容器
    return WindsorRegistrationHelper.CreateServiceProvider(container, services);
}

//通用規則註冊
private WindsorContainer RegistBasicConventionalRegistrar()
{
    var list = new List<Assembly>();
    list.Add(Assembly.GetExecutingAssembly ());
    list.Add(yourProjectAssembly);
    ...
    return BasicConventionalRegistrar.RegisterAssembly(list); 
}

複製代碼

  按程序集註冊  

  上面的代碼我們可以看到,主要是利用了WindsorRegistrationHelper這個類實現註冊到Windsor Castle容器中,且核心方法RegistBasicConventionalRegistrar是基於程序集註冊的。在正式的應用場景中,我們是非常可能需要把多個程序集都按約定來註冊的,個人建議的做法是,在每個需要註冊的程序集中添加一個標誌該程序集的基類,比如說我會在Service這個程序集中添加一個IServiceBase接口,這樣就可以通過typeof(IServiceBase).Assembly獲取到這個程序集進而用來註冊

  按約定進行繼承

  在我們的service業務層(或者任何需要註冊的地方),把需要註冊的類繼承通用註冊接口(生命週期自己選擇)

複製代碼

public interface IPostBlogService : ITransientDependency
{
  Result Post(BlogDto dto);
}

public class PostBlogService : IPostBlogService
{
    public Result Post(BlogDto dto)
  {
    //todo: post this blog
  }
}

複製代碼

  實例解析  

  在任何需要的地方通過構造方法注入(或屬性注入)

複製代碼

[ApiController]
[Route("api/Blog")]
public class BlogController: Controller
{
    public readonly IBlogService _blogService;
    public BlogController(IBlogService blogService)
    {    
       _blogService = blogService;
    }      

  [HttpPost]
    public Result PostBlog(BlogDto dto)
    {
       return _blogService.Post(dto);
    }
}

複製代碼

   這樣就實現了完整的通用註冊解析流程,在實際應用中,個人建議把這些通用解析方法放在基礎設施模塊,因爲這樣不僅本項目可以用,還可以把基礎設施抽離出來作爲基礎框架,通過nuget使用版本控制作爲公司所有項目的通用註冊礎框架。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章