使用Autofac替换Asp.net core内置的依赖注入容器

发布时间 2023-07-06 14:28:34作者: shanawdj

Autofac是一个依赖注入容器。Autofac 和其他容器的不同之处是它和 C# 语言的结合非常紧密,在使用过程中对你的应用的侵入性几乎为零,更容易与第三方的组件集成。

通常情况下,Asp.net core内置的依赖注入实现已经足够用了,但Autofac确实提供了更多强大的功能,比如基于属性的注入,动态代理的AOP等。

准备工作

首先,安装两个nuget包:

  • Autofac.Extensions.DependencyInjection
  • Autofac.Extras.DynamicProxy

准备几个服务类:

public interface IMyService
{
    void Show();
}

public class MyService : IMyService
{
    public void Show()
    {
        Console.WriteLine($"MyService:{GetHashCode()}");
    }
}

public class MyService2 : IMyService
{
    public MyNameService NameService { get; set; }

    public void Show()
    {
        Console.WriteLine($"MyService2:{GetHashCode()},NameService是否为空:{NameService==null}");
    }
}

public class MyNameService
{
}

然后来认识一个接口:

public interface IServiceProviderFactory<TContainerBuilder>

这个接口是一个指定服务提供者的一个抽象泛型接口,所有的第三方依赖注入容器想在asp.net core中使用都需要实现它。我们自己也可以用它来实现自定义的依赖注入容器。

Autofac实现这个接口的类AutofacServiceProviderFactory,我们需要用它来替换内置的实现:

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());

//这里注册服务
builder.Host.ConfigureContainer<ContainerBuilder>(builder =>
{
    
});

构造函数注入

注册:

//不指定默认是瞬时服务
builder.RegisterType<MyService>().As<IMyService>();

//单例
builder.RegisterType<MyService>().As<IMyService>().SingleInstance();

//域
builder.RegisterType<MyService>().As<IMyService>().InstancePerLifetimeScope();

autofac的生命周期作用域相对更加丰富,后续可以往后探索

使用:

[ApiController]
[Route("[controller]/[action]")]
public class MyController : ControllerBase
{
    private readonly IMyService _myService;

    public MyController(IMyService myService)
    {
        _myService = myService;
    }
}

属性注入

注册:

builder.RegisterType<MyService>().As<IMyService>().PropertiesAutowired();

使用:

[ApiController]
[Route("[controller]/[action]")]
public class MyController : ControllerBase
{
    public IMyService MyService { get; set; }
}

如果只是按照上面的流程操作,应该会是这样一个结果:构造函数注入成功,但是属性注入失败。原因如下:此时控制器本身的实例(以及它的处理)是由框架创建和拥有的,而不是由Autofac容器所有。构造函数的依赖性会有IServiceProvider解决,其他autofac的高级功能就无法使用了。此时应该:

builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory());           
builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder =>
        {
            containerBuilder.RegisterType<MyService>().As<IMyService>().PropertiesAutowired();

            var controllerBaseType = typeof(ControllerBase);
            containerBuilder.RegisterAssemblyTypes(typeof(Program).Assembly)
            .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
            .PropertiesAutowired();
        });

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

还有更多用法后续用到再更新。。