Skip to content
.NET 开发者指北.NET 开发者指北
CMS
.NET指北
FreeKit
Docker
关于
博客
github icon
    • 文件上传系统
      • 行为日志
        • 基于 RBAC 实现权限设计
          • 方法级别的权限控制(API级别)
            • LinCms.Scaffolding 代码生成器
              • Autofac 依赖注入小知识
                • 控制反转/依赖注入 IOC/DI
                  • Autofac 生命周期
                  • 使用说明
                    • 动态代理
                      • 同步拦截
                      • 链接
                      • ASP.NETCore 下 FreeSql 的仓储事务

                      Autofac 依赖注入小知识

                      calendar icon2021年12月12日timer icon大约 5 分钟word icon约 1384 字

                      此页内容
                      • 控制反转/依赖注入 IOC/DI
                        • Autofac 生命周期
                      • 使用说明
                        • 动态代理
                        • 同步拦截
                      • 链接

                      # Autofac 依赖注入小知识

                      # 控制反转/依赖注入 IOC/DI

                      依赖接口而不依赖于实现,是面向对象的六大设计原则(SOLID)之一。即依赖倒置原则(Dependence Inversion Principle)

                      aspnetcore中的生命周期通常分为三种,具体如下

                      • Singleton 单例(全局唯一实例)
                      • Scoped 范围 (在同一个生命周期内是同一个实例)
                      • Transient 瞬时(每次请求都是一个新的实例)

                      Autofac命名有一些不同,但功能是一样的。

                      • Instance Per Dependency瞬时的
                      • Single Instance 单例
                      • Instance Per Lifetime Scope 作用域

                      # Autofac 生命周期

                      • Instance Per Dependency 瞬时
                      builder.RegisterType<Worker>().InstancePerDependency();
                      // 如果不指定,默认就是InstancePerDependency
                      builder.RegisterType<Worker>();
                      
                      1
                      2
                      3
                      • Instance Per Lifetime Scope 作用域
                      // 在同一个作用域中获得的是相同实例,在不同作用域获得的是不同实例
                      builder.RegisterType<Worker>().InstancePerLifetimeScope();
                      
                      1
                      2
                      • Single Instance 单例
                      // 注册Worker类为SIngle Instance(单例),每次获取均返回同一个实例
                      builder.RegisterType<Worker>().SingleInstance();
                      
                      1
                      2

                      # 使用说明

                      创建ASP.NET Core 3.0+的项目,并安装Autofac包

                      dotnet add package Autofac.Extensions.DependencyInjection
                      
                      1

                      在 Program 中 Host 主机指定 .UseServiceProviderFactory(new AutofacServiceProviderFactory()).

                      UseServiceProviderFactory 调用 Autofac 提供程序,附加到通用宿主机制。

                      public class Program
                      {
                          public static void Main(string[] args)
                          {
                              var host = Host.CreateDefaultBuilder(args)
                      +       .UseServiceProviderFactory(new AutofacServiceProviderFactory())
                              .ConfigureWebHostDefaults(webHostBuilder => {
                                  webHostBuilder
                                  .UseContentRoot(Directory.GetCurrentDirectory())
                                  .UseIISIntegration()
                                  .UseStartup<Startup>();
                              })
                              .Build();
                      
                              host.Run();
                          }
                      }
                      
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17

                      在 StartUp 中配置

                      public class Startup
                      {
                        public Startup(IConfiguration configuration)
                        {
                          this.Configuration = configuration;
                        }
                      
                        public IConfiguration Configuration { get; private set; }
                      
                      +  public ILifetimeScope AutofacContainer { get; private set; }
                      
                        public void ConfigureServices(IServiceCollection services)
                        {
                          services.AddOptions();
                        }
                      
                        // 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)
                        {
                          // Register your own things directly with Autofac here. Don't
                          // call builder.Populate(), that happens in AutofacServiceProviderFactory
                          // for you.
                      +    builder.RegisterModule(new MyApplicationModule());
                        }
                      
                        public void Configure(
                          IApplicationBuilder app,
                          ILoggerFactory loggerFactory)
                        {
                      +   this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
                      
                          loggerFactory.AddConsole(this.Configuration.GetSection("Logging"));
                          loggerFactory.AddDebug();
                          app.UseMvc();
                        }
                      }
                      
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      18
                      19
                      20
                      21
                      22
                      23
                      24
                      25
                      26
                      27
                      28
                      29
                      30
                      31
                      32
                      33
                      34
                      35
                      36
                      37
                      38
                      39

                      定义注入实现

                      public class MyApplicationModule : Autofac.Module
                      {
                          protected override void Load(ContainerBuilder builder)
                          {
                            builder.RegisterType<HttpContextAccessor>().As<IHttpContextAccessor>().SingleInstance();
                          }
                      }
                      
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      • 注册泛型仓储
                      builder.RegisterGeneric(typeof(AuditBaseRepository<>)).As(typeof(IAuditBaseRepository<>)).InstancePerLifetimeScope();
                      builder.RegisterGeneric(typeof(AuditBaseRepository<,>)).As(typeof(IAuditBaseRepository<,>)).InstancePerLifetimeScope();
                      
                      1
                      2
                      • 一个接口多个实现,使用 Named,区分、参数为字符串即可。

                      注册服务

                      builder.RegisterType<IdentityServer4Service>().Named<ITokenService>(typeof(IdentityServer4Service).Name).InstancePerLifetimeScope();
                      builder.RegisterType<JwtTokenService>().Named<ITokenService>(typeof(JwtTokenService).Name).InstancePerLifetimeScope();
                      
                      1
                      2

                      根据 Name 获取哪个服务

                      private readonly ITokenService _tokenService;
                      public AccountController(IComponentContext componentContext, IConfiguration configuration)
                      {
                          bool isIdentityServer4 = configuration.GetSection("Service:IdentityServer4").Value?.ToBoolean() ?? false;
                          _tokenService = componentContext.ResolveNamed<ITokenService>(isIdentityServer4 ? typeof(IdentityServer4Service).Name : typeof(JwtTokenService).Name);
                      }
                      
                      1
                      2
                      3
                      4
                      5
                      6

                      可通过 appsettings.json 中配置,可决定是哪个服务

                        "Service": {
                          "IdentityServer4": false
                        }
                      
                      1
                      2
                      3
                      • 基于接口的注入

                      AsImplementedInterfaces Specifies that a type from a scanned assembly is registered as providing all of its implemented interfaces. 指定将扫描程序集中的类型注册为提供其所有实现的接口。

                      根据接口ITransientDependency可以得到有哪些类继承了此接口,并判断是类,不是抽象类,不是泛型。

                      所有继承类接口的类,将以接口的方式自动注入实例。可直接使用接口即可。

                      • InstancePerDependency 瞬时 (每次请求都是一个新的实例)
                      • InstancePerLifetimeScope 范围(在同一个生命周期内是同一个实例)
                      • SingleInstance 单例(全局唯一实例)
                          public class DependencyModule : Autofac.Module
                          {
                              protected override void Load(ContainerBuilder builder)
                              {
                                  Assembly[] currentAssemblies = AppDomain.CurrentDomain.GetAssemblies().Where(r => r.FullName.Contains("LinCms.")).ToArray();
                      
                                  //每次调用,都会重新实例化对象;每次请求都创建一个新的对象;
                                  Type transientDependency = typeof(ITransientDependency);
                                  builder.RegisterAssemblyTypes(currentAssemblies)
                                      .Where(t => transientDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract && !t.IsGenericType)
                                      .AsImplementedInterfaces().InstancePerDependency();
                      
                                  //同一个Lifetime生成的对象是同一个实例
                                  Type scopeDependency = typeof(IScopedDependency);
                                  builder.RegisterAssemblyTypes(currentAssemblies)
                                      .Where(t => scopeDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract && !t.IsGenericType)
                                      .AsImplementedInterfaces().InstancePerLifetimeScope();
                      
                                  //单例模式,每次调用,都会使用同一个实例化的对象;每次都用同一个对象;
                                  Type singletonDependency = typeof(ISingletonDependency);
                                  builder.RegisterAssemblyTypes(currentAssemblies)
                                      .Where(t => singletonDependency.GetTypeInfo().IsAssignableFrom(t) && t.IsClass && !t.IsAbstract &&!t.IsGenericType)
                                      .AsImplementedInterfaces().SingleInstance();
                      
                              }
                          }
                      
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      18
                      19
                      20
                      21
                      22
                      23
                      24
                      25
                      26

                      如果不写继承,如何批量注入呢。 1.类名有规则 2.基于特殊标签 3.继承接口。

                      • 类名有规则 比如仓储后缀,全是Repository,其中Assembly为仓储的实现所在程序集。将自动注入所有的仓储,仓储必须有接口。
                          Assembly assemblysRepository = Assembly.Load("LinCms.Infrastructure");
                          builder.RegisterAssemblyTypes(assemblysRepository)
                                  .Where(a => a.Name.EndsWith("Repository"))
                                  .AsImplementedInterfaces()
                                  .InstancePerLifetimeScope();
                      
                      1
                      2
                      3
                      4
                      5
                      • 注入服务后就执行一段逻辑
                      builder.RegisterType<MigrationStartupTask>().SingleInstance();
                      builder.RegisterBuildCallback(async (c) => await c.Resolve<MigrationStartupTask>().StartAsync());
                      
                      1
                      2

                      # 动态代理

                      dotnet add package Autofac.Extras.DynamicProxy
                      dotnet add package Castle.Core.AsyncInterceptor
                      
                      1
                      2
                      • 服务注册

                      AOP+属性注入+以后缀为 Service 的服务实现,注入 Scope 范围的生命周期+启用接口的拦截器。

                      • 使用EnableInterfaceInterceptors创建执行拦截的接口代理,
                      • 使用EnableClassInterceptors() 动态对子类进行重写, 执行 virtual 方法的拦截
                      builder.RegisterType<UnitOfWorkInterceptor>();
                      builder.RegisterType<UnitOfWorkAsyncInterceptor>();
                      
                      
                      List<Type> interceptorServiceTypes = new List<Type>()
                      {
                          typeof(UnitOfWorkInterceptor),
                      };
                      
                      Assembly servicesDllFile = Assembly.Load("LinCms.Application");
                      builder.RegisterAssemblyTypes(servicesDllFile)
                          .Where(a => a.Name.EndsWith("Service") && !a.IsAbstract && !a.IsInterface && a.IsPublic)
                          .AsImplementedInterfaces()//接口注入
                          .InstancePerLifetimeScope()//生命周期:范围
                          .PropertiesAutowired()// 属性注入
                          .InterceptedBy(interceptorServiceTypes.ToArray())//声明拦截器
                          .EnableInterfaceInterceptors();//启用接口的拦截器。
                      
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17

                      这二个类,请参考如下代码

                      • 同步:UnitOfWorkInterceptor.cs https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Middleware/UnitOfWorkInterceptor.csopen in new window
                      • 异步拦截:UnitOfWorkAsyncInterceptor.cs https://github.com/luoyunchong/lin-cms-dotnetcore/blob/master/src/LinCms.Web/Middleware/UnitOfWorkInterceptor.csopen in new window

                      Autofac.Extras.DynamicProxy依赖 Castle.Core,即只支持同步方法的拦截。 异步方法的拦截需要安装包:Castle.Core.AsyncInterceptor。

                      • 异步方法,分为有/无返回值:async Task RunAsync(),asyn Task<Result> RunAsync()
                      • 同步方法:void Run(),Result Run()

                      # 同步拦截

                      1.定义拦截器

                      public class CallLogger : IInterceptor
                      {
                        TextWriter _output;
                      
                        public CallLogger(TextWriter output)
                        {
                          _output = output;
                        }
                      
                        public void Intercept(IInvocation invocation)
                        {
                          _output.Write("Calling method {0} with parameters {1}... ",
                            invocation.Method.Name,
                            string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
                      
                          invocation.Proceed();
                      
                          _output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
                        }
                      }
                      
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      18
                      19
                      20

                      2.注册拦截器。

                      // Named registration
                      builder.Register(c => new CallLogger(Console.Out))
                             .Named<IInterceptor>("log-calls");
                      
                      // Typed registration
                      builder.Register(c => new CallLogger(Console.Out));
                      
                      1
                      2
                      3
                      4
                      5
                      6

                      将拦截器与要拦截的类型 关联

                      [Intercept(typeof(CallLogger))]
                      public class First
                      {
                        public virtual int GetValue()
                        {
                          // Do some calculation and return a value
                        }
                      }
                      
                      // This attribute will look for a NAMED
                      // interceptor registration:
                      [Intercept("log-calls")]
                      public class Second
                      {
                        public virtual int GetValue()
                        {
                          // Do some calculation and return a value
                        }
                      }
                      
                      1
                      2
                      3
                      4
                      5
                      6
                      7
                      8
                      9
                      10
                      11
                      12
                      13
                      14
                      15
                      16
                      17
                      18
                      19

                      # 链接

                      • 官网 https://autofac.org/
                      • GitHub https://github.com/autofac/Autofac
                      • 文档 https://autofac.readthedocs.io/en/latest/
                      edit icon在 GitHub 上编辑此页open in new window
                      上次编辑于: 2022/8/15 16:21:26
                      贡献者: igeekfan,luoyunchong
                      上一页
                      LinCms.Scaffolding 代码生成器
                      下一页
                      ASP.NETCore 下 FreeSql 的仓储事务
                      MIT Licensed | Copyright © 2021-present luoyunchong
                      苏ICP备16046457号-1

                      该应用可以安装在你的 PC 或移动设备上。这将使该 Web 应用程序外观和行为与其他应用程序相同。它将在出现在应用程序列表中,并可以固定到主屏幕,开始菜单或任务栏。此 Web 应用程序还将能够与其他应用程序和你的操作系统安全地进行交互。

                      详情