当前位置 博文首页 > 不问前世:重新整理 .net core 实践篇————依赖注入应用之援

    不问前世:重新整理 .net core 实践篇————依赖注入应用之援

    作者:不问前世 时间:2021-05-29 18:20

    前言

    介绍第三方依赖注入框架Autofac,看看为我们解决什么问题。

    下面介绍4个点:

    1. 命名注册

    2. 属性注册

    3. aop 注入

    4. 子容器命名

    正文

    为什么我们需要使用第三方框架?第三方框架为我们做了什么?第三方框架扩展了哪一个部分?

    这里主要介绍一下Autofac。
    Autofac 主要是替换了我们ServiceProviderFactory 这个东西。

    public interface IServiceProviderFactory<IContainerBuilder>
    

    我们使用的时候这样用:

    Host.CreateDefaultBuilder(args).UseServiceProviderFactory(new AutofacServiceProviderFactory())
    .ConfigureWebHostDefaults(webBuilder =>
    {
    	webBuilder.UseStartup<Startup>();
    });
    

    看下UseServiceProviderFactory 源码:

    private IServiceFactoryAdapter _serviceProviderFactory = new ServiceFactoryAdapter<IServiceCollection>(new DefaultServiceProviderFactory());
    public IHostBuilder UseServiceProviderFactory<TContainerBuilder>(IServiceProviderFactory<TContainerBuilder> factory)
    {
    	_serviceProviderFactory = new ServiceFactoryAdapter<TContainerBuilder>(factory ?? throw new ArgumentNullException(nameof(factory)));
    	return this;
    }
    

    所以说是替换了我们的serviceProviderFactory。

    原理篇<<从新整理1400篇>>介绍了这个东西,这里再简要介绍一下。

    _serviceProviderFactory 通过代理模式下进行的,也就是一个适配过程,那么我们直接看适配器。

    其实适配器有一个小的隐藏信息哈。比如说_serviceProviderFactory的命名上看,翻译过来就是serviceProvider的构建工厂,也就是为我们提供serviceProvider的生产工厂。

    直接看ServiceFactoryAdapter的源码。

    internal class ServiceFactoryAdapter<TContainerBuilder> : IServiceFactoryAdapter
      {
        private IServiceProviderFactory<TContainerBuilder> _serviceProviderFactory;
        private readonly Func<HostBuilderContext> _contextResolver;
        private Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> _factoryResolver;
    
        public ServiceFactoryAdapter(
          IServiceProviderFactory<TContainerBuilder> serviceProviderFactory)
        {
          IServiceProviderFactory<TContainerBuilder> serviceProviderFactory1 = serviceProviderFactory;
          if (serviceProviderFactory1 == null)
            throw new ArgumentNullException(nameof (serviceProviderFactory));
          this._serviceProviderFactory = serviceProviderFactory1;
        }
    
        public ServiceFactoryAdapter(
          Func<HostBuilderContext> contextResolver,
          Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> factoryResolver)
        {
          Func<HostBuilderContext> func1 = contextResolver;
          if (func1 == null)
            throw new ArgumentNullException(nameof (contextResolver));
          this._contextResolver = func1;
          Func<HostBuilderContext, IServiceProviderFactory<TContainerBuilder>> func2 = factoryResolver;
          if (func2 == null)
            throw new ArgumentNullException(nameof (factoryResolver));
          this._factoryResolver = func2;
        }
    
        public object CreateBuilder(IServiceCollection services)
        {
          if (this._serviceProviderFactory == null)
          {
            this._serviceProviderFactory = this._factoryResolver(this._contextResolver());
            if (this._serviceProviderFactory == null)
              throw new InvalidOperationException("The resolver returned a null IServiceProviderFactory");
          }
          return (object) this._serviceProviderFactory.CreateBuilder(services);
        }
    
        public IServiceProvider CreateServiceProvider(object containerBuilder)
        {
          if (this._serviceProviderFactory == null)
            throw new InvalidOperationException("CreateBuilder must be called before CreateServiceProvider");
          return this._serviceProviderFactory.CreateServiceProvider((TContainerBuilder) containerBuilder);
        }
      }
    

    看这个CreateServiceProvider方法,和猜想的一致。再来看下HostBuilder调用情况。

    private void CreateServiceProvider()
    {
    	var services = new ServiceCollection();
    #pragma warning disable CS0618 // Type or member is obsolete
    	services.AddSingleton<IHostingEnvironment>(_hostingEnvironment);
    #pragma warning restore CS0618 // Type or member is obsolete
    	services.AddSingleton<IHostEnvironment>(_hostingEnvironment);
    	services.AddSingleton(_hostBuilderContext);
    	// register configuration as factory to make it dispose with the service provider
    	services.AddSingleton(_ => _appConfiguration);
    #pragma warning disable CS0618 // Type or member is obsolete
    	services.AddSingleton<IApplicationLifetime>(s => (IApplicationLifetime)s.GetService<IHostApplicationLifetime>());
    #pragma warning restore CS0618 // Type or member is obsolete
    	services.AddSingleton<IHostApplicationLifetime, ApplicationLifetime>();
    	services.AddSingleton<IHostLifetime, ConsoleLifetime>();
    	services.AddSingleton<IHost, Internal.Host>();
    	services.AddOptions();
    	services.AddLogging();
    
    	foreach (var configureServicesAction in _configureServicesActions)
    	{
    		configureServicesAction(_hostBuilderContext, services);
    	}
    
    	var containerBuilder = _serviceProviderFactory.CreateBuilder(services);
    
    	foreach (var containerAction in _configureContainerActions)
    	{
    		containerAction.ConfigureContainer(_hostBuilderContext, containerBuilder);
    	}
    
    	_appServices = _serviceProviderFactory.CreateServiceProvider(containerBuilder);
    
    	if (_appServices == null)
    	{
    		throw new InvalidOperationException($"The IServiceProviderFactory returned a null IServiceProvider.");
    	}
    
    	// resolve configuration explicitly once to mark it as resolved within the
    	// service provider, ensuring it will be properly disposed with the provider
    	_ = _appServices.GetService<IConfiguration>();
    }
    

    的却如此。完全证实了这个猜想。

    那么serviceProver 用来做什么的呢?

       1: internal class ServiceProvider : IServiceProvider, IDisposable
       2: {
       3:     public ServiceProvider Root { get; private set; }
       4:     public ServiceTable ServiceTable { get; private set; }
       5:     public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices { get; private set; } = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
       6:     public IList<IDisposable> TransientDisposableServices { get; private set; } = new List<IDisposable>();
       7:     public ConcurrentDictionary<IService, object> ResolvedServices { get; private set; } = new ConcurrentDictionary<IService, object>();
       8:     
       9:     public ServiceProvider(IServiceCollection services)
      10:     {
      11:         this.Root         = this;
      12:         this.ServiceTable     = new ServiceTable(services);
      13:     }
      14:  
      15:     public object GetService(Type serviceType)
      16:     {
      17:         Func<ServiceProvider, object> serviceAccessor;
      18:         if (this.RealizedServices.TryGetValue(serviceType, out serviceAccessor))
      19:         {
      20:             return serviceAccessor(this);
      21:         }
      22:  
      23:         IServiceCallSite serviceCallSite = this.GetServiceCallSite(serviceType, new HashSet<Type>());
      24:         if (null != serviceCallSite)
      25:         {
      26:             var providerExpression = Expression.Parameter(typeof(ServiceProvider), "provider");
      27:             this.RealizedServices[serviceType] = Expression.Lambda<Func<ServiceProvider, object>>(serviceCallSite.Build(providerExpression), providerExpression).Compile();
      28:             return serviceCallSite.Invoke(this);
      29:         }
      30:  
      31:         this.RealizedServices[serviceType] = _ => null;
      32:         return null;
      33:     }
      34:  
      35:     public IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain)
      36:     {
      37:             try
      38:             {
      39:                 if (callSiteChain.Contains(serviceType))
      40:                 {
      41:                     throw new InvalidOperationException(string.Format("A circular dependency was detected for the service of type '{0}'", serviceType.FullName);
      42:                 }
      43:                 callSiteChain.Add(serviceType);
      44:  
      45:                 ServiceEntry serviceEntry;
      46:                 if (this.ServiceTable.ServieEntries.TryGetValue(serviceType, 
      47:                     out serviceEntry))
      48:                 {
      49:                     return serviceEntry.Last.CreateCallSite(this, callSiteChain);
      50:                 }
      51:  
      52:                 //省略其他代码
      53:  
      54:                 return null;
      55:             }
      56:             finally
      57:             {
      58:                 callSiteChain.Remove(serviceType);
      59:             }
      60:     }    
      61:  
      62:     public void Dispose()
      63:     {
      64:         Array.ForEach(this.TransientDisposableServices.ToArray(), _ => _.Dispose());
      65:         Array.ForEach(this.ResolvedServices.Values.ToArray(), _ => (_ as IDisposable)?.Dispose());
      66:         this.TransientDisposableServices.Clear();
      67:         this.ResolvedServices.Clear();
      68:     }
      69:     //其他成员
      70: }
    

    看到GetService 是否特别的眼熟?这个就是通过我们的注册信息来产生不同的对象的。

    那么Autofac的作用就是替换了我们的ServiceProvider。也就是替换了,往容器注入的方式了。

    那么他给我们扩展了什么功能,后面就不介绍具体源码了,应为是实践篇,主要解释用法。

    命名注册

    为什么有命名注册的方式呢?有什么痛点呢?

    比如说:

    services.AddSingleton<IMySingletonService>(new MySingletonService());
    services.AddSingleton<IMySingletonService>(ServiceProvider =>
    {
    	return new MySingletonService();
    });
    

    那么可以肯定一点的是,这时候通过IMySingletonService 可以获取第一个,也可以获取全部。

    但是假如我要获取指定的一个呢?那么要给他们加上标记,但是.net core 自带的并没有并没有。

    这时候使用autofac。

    在startUp 中加入:

    public void ConfigureContainer(ContainerBuilder builder)
    {
    	builder.RegisterType<TestService>().As<ITestService>();
    	builder.RegisterType<TestService>().Named<ITestService>("service2");
    }
    

    然后在startUp 的Configure 中加入:

    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
    
    var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
    Console.WriteLine(testServiceNameDefault.GetHashCode());
    
    var testServiceName2= this.AutofacContainer.ResolveNamed<ITestService>("service2");
    Console.WriteLine(testServiceName2.GetHashCode());
    

    发现他们的hashcode并不一致,故而获取了不同对象。

    属性注册

    把这个TestService 修改一下:

    public interface ITestService
    {
           public void ShowAttributeState();
    }
    
    public class TestService:ITestService,IDisposable
    {
    	public AttributeService Attribute { get; set; }
    
    	public void ShowAttributeState()
    	{
    		Console.WriteLine($"attribute is null?{(Attribute == null ? "true":"false")}");
    	}
    
    	public void Dispose()
    	{
    		Console.WriteLine($"DisposableTestService Disposed:{this.GetHashCode()}");
    	}
    }
    

    AttributeService 如下:

    public class AttributeService
    {
    }
    

    AttributeService 的注册信息:
    然后添加:

    services.AddTransient<AttributeService>();
    builder.RegisterType<TestService>().As<ITestService>();
    

    注:这里我特意用services 来注册AttributeService 是为了证明autofac 兼容了.net core 原生的注册信息,证明前面的替换serviceProvider 的推导过程,这样我们就可以在我们的老项目中直接使用。

    获取信息:

    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
    
    var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
    testServiceNameDefault.ShowAttributeState();
    

    TestService的注册信息修改一下:

    builder.RegisterType<TestService>().As<ITestService>().PropertiesAutowired();
    

    设置自动注入属性,这样就可以了。如果对象属性有注册信息的话,会帮我们自动填充。

    aop 注入

    aop 如果没怎么看的话,可以简单理解可以理解为拦截器。

    一般我们看到属性的方式来写加入拦截器:

    [Attribuite]
    public void method(){
    }
    

    这样是显式注入拦截器,那么autofac 为我们隐式注入。

    安装一下Castle.core。然后我们写一个拦截器。

    public class TestInterceptor : IInterceptor
    {
    	public void Intercept(IInvocation invocation)
    	{
    		Console.WriteLine($"Invocation before Method:{invocation.Method.Name}");
    		invocation.Proceed();
    		Console.WriteLine($"Invocation after Method:{invocation.Method.Name}");
    	}
    }
    

    然后我们注册的时候注入拦截器。

    安装一下:Autofac.Extras.DynamicProxy

    写入注册信息:

    builder.RegisterType<TestInterceptor>();
    builder.RegisterType<TestService>().As<ITestService>().PropertiesAutowired().InterceptedBy(typeof(TestInterceptor)).EnableInterfaceInterceptors();
    

    然后调用:

    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
    
    var testServiceNameDefault = this.AutofacContainer.Resolve<ITestService>();
    testServiceNameDefault.ShowAttributeState();
    

    看下结果:

    给容器命名

    builder.RegisterType<TestService>().As<ITestService>().InstancePerMatchingLifetimeScope("selfScope");
    

    然后调用:

    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
    using (var myScope = AutofacContainer.BeginLifetimeScope())
    { 
    	var obj = myScope.Resolve<ITestService>();
    	Console.WriteLine(obj==null?"true":"false");
    }
    

    这里会报错myScope.Resolve();,因为获取不到,指定了指定容器selfScope。

    要这样写:

    using (var myScope = AutofacContainer.BeginLifetimeScope("selfScope"))
    { 
    	var obj = myScope.Resolve<ITestService>();
    	Console.WriteLine(obj==null?"true":"false");
    }
    

    这么写好像是不能体现出这个容器命名有什么作用。

    画一张概念图:

    上述是原先.net core 的一个隔离思路。

    如果给容器命名的话,相当于每个scope可以继续套娃,起一个隔离作用。

    代码演示:

    this.AutofacContainer = app.ApplicationServices.GetAutofacRoot();
    using (var myScope = AutofacContainer.BeginLifetimeScope("selfScope"))
    { 
    	var obj = myScope.Resolve<ITestService>();
    	using (var myChildScope = myScope.BeginLifetimeScope())
    	{
    		var obj1 = myChildScope.Resolve<ITestService>();
    		Console.WriteLine(obj.GetHashCode());
    		Console.WriteLine(obj1.GetHashCode());
    	}
    }
    

    这种隔离机制做项目的时候就能体现出来,因为可能有几个服务共同用到了某个类,这样解决管理困难问题。

    上述只是个人整理,如有问题,望请指出,谢谢。

    下一节:配置之盟约。

    bk