• Autofac 依赖注入小知识


    Autofac 依赖注入小知识

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

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

    生命周期分为三种,具体如下

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

    使用说明

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

    dotnet add package Autofac.Extensions.DependencyInjection
    

    在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();
        }
    }
    

    在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();
      }
    }
    

    定义注入实现

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

    注册服务

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

    根据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);
    }
    

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

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

    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.继承接口。

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

    动态代理

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

    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();//启用接口的拦截器。
    

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

    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);
      }
    }
    

    2.注册拦截器。

    // Named registration
    builder.Register(c => new CallLogger(Console.Out))
           .Named<IInterceptor>("log-calls");
    
    // Typed registration
    builder.Register(c => new CallLogger(Console.Out));
    

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

    [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
      }
    }
    

    链接

  • 相关阅读:
    Apache CXF入门
    C++中关于类型转换的问题讨论
    makefile实例(3)-多个文件实例优化
    makefile实例(2)-多个文件实例
    makefile实例(1)-helloworld
    【原创】Linux下编译链接中常见问题总结
    tcpdump命令
    共享内存基础
    关于TCP/UDP缓存
    TCP/UDP常见问题小结
  • 原文地址:https://www.cnblogs.com/igeekfan/p/autofac.html
Copyright © 2020-2023  润新知