转载

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)

这个系列已经写了6篇,链接地址如下:

[Asp.net 5] DependencyInjection项目代码分析

[Asp.net 5] DependencyInjection项目代码分析2-Autofac

[Asp.net 5] DependencyInjection项目代码分析3-Ninject

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1)

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2)

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3)

如果想对本篇有个更好的了解,建议需要先看

“ [Asp.net 5] DependencyInjection项目代码分析 ”

“ [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(1) ”

“ [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(2) ”。

" [Asp.net 5] DependencyInjection项目代码分析4-微软的实现(3) "

继续 ServiceProvider类

在之前的讲解中我们提到过Service类调用CreateCallSite方法时会递归调用,但是我们没具体说明如何递归调的。实际上Service类,通过反射创建实例的时候,会实例化的参数对象,而实例话参数对象通过 ServiceProvider 类创建,而 ServiceProvider 类创建参数的实例,又需要通过Service类(如果是通过Type注册的)创建。下面我们把Service的CreateInstanceCallSite方法以及 ServiceProvider 相关的方法列出来。

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)
public IServiceCallSite CreateCallSite(ServiceProvider provider, ISet<Type> callSiteChain) {  ConstructorInfo[] constructors = _descriptor.ImplementationType.GetTypeInfo()   .DeclaredConstructors   .Where(IsInjectable)   .ToArray();  // TODO: actual service-fulfillment constructor selection  if (constructors.Length == 1)  {   ParameterInfo[] parameters = constructors[0].GetParameters();   IServiceCallSite[] parameterCallSites = new IServiceCallSite[parameters.Length];   for (var index = 0; index != parameters.Length; ++index)   {    parameterCallSites[index] = provider.GetServiceCallSite(parameters[index].ParameterType, callSiteChain);    if (parameterCallSites[index] == null && parameters[index].HasDefaultValue)    {     parameterCallSites[index] = new ConstantCallSite(parameters[index].DefaultValue);    }    if (parameterCallSites[index] == null)    {     throw new InvalidOperationException(Resources.FormatCannotResolveService(       parameters[index].ParameterType,        _descriptor.ImplementationType));    }   }   return new ConstructorCallSite(constructors[0], parameterCallSites);  }  return new CreateInstanceCallSite(_descriptor); } 
Service的CreateCallSite
[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)
internal IServiceCallSite GetServiceCallSite(Type serviceType, ISet<Type> callSiteChain) {  try  {   if (callSiteChain.Contains(serviceType))   {    throw new InvalidOperationException(Resources.FormatCircularDependencyException(serviceType));   }   callSiteChain.Add(serviceType);   ServiceEntry entry;   if (_table.TryGetEntry(serviceType, out entry))   {    return GetResolveCallSite(entry.Last, callSiteChain);   }   object emptyIEnumerableOrNull = GetEmptyIEnumerableOrNull(serviceType);   if (emptyIEnumerableOrNull != null)   {    return new EmptyIEnumerableCallSite(serviceType, emptyIEnumerableOrNull);   }   return null;  }  finally  {   callSiteChain.Remove(serviceType);  } } internal IServiceCallSite GetResolveCallSite(IService service, ISet<Type> callSiteChain) {  IServiceCallSite serviceCallSite = service.CreateCallSite(this, callSiteChain);  if (service.Lifetime == ServiceLifetime.Transient)  {   return new TransientCallSite(serviceCallSite);  }  else if (service.Lifetime == ServiceLifetime.Scoped)  {   return new ScopedCallSite(service, serviceCallSite);  }  else  {   return new SingletonCallSite(service, serviceCallSite);  } } 
ServiceProvider

对于Service的CreateCallSite方法,之前我们已经介绍过,现在我们重点讲下ServiceProvider的GetServiceCallSite方法。从上面代码中我们发现参数中含有“ ISet<Type> callSiteChain”,这个参数是防止发生A的构造函数有B类型参数,B的构织函数中有A类型参数,当A,B都是通过类型注入的,那么系统会陷入死循环。而callSiteChain得作用就是防止这样的死循环发生,当创建A时,会在callSiteChain中查询历史中是否有A的创建过程,如果有则说明发生死循环了,直接抛出异常,结束;如果没有将A加入到callSiteChain中,继续创建其参数。GetResolveCallSite方法比较简单,对于ServiceProvider已经能够获取的IServiceCallSite实例,进行包装,已保证生成的实例能够适应不同的Scoped(该处应该使用设计模式中的代理模式,不过我设计模式不过关,请帮忙确认)。

对于TransientCallSite、ScopedCallSite、SingletonCallSite以及EmptyIEnumerableCallSite代码,如下所示:

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)
private class EmptyIEnumerableCallSite : IServiceCallSite {  private readonly object _serviceInstance;  private readonly Type _serviceType;  public EmptyIEnumerableCallSite(Type serviceType, object serviceInstance)  {   _serviceType = serviceType;   _serviceInstance = serviceInstance;  }  public object Invoke(ServiceProvider provider)  {   return _serviceInstance;  }  public Expression Build(Expression provider)  {   return Expression.Constant(_serviceInstance, _serviceType);  } } private class TransientCallSite : IServiceCallSite {  private readonly IServiceCallSite _service;  public TransientCallSite(IServiceCallSite service)  {   _service = service;  }  public object Invoke(ServiceProvider provider)  {   return provider.CaptureDisposable(_service.Invoke(provider));  }  public Expression Build(Expression provider)  {   return Expression.Call(    provider,    CaptureDisposableMethodInfo,    _service.Build(provider));  } } private class ScopedCallSite : IServiceCallSite {  private readonly IService _key;  private readonly IServiceCallSite _serviceCallSite;  public ScopedCallSite(IService key, IServiceCallSite serviceCallSite)  {   _key = key;   _serviceCallSite = serviceCallSite;  }  public virtual object Invoke(ServiceProvider provider)  {   object resolved;   lock (provider._sync)   {    if (!provider._resolvedServices.TryGetValue(_key, out resolved))    {     resolved = provider.CaptureDisposable(_serviceCallSite.Invoke(provider));     provider._resolvedServices.Add(_key, resolved);    }   }   return resolved;  }  public virtual Expression Build(Expression providerExpression)  {   var keyExpression = Expression.Constant(    _key,    typeof(IService));   var resolvedExpression = Expression.Variable(typeof(object), "resolved");   var resolvedServicesExpression = Expression.Field(    providerExpression,    "_resolvedServices");   var tryGetValueExpression = Expression.Call(    resolvedServicesExpression,    TryGetValueMethodInfo,    keyExpression,    resolvedExpression);   var captureDisposableExpression = Expression.Assign(    resolvedExpression,    Expression.Call(     providerExpression,     CaptureDisposableMethodInfo,     _serviceCallSite.Build(providerExpression)));   var addValueExpression = Expression.Call(    resolvedServicesExpression,    AddMethodInfo,    keyExpression,    resolvedExpression);   var blockExpression = Expression.Block(    typeof(object),    new[] { resolvedExpression },    Expression.IfThen(     Expression.Not(tryGetValueExpression),     Expression.Block(captureDisposableExpression, addValueExpression)),    resolvedExpression);   return Lock(providerExpression, blockExpression);  }  private static Expression Lock(Expression providerExpression, Expression body)  {   // The C# compiler would copy the lock object to guard against mutation.   // We don't, since we know the lock object is readonly.   var syncField = Expression.Field(providerExpression, "_sync");   var lockWasTaken = Expression.Variable(typeof(bool), "lockWasTaken");   var monitorEnter = Expression.Call(MonitorEnterMethodInfo, syncField, lockWasTaken);   var monitorExit = Expression.Call(MonitorExitMethodInfo, syncField);   var tryBody = Expression.Block(monitorEnter, body);   var finallyBody = Expression.IfThen(lockWasTaken, monitorExit);   return Expression.Block(    typeof(object),    new[] { lockWasTaken },    Expression.TryFinally(tryBody, finallyBody));  } } private class SingletonCallSite : ScopedCallSite {  public SingletonCallSite(IService key, IServiceCallSite serviceCallSite) : base(key, serviceCallSite)  {  }  public override object Invoke(ServiceProvider provider)  {   return base.Invoke(provider._root);  }  public override Expression Build(Expression provider)  {   return base.Build(Expression.Field(provider, "_root"));  } } 
View Code

该工程所有类的关系图(包括内部类以及一些接口),如下所示:

[Asp.net 5] DependencyInjection项目代码分析4-微软的实现(4)

补充说明

  • IServiceCallSite中定义了Build方法,该方法使用了Expression,但是该篇文章没有对其进行具体的研究,并且Build方法是相对独立的。
  • 对于OpenIEnumerableService泛型省略详解。

示例测试代码:

public static class TestServices  {   public static IServiceCollection DefaultServices()   {    var services = new ServiceCollection();    services.AddTransient<IFakeService, FakeService>();    services.AddTransient<IFakeMultipleService, FakeOneMultipleService>();    services.AddTransient<IFakeMultipleService, FakeTwoMultipleService>();    services.AddTransient<IFakeOuterService, FakeOuterService>();    services.AddInstance<IFakeServiceInstance>(new FakeService() { Message = "Instance" });    services.AddScoped<IFakeScopedService, FakeService>();    services.AddSingleton<IFakeSingletonService, FakeService>();    services.AddTransient<IDependOnNonexistentService, DependOnNonexistentService>();    services.AddTransient<IFakeOpenGenericService<string>, FakeService>();    services.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>));    services.AddTransient<IFactoryService>(provider =>    {     var fakeService = provider.GetService<IFakeService>();     return new TransientFactoryService     {      FakeService = fakeService,      Value = 42     };    });    services.AddScoped(provider =>    {     var fakeService = provider.GetService<IFakeService>();     return new ScopedFactoryService     {      FakeService = fakeService,     };    });    services.AddTransient<ServiceAcceptingFactoryService, ServiceAcceptingFactoryService>();    return services;   }  } public class ServiceProviderContainerTests : ScopingContainerTestBase  {   protected override IServiceProvider CreateContainer()   {    return TestServices.DefaultServices().BuildServiceProvider();   } [Fact]   public void ScopedServiceCanBeResolved()   {    IServiceProvider container = CreateContainer();    var scopeFactory = container.GetService<IServiceScopeFactory>();    using (var scope = scopeFactory.CreateScope())    {     var containerScopedService = container.GetService<IFakeScopedService>();     var scopedService1 = scope.ServiceProvider.GetService<IFakeScopedService>();     Thread.Sleep(200);     var scopedService2 = scope.ServiceProvider.GetService<IFakeScopedService>();     Assert.NotEqual(containerScopedService, scopedService1);     Assert.Equal(scopedService1, scopedService2);    }   } } 
正文到此结束
Loading...