Skip to content

使用 autofac 实现 asp .net core 的属性注入

Published: at 12:21

使用 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; }

准备工作

<PackageReference Include="Autofac" Version="5.2.0" />
<PackageReference Include="Autofac.Extensions.DependencyInjection" Version="6.0.0" />

使用autofac的容器工厂替换系统默认的容器

在 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使用 - 焰尾迭 - 博客园

如果需要更多更高级的功能,可以直接使用这个库:

yuzd/Autofac.Annotation: DI容器,依赖注入,AOP,动态代理等用注解来load autofac 摆脱代码或者xml配置和java的spring的注解注入一样的体验


原文链接: https://blog.jgrass.cc/posts/autofac-property-inject/

本作品采用 「署名 4.0 国际」 许可协议进行许可,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接。