zoukankan      html  css  js  c++  java
  • 使用 autofac 实现 asp .net core 的属性注入

    使用 autofac 代替 asp .net core 默认的 IOC 容器,可实现属性注入。
    之前的使用方式不受影响。

    使用效果示例

    向容器中注入服务

    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

  • 相关阅读:
    UVA 11174 Stand in a Line,UVA 1436 Counting heaps —— (组合数的好题)
    UVA 1393 Highways,UVA 12075 Counting Triangles —— (组合数,dp)
    【Same Tree】cpp
    【Recover Binary Search Tree】cpp
    【Binary Tree Zigzag Level Order Traversal】cpp
    【Binary Tree Level Order Traversal II 】cpp
    【Binary Tree Level Order Traversal】cpp
    【Binary Tree Post order Traversal】cpp
    【Binary Tree Inorder Traversal】cpp
    【Binary Tree Preorder Traversal】cpp
  • 原文地址:https://www.cnblogs.com/jasongrass/p/13457212.html
Copyright © 2011-2022 走看看