zoukankan      html  css  js  c++  java
  • 在AspNetCore3.0中使用Autofac

    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);
        }
    }
    
    二、修改StartupConfigureContainer方法

    注意:

    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的实现");
    }
    

    补充:获取容器 IContainer

    下面是官方推荐的方式,定义一个继承自Autofac.Module的类,重写其Load方法,将Startup.cs中ConfigurationContainer方法中的代码全部移至Load的方法内,
    请注意该 RmesAutoFacModule 类中定义了一个静态字段 _container,用于接收注册完成后的Container容器。
    Load 方法的最后一行,builder将容器传出,我们使用 _container 接收到,后面就可以直接使用 RmesAutoFacModule.GetContainer() 获取容器了。

    public class RmesAutoFacModule : Module
    {
        private static IContainer _container;
    
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterAssemblyTypes(Assembly.GetExecutingAssembly())
                .AsImplementedInterfaces()
                .EnableInterfaceInterceptors();
    
            var controllerBaseType = typeof(ControllerBase);
            builder.RegisterAssemblyTypes(typeof(Program).Assembly)
                .Where(t => controllerBaseType.IsAssignableFrom(t) && t != controllerBaseType)
                .PropertiesAutowired()      // 允许属性注入
                .EnableClassInterceptors(); // 允许在Controller类上使用拦截器
    
            // 手动高亮
            builder.RegisterBuildCallback(container => _container = container);
        }
    
        public static IContainer GetContainer()
        {
            return _container;
        }
    }
    
  • 相关阅读:
    window.open打开新窗体并用post方式传参
    开发环境搭建问题及解决
    JavaScript高级程序设计(二)
    JavaScript高级程序设计(一)
    JS 小技巧整理
    Windows7版本了解
    安装mysql8.0.17时候报错1251-Client does not support authentication protocol requested by server; consider upgrading MySQL client
    idea中gradle的springboot的项目热部署
    idea多行注释缩进
    idea的类头注释和方法注释的编辑
  • 原文地址:https://www.cnblogs.com/diwu0510/p/11562248.html
Copyright © 2011-2022 走看看