目的:记录Autofac在Asp.Net Core项目中替代原生IOC简单用法
Nuget包
1、Autofac
2、Autofac.Extension.DependencyInjection
一、Program中新增Autofac注册工厂
添加引用:using Autofac.Extensions.DependencyInjection;
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
})
.UseServiceProviderFactory(new AutofacServiceProviderFactory());
二、Startup类中添加ConfigureContainer方法
在ConfigureContainer方法中使用ContainerBuilder进行服务的抽象与实现注册
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
//ServiceA接口实现,IServiceA服务接口
containerBuilder.RegisterType<ServiceA>().As<IServiceA>();
}
三、 Autofac生命周期
声明声明周期在注册抽象与实现方法后继续指定。例如:
-单例生命周期:
//InstancePerDependency:瞬时生命周期,即每次获取实例都是全新的实例
containerBuilder.RegisterType<ServiceA>().As<IServiceA>().InstancePerDependency();
//生命周期说明:
IContainer container = containerBuilder.Build();
IServiceA serviceA = container.Resolve<IServiceA>();//获取服务
IServiceA serviceA2 = container.Resolve<IServiceA>();//获取服务
if (object.ReferenceEquals(serviceA, serviceA2))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:false
-单例生命周期:
//SingleInstance:单例生命周期,即一个进程中获取的N个实例都是同一个实例
containerBuilder.RegisterType<ServiceA>().As<IServiceA>().SingleInstance();
//生命周期说明:
IContainer container = containerBuilder.Build();
IServiceA serviceA = container.Resolve<IServiceA>();//获取服务
IServiceA serviceA2 = container.Resolve<IServiceA>();//获取服务
if (object.ReferenceEquals(serviceA, serviceA2))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:true
-作用域生命周期:
//InstancePerLifetimeScope:范围内生命周期,即同一个Lifetime生成的对象是同一个实例
containerBuilder.RegisterType<ServiceA>().As<IServiceA>().InstancePerLifetimeScope();
//生命周期说明:
IServiceA AreaServiceA = null;
IServiceA AreaServiceB = null;
IContainer container = containerBuilder.Build();
//区域中的serviceA与serviceA2实例是同一个
using (var scope = container.BeginLifetimeScope())
{
IServiceA serviceA = container.Resolve<IServiceA>();//获取服务
IServiceA serviceA2 = container.Resolve<IServiceA>();//获取服务
if (object.ReferenceEquals(serviceA, serviceA2))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:true
AreaServiceA = serviceA;
}
//区域中的serviceA与serviceA2实例是同一个
using (var scope = container.BeginLifetimeScope())
{
IServiceA serviceA = container.Resolve<IServiceA>();//获取服务
IServiceA serviceA2 = container.Resolve<IServiceA>();//获取服务
if (object.ReferenceEquals(serviceA, serviceA2))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:true
AreaServiceB = serviceA;
}
//两个不同区域内声明的服务实例不是同一个
if (object.ReferenceEquals(AreaServiceA, AreaServiceB))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:false
-指定标识作用域生命周期:
//InstancePerMatchingLifetimeScope("指定一个标识"):标识的生命周期域,即在指定了标识的Lifetime创建的范围内的实例都是同一个实例。
containerBuilder.RegisterType<ServiceA>().As<IServiceA>().InstancePerMatchingLifetimeScope("");
//生命周期说明:
IServiceA AreaServiceA = null;
IServiceA AreaServiceB = null;
IContainer container = containerBuilder.Build();
//区域中的serviceA与serviceA2实例是同一个
using (var scope = container.BeginLifetimeScope("AAAA"))
{
IServiceA serviceA = container.Resolve<IServiceA>();//获取服务
IServiceA serviceA2 = container.Resolve<IServiceA>();//获取服务
if (object.ReferenceEquals(serviceA, serviceA2))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:true
AreaServiceA = serviceA;
using (var scope2 = container.BeginLifetimeScope())
{
IServiceA serviceA3 = scope.Resolve<IServiceA>();
if (object.ReferenceEquals(serviceA, serviceA3))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:true,说明标识的生命周期域AAAA内的实例都是同一个
}
}
//区域中的serviceA与serviceA2实例是同一个
using (var scope = container.BeginLifetimeScope("BBBB"))
{
IServiceA serviceA = container.Resolve<IServiceA>();//获取服务
IServiceA serviceA2 = container.Resolve<IServiceA>();//获取服务
if (object.ReferenceEquals(serviceA, serviceA2))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:true
AreaServiceB = serviceA;
}
//两个不同区域内声明的服务实例不是同一个
if (object.ReferenceEquals(AreaServiceA, AreaServiceB))
{
Console.WriteLine("true");
}
else
{
Console.WriteLine("false");
}
//结果输出:false,说明两个标识的生命周期域不同的作用域中获取的服务实例不是同一个
四、Autofac注入方式
1、构造函数注入
2、属性注入
3、方法注入
首先都需要先在Startup中的ConfigureContainer方法中注册服务的抽象与实现,例如:
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
//构造函数注入
containerBuilder.RegisterType<ServiceA().As<IServiceA().InstancePerDependency();
//属性注入.PropertiesAutowired()
containerBuilder.RegisterType<ServiceB().As<IServiceB().PropertiesAutowired();
//方法注入.OnActivated(),中获取单例调用方法SetMethod获取服务实例
containerBuilder.RegisterType<ServiceD>().OnActivated(e=>e.Instance.SetMethod(e.Context.Resolve<IServiceA>())).As<IServiceD>();
}
//-----以下为空接口的实现类
public class ServiceA : IServiceA
{
public ServiceA()
{
}
}
public class ServiceB : IServiceB
{
public IServiceA _iServiceA { get; set; }
public IServiceB _iServiceB { get; set; }
public TestServiceD()
{
}
}
public class ServiceD : IServiceD
{
public IServiceA _iServiceA = null;
public void SetMethod(IServiceA iServiceA)
{
_iServiceA = iServiceA;
}
public ServiceD(IServiceA iServiceA)
{
_iServiceA = iServiceA;
}
}
五、控制器使用Autofac
1、ConfigureServices中设置控制器实例化由Autofac来创建
需要添加引用:
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.AspNetCore.Mvc.Controllers;
public void ConfigureServices(IServiceCollection services)
{
//省略一堆注入
#region 指定控制器实例让容器来创建
services.Replace(ServiceDescriptor.Transient<IControllerActivator,ServiceBasedControllerActivator>());
#endregion
}
2、ConfigureContainer方法获取所有的控制器,并实例化控制器所有需要的组件
需要添加引用:using Microsoft.AspNetCore.Mvc;
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<ServiceA>().As<IServiceA>().InstancePerDependency();
#region 注册所有控制器,实例化需要的所有组件
Type[] controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
.Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
containerBuilder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired(new CustomPropertySelector());
#endregion
}
六、控制器中需要使用Autofac的属性注入
实例化控制器所有需要的组件后添加.PropertiesAutowired(),该方法需要指定需要去实现指定哪些属性,因此需要为需要的属性添加特性进行标识,再通过选择器进行过滤,将添加了特性的属性注入实现。
1、创建特性类
/// <summary>
/// 自定义属性特性
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class CustomPropertyAttribute : Attribute
{
}
2、创建选择器
需要添加引用
using Autofac.Core;
using System.Linq;
using System.Reflection;
如果特性类不在同一目录需要添加特性类的引用
/// <summary>
/// 自定义属性选择器
/// </summary>
public class CustomPropertySelector : IPropertySelector
{
public bool InjectProperty(PropertyInfo propertyInfo, object instance)
{
return propertyInfo.CustomAttributes.Any(it => it.AttributeType == typeof(CustomPropertyAttribute));
}
}
3、控制器中添加接口属性并标记特性
public class FourController : Controller
{
/// <summary>
/// 属性注入
/// </summary>
[CustomPropertyAttribute]
private IServiceA serviceAP { get; set; }
public FourController()
{
}
public IActionResult Index()
{
return View();
}
}
七、一个接口多个实现注入(构造函数注入)
1、存在两个实现ServiceA、ServiceU都继承接口IServiceA
2、将IServiceA采用注册资源的方式注册
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource(t => t.IsAssignableTo<IServiceA>()));
#region 注册所有控制器,实例化需要的所有组件
Type[] controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
.Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
containerBuilder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired(new CustomPropertySelector());
#endregion
}
3、构造函数注入的是接口的实现类不是注入抽象接口
public class FourController : Controller
{
private readonly ServiceA _ServiceA = null;
private readonly ServiceU _ServiceU = null;
public FourController(ServiceA serviceA, ServiceU serviceU)
{
_ServiceA = serviceA;
_ServiceU = serviceU;
}
public IActionResult Index()
{
return View();
}
}
如果也需要注入接口,则 在ConfigureContainer中需要添加接口的注册
public void ConfigureContainer(ContainerBuilder containerBuilder)
{
containerBuilder.RegisterType<TestServiceA>().As<ITestServiceA>().InstancePerDependency();
//写法一
containerBuilder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource(t => t.IsAssignableTo<IServiceA>()));
//写法二,将之提取成module进行注册
containerBuilder.RegisterModule<AutufacModule>();
#region 注册所有控制器,实例化需要的所有组件
Type[] controllersTypesInAssembly = typeof(Startup).Assembly.GetExportedTypes()
.Where(type => typeof(ControllerBase).IsAssignableFrom(type)).ToArray();
containerBuilder.RegisterTypes(controllersTypesInAssembly).PropertiesAutowired(new CustomPropertySelector());
#endregion
}
封装Module
需要引用:
using Autofac;
using Autofac.Features.ResolveAnything;
添加接口层引用
public class AutufacModule:Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource(t => t.IsAssignableTo<ITestServiceA>()));
}
}
控制器实现
public class FourController : Controller
{
private readonly ServiceA _ServiceA = null;
private readonly ServiceU _ServiceU = null;
private readonly IServiceA _IServiceA = null;
public FourController(ServiceA serviceA, ServiceU serviceU, IServiceA iServiceA)
{
_ServiceA = ServiceA;
_ServiceU = ServiceU;
_IService = iServiceA;
}
public IActionResult Index()
{
return View();
}
}
八、使用Autofac上下文实现一接口多实现注入
1、添加抽象与实现使用Named注册
containerBuilder.RegisterType<ServiceA>().Named<IServiceA>("ServiceA").InstancePerDependency();
containerBuilder.RegisterType<ServiceU>().Named<IServiceA>("ServiceU").InstancePerDependency();
2、控制器通过Autofac上下文获取服务
//需要添加属性注入特性类引用
public class FiveController : Controller
{
//Autofac上下文
private readonly IComponentContext _componentContext = null;
public FiveController(IComponentContext componentContext)
{
_componentContext = componentContext;
}
public IActionResult Index()
{
//通过上下文获取服务
IServiceA serviceA = _componentContext.ResolveNamed<IServiceA>("ServiceA");
IServiceA serviceA1 = _componentContext.ResolveNamed<IServiceA>("ServiceU");
return View();
}
}
3、Autofac上下文也能够用作属性注入
public class FiveController : Controller
{
/// <summary>
/// Autofac上下文属性注入
/// </summary>
[CustomPropertyAttribute]
private IComponentContext componentContextP { get; set; }
//Autofac上下文
private readonly IComponentContext _componentContext = null;
public FiveController( IComponentContext componentContext)
{
_componentContext = componentContext;
}
public IActionResult Index()
{
ITestServiceA serviceA = _componentContext.ResolveNamed<IServiceA>("ServiceA");
ITestServiceA serviceA1 = _componentContext.ResolveNamed<IServiceA>("ServiceU");
ITestServiceA serviceAP = componentContextP.ResolveNamed<IServiceA>("ServiceA");
ITestServiceA serviceAP1 = componentContextP.ResolveNamed<IServiceA>("ServiceU");
return View();
}
}