从EFCore上下文的使用到深入剖析DI的生命周期最后实现自动属性

2020-01-18 19:39:09王冬梅

既然前面已经在启动类中注册了上下文,那么从DI容器中获取实例肯定是没问题的。于是我写了这样一句测试代码用来验证猜想:


 var context = app.ApplicationServices.GetService<BloggingContext>();

不过很遗憾抛出了异常:

报错信息说的很明确,不能从root provider中获取这个服务。我从G站下载了DI框架的源码(地址是https://github.com/aspnet/Extensions/tree/master/src/DependencyInjection),拿报错信息进行反向追溯,发现异常来自于CallSiteValidator类的ValidateResolution方法:


public void ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
    {
      if (ReferenceEquals(scope, rootScope)
        && _scopedServices.TryGetValue(serviceType, out var scopedService))
      {
        if (serviceType == scopedService)
        {
          throw new InvalidOperationException(
            Resources.FormatDirectScopedResolvedFromRootException(serviceType,
              nameof(ServiceLifetime.Scoped).ToLowerInvariant()));
        }

        throw new InvalidOperationException(
          Resources.FormatScopedResolvedFromRootException(
            serviceType,
            scopedService,
            nameof(ServiceLifetime.Scoped).ToLowerInvariant()));
      }
    }

继续往上,看到了GetService方法的实现:


internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
    {
      if (_disposed)
      {
        ThrowHelper.ThrowObjectDisposedException();
      }

      var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
      _callback?.OnResolve(serviceType, serviceProviderEngineScope);
      DependencyInjectionEventSource.Log.ServiceResolved(serviceType);
      return realizedService.Invoke(serviceProviderEngineScope);
    }

可以看到,_callback在为空的情况下是不会做验证的,于是猜想有参数能对它进行配置。把追溯对象换成_callback继续往上翻,在DI框架的核心类ServiceProvider中找到如下方法:


internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
    {
      IServiceProviderEngineCallback callback = null;
      if (options.ValidateScopes)
      {
        callback = this;
        _callSiteValidator = new CallSiteValidator();
      }
      //省略....
    }

说明我的猜想没错,验证是受ValidateScopes控制的。这样来看,把ValidateScopes设置成False就可以解决了,这也是网上普遍的解决方案:


 .UseDefaultServiceProvider(options =>
    {
       options.ValidateScopes = false;
    })