查看原文
其他

.NET Core 实践篇:依赖注入应用之援军

DotNet 2021-12-15

推荐关注↓

前言


介绍第三方依赖注入框架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 (objectthis._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 用来做什么的呢?

   1internal class ServiceProvider : IServiceProviderIDisposable
   2: {
   3:     public ServiceProvider Root { getprivate set; }
   4:     public ServiceTable ServiceTable { getprivate set; }
   5:     public ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices { getprivate set; } = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
   6:     public IList<IDisposable> TransientDisposableServices { getprivate set; } = new List<IDisposable>();
   7:     public ConcurrentDictionary<IService, object> ResolvedServices { getprivate 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 { getset; }

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

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

总结

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


转自:不问前世

链接:cnblogs.com/aoximin/p/14820357.html

- EOF -

推荐阅读  点击标题可跳转
抓住异步编程async/await语法糖的牛鼻子.NET Core with 微服务 - 架构图.NET 6 Preview 4 已发布

看完本文有收获?请转发分享给更多人

推荐关注「DotNet」,提升.Net技能 

点赞和在看就是最大的支持❤️

: . Video Mini Program Like ,轻点两下取消赞 Wow ,轻点两下取消在看

您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存