1. 引入Nuget包
- Autofac
- Autofac.Extensions.DependencyInjection
2. 修改Program.cs
将默认ServiceProviderFactory
指定为AutofacServiceProviderFactory
- public static IHostBuilder CreateHostBuilder(string[] args) =>
- Host.CreateDefaultBuilder(args)
- .ConfigureWebHostDefaults(webBuilder =>
- {
- webBuilder.UseStartup<Startup>();
- })
- .UseServiceProviderFactory(new AutofacServiceProviderFactory());
3. 修改Startup.cs
添加方法 ConfigureContainer
- public void ConfigureContainer(ContainerBuilder builder)
- {
- // 在这里添加服务注册
- builder.RegisterType<TopicService>();
- }
4. 配置Controller全部由Autofac创建
默认情况下,Controller的参数会由容器创建,但Controller的创建是有AspNetCore框架实现的。要通过容器创建Controller,需要在Startup中配置一下:
- services.Replace(
- ServiceDescriptor
- .Transient<IControllerActivator, ServiceBasedControllerActivator>()
- );
-
- // 或者将Controller加入到Services中,这样写上面的代码就可以省略了
- services.AddControllersWithViews().AddControllersAsServices();
如果需要在Controller中使用属性注入,需要在ConfigureContainer
中添加如下代码
- var controllerBaseType = typeof(ControllerBase);
- builder.RegisterAssemblyTypes(typeof(Program).Assembly)
- .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
- .PropertiesAutowired();
5. 在Controller中使用
- [Route("api/[controller]")]
- [ApiController]
- public class TestController : ControllerBase
- {
- private readonly TopicService _service;
- private readonly IServiceProvider _provider;
-
- public TopicService Service { get; set; }
-
- public TestController(TopicService service, IServiceProvider provider)
- {
- _service = service;
- _provider = provider;
- }
-
- [HttpGet("{id}")]
- public async Task<Result> GetTopics(int id)
- {
- // 构造函数注入
- return await _service.LoadWithPosts(id);
- }
-
- [HttpGet("Get/{id}")]
- public async Task<Result> GetTopics2(int id)
- {
- // 属性注入
- return await Service.LoadWithPosts(id);
- }
- }
6. 使用拦截器
添加Nuget包:Autofac.Extras.DynamicProxy
一、定义一个拦截器类,实现IInterceptor
- public class TestInterceptor : IInterceptor
- {
- public void Intercept(IInvocation invocation)
- {
- Console.WriteLine("你正在调用方法 \"{0}\" 参数是 {1}... ",
- invocation.Method.Name,
- string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
-
- invocation.Proceed();
-
- Console.WriteLine("方法执行完毕,返回结果:{0}", invocation.ReturnValue);
- }
- }
二、修改Startup
的ConfigureContainer
方法
注意:
1、拦截器注册要在使用拦截器的接口和类型之前
2、在类型中使用,仅virtual方法可以触发拦截器
- builder.RegisterType<TestInterceptor>(); // 要先注册拦截器
-
- builder.RegisterAssemblyTypes(typeof(Program).Assembly)
- .AsImplementedInterfaces()
- .EnableInterfaceInterceptors();
-
- var controllerBaseType = typeof(ControllerBase);
- builder.RegisterAssemblyTypes(typeof(Program).Assembly)
- .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
- .PropertiesAutowired() // 允许属性注入
- .EnableClassInterceptors(); // 允许在Controller类上使用拦截器
三、在需要使用拦截器的类或接口上添加描述
[Intercept(typeof(TestInterceptor))]
四、Sample
在接口上添加拦截器,当调用接口的方法时,都会进入拦截器
- public class LogUtil : ILogUtil
- {
- public void Show(string message)
- {
- Console.WriteLine(message);
- }
- }
-
- [Intercept(typeof(TestInterceptor))]
- public interface ILogUtil
- {
- void Show(string message);
- }
在Controller上使用拦截器
- [Intercept(typeof(TestInterceptor))]
- [Route("api/[controller]")]
- [ApiController]
- public class TestController : ControllerBase
- {
- private readonly TopicService _service;
- private readonly IServiceProvider _provider;
- private readonly ILogUtil _log;
-
- public TopicService Service { get; set; }
-
- public TestController(TopicService service, IServiceProvider provider, ILogUtil log)
- {
- _service = service;
- _provider = provider;
- _log = log;
- }
-
- // 会触发拦截器
- [HttpGet("{id}")]
- public virtual async Task<Result> GetTopics(int id)
- {
- // 构造函数注入
- return await _service.LoadWithPosts(id);
- }
-
- // 不会触发拦截器
- [HttpGet("Get/{id}")]
- public async Task<Result> GetTopics2(int id)
- {
- return await Service.LoadWithPosts(id);
- }
-
- // 会由_log触发拦截器
- [HttpGet("Get2")]
- public string GetTopics3()
- {
- _log.Show("abc");
- return "Hello World";
- }
-
- // 会触发拦截器2次
- [HttpGet("Get2")]
- public virtual string GetTopics4()
- {
- _log.Show("abc");
- return "Hello World";
- }
- }
7. 一个接口多个实现
- // 1、需要指定键值 是一个Object类型
- // 2、注册服务使用方法Keyed 参数为指定的键值中的值 (每一个服务的实现和键值要一一对应起来,这里不能重复)
- // 3、获取服务: 直接通过ResolveKeyed() 获取服务无,方法需要传入 指定对应的键值
- // 先获取一个IIndex,再通过IInex 索引来获取服务的实例
-
- containerBuilder.RegisterType<TestServiceD>().Keyed<ITestServiceD>(DeviceState.TestServiceD);
- containerBuilder.RegisterType<TestServiceD_One>().Keyed<ITestServiceD>(DeviceState.TestServiceD_One);
- containerBuilder.RegisterType<TestServiceD_Two>().Keyed<ITestServiceD>(DeviceState.TestServiceD_Two);
- containerBuilder.RegisterType<TestServiceD_Three>().Keyed<ITestServiceD>(DeviceState.TestServiceD_Three);
-
- // 为不同的实现指定名称,这个比较简单,推荐
- containerBuilder.RegisterType<TestServiceD_Three>().Named<ITestServiceD>("three");
-
- IContainer container = containerBuilder.Build();
-
- IIndex<DeviceState, ITestServiceD> index = container.Resolve<IIndex<DeviceState, ITestServiceD>>();
- ITestServiceD testServiceD= index[DeviceState.TestServiceD];
- ITestServiceD TestServiceD_One = index[DeviceState.TestServiceD_One];
- ITestServiceD TestServiceD_Two = index[DeviceState.TestServiceD_Two];
- ITestServiceD TestServiceD_Three = index[DeviceState.TestServiceD_Three];
-
- // 根据名称解析
- var t2 = container.ResolveNamed<ITestServiceD>("three");
- Console.WriteLine("abc");
上面的做法在客户端或者之前的MVC项目中可以这样用。但在AspNetCore3.0中,我们似乎根本拿不到IContainer,所以不能手动Resolve指定的实现,下面是我自己摸索的办法:
- public interface ITestUtil
- {
- void Show(string content);
- }
-
- public class TestUtil1 : ITestUtil
- {
- public void Show(string content)
- {
- Console.WriteLine("TestUtil1:" + content);
- }
- }
-
- public class TestUtil2 : ITestUtil
- {
- public void Show(string content)
- {
- Console.WriteLine($"TestUtil2:{content}");
- }
- }
别忘了在Startup中注册服务
- builder.RegisterType<TestUtil1>().As<ITestUtil>();
- builder.RegisterType<TestUtil2>().As<ITestUtil>();
-
- // 或者这样做,如果程序级中的类型实现了某个接口,
- // 会自动把该类型注册为接口的实现,
- // 这个方法比较狠,个人感觉还是慎用的好
- builder.RegisterAssemblyTypes(this.GetType().Assembly)
- .AsImplementedInterfaces()
- .PropertiesAutowired();
- // 默认情况下,构造函数注入和属性注入的结果都是最后注册的那个实现,
- // 也就是TestUtil2
- private readonly ITestUtil _util;
-
- public HomeController(ITestUtil util, IServiceProvider provider)
- {
- _util = util;
- // 如果知道注册的顺序,可以用这种方式,
- // 第一个注册是TestUtil1,所以这里返回TestUtil1
- var util1 = provider.GetServices<ITestUtil>().ElementAtOrDefault(0);
- util1?.Show("指定注册为ITestUtil的第一个实现");
-
- // 一般情况下用这种方式,指定成具体的类型 TestUtil1
- var utilFirst = provider.GetServices<ITestUtil>()
- .SingleOrDefault(t => t.GetType() == typeof(TestUtil1));
- util1?.Show("指定名称为TestUtil的实现");
- }