使用 autofac 實現 asp .net core 的屬性注入


使用 autofac 代替 asp .net core 默認的 IOC 容器,可實現屬性注入。
之前的使用方式不受影響。

源碼已開源:
dotnet-campus/Autofac.Annotation: Autofac 擴展,使用 Attribute 進行服務注冊與屬性注入

更豐富的功能,可以看這里:
yuzd/Autofac.Annotation: Autofac extras library for component registration via attributes 用注解來load autofac 擺脫代碼或者xml配置和java的spring的注解注入一樣的體驗

使用效果示例

向容器中注入服務

builder.RegisterType<Counter>().As<ICounter>().InstancePerDependency().AsImplementedInterfaces();

通過屬性獲取服務

[Autowired]  // 這個不是 autofac 自帶的,是自己實現的,可以不要。見后面的詳述。
private ICounter Counter { get; set; }

准備工作

  • nuget 引用
    <PackageReference Include="Autofac" Version="5.2.0" />
    <PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />
  • Program.cs 文件

使用autofac的容器工廠替換系統默認的容器

  • Startup.cs 文件

在 Startup 服務配置中加入控制器替換規則

services.Replace(ServiceDescriptor.Transient<IControllerActivator, ServiceBasedControllerActivator>());

這句的意思:使用 ServiceBasedControllerActivator 替換 DefaultControllerActivator;Controller 默認是由 Mvc 模塊管理的,不在 Ioc 容器中。替換之后,將放在 Ioc 容器中。

在 Startup.cs 添加 public void ConfigureContainer(ContainerBuilder builder) 方法,這個方法會由 autofac 自動調用。
在這個方法中,進行依賴的注入和屬性注入的配置。

        // 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 by the factory.
        public void ConfigureContainer(ContainerBuilder builder)
        {
            builder.RegisterModule<BaseServiceRegisterModule>();
            builder.RegisterModule<PropertiesAutowiredModule>();
        }

在 autofac 中,有一個 Module 的概念,可以分模塊處理依賴的注入。試想,如果所有業務相關的依賴注入代碼,都放在 Startup.cs 這一個文件中,代碼會變得很難看。

這里的示例中,定義了 BaseServiceRegisterModulePropertiesAutowiredModule ,分別寫服務注入的代碼,和屬性注入的配置代碼。

具體實現

下面把后面要說明的四個類都列出來:

    public class BaseServiceRegisterModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // Register your own things directly with Autofac, like:
            builder.RegisterType<Counter>().As<ICounter>().InstancePerDependency().AsImplementedInterfaces();
        }
    }
    public class PropertiesAutowiredModule : Autofac.Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            // 獲取所有控制器類型並使用屬性注入
            var controllerBaseType = typeof(ControllerBase);
            builder.RegisterAssemblyTypes(typeof(Program).Assembly)
                .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
                .PropertiesAutowired(new AutowiredPropertySelector());
        }
    }
    public class AutowiredPropertySelector: IPropertySelector
    {
        public bool InjectProperty(PropertyInfo propertyInfo, object instance)
        {
            return propertyInfo.CustomAttributes.Any(it => it.AttributeType == typeof(AutowiredAttribute));
        }
    }

    [AttributeUsage(AttributeTargets.Property)]
    public class AutowiredAttribute : Attribute
    {
    }

BaseServiceRegisterModule 中,向容器中注入了 ICounter 這個服務。

PropertiesAutowiredModule 中,配置了屬性注入的操作。這里是關鍵了。

            var controllerBaseType = typeof(ControllerBase);
            builder.RegisterAssemblyTypes(typeof(Program).Assembly)
                .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
                .PropertiesAutowired(new AutowiredPropertySelector());

代碼還是挺直白的,需要說明

1 可以看到,屬性注入並不是 autofac 自動 幫我們完成的,得自己寫代碼,使用反射的方式,給哪些類進行屬性注入。
2 在上面的代碼中,只給 ControllerBase 的子類進行了屬性注入。
3 這里在 PropertiesAutowired 方法中,加了一個自定義的 AutowiredPropertySelector

如果沒有給 PropertiesAutowired 添加任何方法參數,則 autofac 會對所有屬性嘗試進行注入,PropertiesAutowired 的方法參數,可以指定屬性選擇器。
在本文的示例中,選擇器的實現是:

        public bool InjectProperty(PropertyInfo propertyInfo, object instance)
        {
            return propertyInfo.CustomAttributes.Any(it => it.AttributeType == typeof(AutowiredAttribute));
        }

也就是要求屬性必須顯式的標明 [Autowired] 這個 Attribute。
我這里這樣做的目的是為了讓代碼看起來更直觀,哪些屬性是自動注入的,哪些不是,一目了然。

最終效果

在依賴注冊上(向容器中添加服務),並沒有變化,還是需要手工寫代碼(在 Startup.cs 或者 Module 中),當然,也可以利用反射,自定義一個 Attribute,然后寫一端代碼自動將其注入到容器中。

在依賴注入上(從容器中獲取服務),這里可以利用屬性進行“自動”注入了。使用起來就是這樣 ↓,比 asp.net core 中只能是構造函數注入,方便了很多。

        [Autowired]
        private ICounter Counter { get; set; }

尾巴

對比 spring 框架,asp.net core 的 IOC 在易用性上,感覺還是弱了不少。不過看到這篇博客:ASP.NET Core 奇淫技巧之偽屬性注入 - 曉晨Master - 博客園
覺得屬性注入不可濫用的說法還是有道理的,會造成依賴關系的隱藏。

參考文章

主要參考文章:
ASP.NETCore 3.0 Autofac替換及控制器屬性注入及全局容器使用 - 情·深 - 博客園

autofac 的官方示例:
autofac/Examples: Example projects that consume and demonstrate Autofac IoC functionality and integration

autofac 文檔:
Welcome to Autofac’s documentation! — Autofac 5.2.0 documentation
歡迎來到 Autofac 中文文檔! — Autofac 4.0 文檔

其它:
ASP.NET Core 奇淫技巧之偽屬性注入 - 曉晨Master - 博客園
.net core2.0下Ioc容器Autofac使用 - 焰尾迭 - 博客園

原文鏈接:
https://www.cnblogs.com/jasongrass/p/13457212.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM