zoukankan      html  css  js  c++  java
  • .NET Core开发日志——依赖注入

    依赖注入(DI)不是一个新的话题,它的出现是伴随着系统解耦的需要而几乎必然产生的。

    在SOLID设计原则中,DIP(Dependency inversion principle)——依赖倒置,规定了“需依赖抽象,而非实现”的准则,该原则主要目的是通过引入抽象(比如接口)的方式降低模块之间的耦合性。与此原则相拟而又有所不同的是IoC(inversion of control)——控制反转设计原则。这项原则定义了应该由通用框架而非外部代码决定控制流(control flow)的概念。对控制反转的实现有数种技术,DI(Dependency injection)——依赖注入便是其中之一,而依赖注入技术同时又支持依赖倒置的设计原则,所以它被广泛使用并不是件令人意外的事情。

    依赖注入的基本特性是借由一个对象提供对另一对象的依赖。这样的一个对象通常又被称为容器。容器负责被依赖对象的注册(register),解析(resolve)与释放(release),并具有将被依赖对象注入到依赖对象内部的功能。

    在之前的ASP.NET开发过程中,要想使用到依赖注入技术必需依赖第三方类库,而在ASP.NET Core中,这项技术已经被引入到其自身的框架中。

    容器

    ASP.NET Core中使用ServiceProvider作为依赖注入的容器,它是在WebHostBuilder类中被引入的。

    public IWebHost Build()
    {
        ...
    
        IServiceProvider GetProviderFromFactory(IServiceCollection collection)
        {
            var provider = collection.BuildServiceProvider();
            var factory = provider.GetService<IServiceProviderFactory<IServiceCollection>>();
    
            if (factory != null)
            {
                using (provider)
                {
                    return factory.CreateServiceProvider(factory.CreateBuilder(collection));
                }
            }
    
            return provider;
        }
    }
    

    注册

    所需依赖的对象通过ServiceCollectionServiceExtensions中的各种扩展方法被加入到ServiceCollection类中。ServiceCollection类内部维护着一个ServiceDescriptor集合。而ServiceCollection又会被传入ServiceProvider的构造方法。

    public static IServiceCollection AddTransient(
        this IServiceCollection services,
        Type serviceType,
        Type implementationType)
    {
        ...
    
        return Add(services, serviceType, implementationType, ServiceLifetime.Transient);
    }
    
    public static IServiceCollection AddScoped(
        this IServiceCollection services,
        Type serviceType,
        Type implementationType)
    {
        ...
    
        return Add(services, serviceType, implementationType, ServiceLifetime.Scoped);
    }
    
    public static IServiceCollection AddSingleton(
        this IServiceCollection services,
        Type serviceType,
        Type implementationType)
    {
        ...
    
        return Add(services, serviceType, implementationType, ServiceLifetime.Singleton);
    }
    
    private static IServiceCollection Add(
        IServiceCollection collection,
        Type serviceType,
        Type implementationType,
        ServiceLifetime lifetime)
    {
        var descriptor = new ServiceDescriptor(serviceType, implementationType, lifetime);
        collection.Add(descriptor);
        return collection;
    }
    

    解析

    要想获得已注册的对象,可以通过ServiceProviderServiceExtensions类的扩展方法GetService。

    public static T GetService<T>(this IServiceProvider provider)
    {
        if (provider == null)
        {
            throw new ArgumentNullException(nameof(provider));
        }
    
        return (T)provider.GetService(typeof(T));
    }
    

    ServiceProvider的GetService方法其实是调用了它内部各种引擎的父类ServiceProviderEngine的方法。这些引擎间区别在于实现方式以及性能上,功能方面都是一样的。默认引擎是DynamicServiceProviderEngine。

    private readonly IServiceProviderEngine _engine;
    
    internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
    {
      IServiceProviderEngineCallback callback = null;
      if (options.ValidateScopes)
      {
          callback = this;
          _callSiteValidator = new CallSiteValidator();
      }
      switch (options.Mode)
      {
          case ServiceProviderMode.Dynamic:
              _engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
              break;
          case ServiceProviderMode.Runtime:
              _engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
              break;
    #if IL_EMIT
          case ServiceProviderMode.ILEmit:
              _engine = new ILEmitServiceProviderEngine(serviceDescriptors, callback);
              break;
    #endif
          case ServiceProviderMode.Expressions:
              _engine = new ExpressionsServiceProviderEngine(serviceDescriptors, callback);
              break;
          default:
              throw new NotSupportedException(nameof(options.Mode));
      }
    }
    
    public object GetService(Type serviceType) => _engine.GetService(serviceType);
    

    ServiceProviderEngine类的CreateServiceAccessor方法创建了CallSite。

    internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
    {
        if (_disposed)
        {
            ThrowHelper.ThrowObjectDisposedException();
        }
    
        var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
        _callback?.OnResolve(serviceType, serviceProviderEngineScope);
        return realizedService.Invoke(serviceProviderEngineScope);
    }
    
    private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
    {
        var callSite = CallSiteFactory.CreateCallSite(serviceType, new CallSiteChain());
        if (callSite != null)
        {
            _callback?.OnCreate(callSite);
            return RealizeService(callSite);
        }
    
        return _ => null;
    }
    

    假设注册是用的public static IServiceCollection AddSingleton(this IServiceCollection services, Type serviceType, Type implementationType)方法,那么之后的处理会生成一个SingletonCallSite对象并且包含ConstructorCallSite参数值。

    private IServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain)
    {
        if (serviceType == descriptor.ServiceType)
        {
            IServiceCallSite callSite;
            if (descriptor.ImplementationInstance != null)
            {
                callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);
            }
            else if (descriptor.ImplementationFactory != null)
            {
                callSite = new FactoryCallSite(descriptor.ServiceType, descriptor.ImplementationFactory);
            }
            else if (descriptor.ImplementationType != null)
            {
                callSite = CreateConstructorCallSite(descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
            }
            else
            {
                throw new InvalidOperationException("Invalid service descriptor");
            }
    
            return ApplyLifetime(callSite, descriptor, descriptor.Lifetime);
        }
    
        return null;
    }
    
    private IServiceCallSite CreateConstructorCallSite(Type serviceType, Type implementationType, CallSiteChain callSiteChain)
    {
        callSiteChain.Add(serviceType, implementationType);
    
        var constructors = implementationType.GetTypeInfo()
            .DeclaredConstructors
            .Where(constructor => constructor.IsPublic)
            .ToArray();
    
        IServiceCallSite[] parameterCallSites = null;
    
        if (constructors.Length == 0)
        {
            throw new InvalidOperationException(Resources.FormatNoConstructorMatch(implementationType));
        }
        else if (constructors.Length == 1)
        {
            var constructor = constructors[0];
            var parameters = constructor.GetParameters();
            if (parameters.Length == 0)
            {
                return new CreateInstanceCallSite(serviceType, implementationType);
            }
    
            parameterCallSites = CreateArgumentCallSites(
                serviceType,
                implementationType,
                callSiteChain,
                parameters,
                throwIfCallSiteNotFound: true);
    
            return new ConstructorCallSite(serviceType, constructor, parameterCallSites);
        }
    
        ...
    }
    
    private IServiceCallSite ApplyLifetime(IServiceCallSite serviceCallSite, object cacheKey, ServiceLifetime descriptorLifetime)
    {
        if (serviceCallSite is ConstantCallSite)
        {
            return serviceCallSite;
        }
    
        switch (descriptorLifetime)
        {
            case ServiceLifetime.Transient:
                return new TransientCallSite(serviceCallSite);
            case ServiceLifetime.Scoped:
                return new ScopedCallSite(serviceCallSite, cacheKey);
            case ServiceLifetime.Singleton:
                return new SingletonCallSite(serviceCallSite, cacheKey);
            default:
                throw new ArgumentOutOfRangeException(nameof(descriptorLifetime));
        }
    }
    

    ServiceProvider真正解析的是这个生成出来的CallSite对象。

    protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
    {
        var callCount = 0;
        return scope =>
        {
            if (Interlocked.Increment(ref callCount) == 2)
            {
                Task.Run(() => base.RealizeService(callSite));
            }
            return RuntimeResolver.Resolve(callSite, scope);
        };
    }
    
    public object Resolve(IServiceCallSite callSite, ServiceProviderEngineScope scope)
    {
        return VisitCallSite(callSite, scope);
    }
    
    protected virtual TResult VisitCallSite(IServiceCallSite callSite, TArgument argument)
    {
        switch (callSite.Kind)
        {
            case CallSiteKind.Factory:
                return VisitFactory((FactoryCallSite)callSite, argument);
            case  CallSiteKind.IEnumerable:
                return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
            case CallSiteKind.Constructor:
                return VisitConstructor((ConstructorCallSite)callSite, argument);
            case CallSiteKind.Transient:
                return VisitTransient((TransientCallSite)callSite, argument);
            case CallSiteKind.Singleton:
                return VisitSingleton((SingletonCallSite)callSite, argument);
            case CallSiteKind.Scope:
                return VisitScoped((ScopedCallSite)callSite, argument);
            case CallSiteKind.Constant:
                return VisitConstant((ConstantCallSite)callSite, argument);
            case CallSiteKind.CreateInstance:
                return VisitCreateInstance((CreateInstanceCallSite)callSite, argument);
            case CallSiteKind.ServiceProvider:
                return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
            case CallSiteKind.ServiceScopeFactory:
                return VisitServiceScopeFactory((ServiceScopeFactoryCallSite)callSite, argument);
            default:
                throw new NotSupportedException($"Call site type {callSite.GetType()} is not supported");
        }
    }
    

    因为上例中CallSite的类型是Constructor,所以最终通过VisitConstructor方法获得所依赖的对象。

    protected override object VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
    {
        object[] parameterValues = new object[constructorCallSite.ParameterCallSites.Length];
        for (var index = 0; index < parameterValues.Length; index++)
        {
            parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], scope);
        }
    
        try
        {
            return constructorCallSite.ConstructorInfo.Invoke(parameterValues);
        }
        catch (Exception ex) when (ex.InnerException != null)
        {
            ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
            // The above line will always throw, but the compiler requires we throw explicitly.
            throw;
        }
    }
    

    至于创建对象的方法是用反射,表达式树(Expression Tree)还是IL Emit,则取决于所使用的内部引擎。

    释放

    创建ServiceProviderEngine的时候会为其Root属性绑定ServiceProviderEngineScope类型的值,Root = new ServiceProviderEngineScope(this);

    在ServiceProviderEngineScope类内部有着用于释放资源的Dispose方法。

    public void Dispose()
    {
        lock (ResolvedServices)
        {
            if (_disposed)
            {
                return;
            }
    
            _disposed = true;
            if (_disposables != null)
            {
                for (var i = _disposables.Count - 1; i >= 0; i--)
                {
                    var disposable = _disposables[i];
                    disposable.Dispose();
                }
    
                _disposables.Clear();
            }
    
            ResolvedServices.Clear();
        }
    }
    
    internal object CaptureDisposable(object service)
    {
        _captureDisposableCallback?.Invoke(service);
    
        if (!ReferenceEquals(this, service))
        {
            if (service is IDisposable disposable)
            {
                lock (ResolvedServices)
                {
                    if (_disposables == null)
                    {
                        _disposables = new List<IDisposable>();
                    }
    
                    _disposables.Add(disposable);
                }
            }
        }
        return service;
    }
    

    并不是所有对象都会通过ServiceProvider容器释放资源,只有容器自己创建的才可以。如果是新建对象再传入容器,容器不会为其作处理。

    public void ConfigureServices(IServiceCollection services)
    {
        // 容器创建了实例所以会释放它。
        services.AddScoped<Service1>();
        services.AddSingleton<Service2>();
        services.AddSingleton<ISomeService>(sp => new SomeServiceImplementation());
    
        // 容器没有创建实例所以不会释放它。
        services.AddSingleton<Service3>(new Service3());
        services.AddSingleton(new Service3());
    }
    

    注入

    ASP.NET Core中最常用的是Constructor Inject(构造器注入)方式。在其MVC框架中,通过DefaultControllerActivator生成Controller时,就可以跟踪到依赖注入是如何被其使用的。

    public virtual object Create(ControllerContext controllerContext)
    {
        ...
    
        var serviceProvider = controllerContext.HttpContext.RequestServices;
        return _typeActivatorCache.CreateInstance<object>(serviceProvider, controllerTypeInfo.AsType());
    }
    

    DefaultControllerActivator的Create方法使用了TypeActivatorCache类,其内部用到了ActivatorUtilities.CreateFactory方法。

    public class TypeActivatorCache : ITypeActivatorCache
    {
        private readonly Func<Type, ObjectFactory> _createFactory =
            (type) => ActivatorUtilities.CreateFactory(type, Type.EmptyTypes);
        private readonly ConcurrentDictionary<Type, ObjectFactory> _typeActivatorCache =
               new ConcurrentDictionary<Type, ObjectFactory>();
    
        public TInstance CreateInstance<TInstance>(
            IServiceProvider serviceProvider,
            Type implementationType)
        {
            ...
    
            var createFactory = _typeActivatorCache.GetOrAdd(implementationType, _createFactory);
            return (TInstance)createFactory(serviceProvider, arguments: null);
        }
    }
    

    ActivatorUtilities类位于ServiceProvider同样的程序集中。

    public static ObjectFactory CreateFactory(Type instanceType, Type[] argumentTypes)
    {
        FindApplicableConstructor(instanceType, argumentTypes, out ConstructorInfo constructor, out int?[] parameterMap);
    
        var provider = Expression.Parameter(typeof(IServiceProvider), "provider");
        var argumentArray = Expression.Parameter(typeof(object[]), "argumentArray");
        var factoryExpressionBody = BuildFactoryExpression(constructor, parameterMap, provider, argumentArray);
    
        var factoryLamda = Expression.Lambda<Func<IServiceProvider, object[], object>>(
            factoryExpressionBody, provider, argumentArray);
    
        var result = factoryLamda.Compile();
        return result.Invoke;
    }
    

    留意BuildFactoryExpression方法中GetServiceInfo变量。

    private static Expression BuildFactoryExpression(
        ConstructorInfo constructor,
        int?[] parameterMap,
        Expression serviceProvider,
        Expression factoryArgumentArray)
    {
        var constructorParameters = constructor.GetParameters();
        var constructorArguments = new Expression[constructorParameters.Length];
    
        for (var i = 0; i < constructorParameters.Length; i++)
        {
            var constructorParameter = constructorParameters[i];
            var parameterType = constructorParameter.ParameterType;
            var hasDefaultValue = ParameterDefaultValue.TryGetDefaultValue(constructorParameter, out var defaultValue);
    
            if (parameterMap[i] != null)
            {
                constructorArguments[i] = Expression.ArrayAccess(factoryArgumentArray, Expression.Constant(parameterMap[i]));
            }
            else
            {
                var parameterTypeExpression = new Expression[] { serviceProvider,
                    Expression.Constant(parameterType, typeof(Type)),
                    Expression.Constant(constructor.DeclaringType, typeof(Type)),
                    Expression.Constant(hasDefaultValue) };
                constructorArguments[i] = Expression.Call(GetServiceInfo, parameterTypeExpression);
            }
    
            ...
        }
    
        return Expression.New(constructor, constructorArguments);
    }
    

    GetServiceInfo变量申明了对GetService方法的调用,而此GetService其实正是对ServiceProvider的调用。

    private static readonly MethodInfo GetServiceInfo =
        GetMethodInfo<Func<IServiceProvider, Type, Type, bool, object>>((sp, t, r, c) => GetService(sp, t, r, c));
    
    private static object GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
    {
        var service = sp.GetService(type);
        ...
        
        return service;
    }
    

    通过以上的处理,在创建Controller时,其构造方法中所需参数的类型也会被容器解析,创建相应实例,从而实现依赖注入功能。

    生命周期

    ASP.NET Core容器可以创建三种生命周期的对象:

    • Transient, 每次取得的都是新的对象。
    • Scoped, 每次ASP.NET请求生成不同的对象。
    • Singleton,同一对象只会生成一次。

    检视这三者类型的构造方法,可以很容易理解Scoped与Singleton是通过缓存的方式实现对象的重用。

    public TransientCallSite(IServiceCallSite serviceCallSite)
    {
        ServiceCallSite = serviceCallSite;
    }
    
    public ScopedCallSite(IServiceCallSite serviceCallSite, object cacheKey)
    {
        ServiceCallSite = serviceCallSite;
        CacheKey = cacheKey;
    }
    
    public SingletonCallSite(IServiceCallSite serviceCallSite, object cacheKey) : base(serviceCallSite, cacheKey)
    {
    }
    

    性能

    由于ServiceProvider容器使用了反射,表达式树以及IL Emit方式创建对象,可能会对其性能有所担忧,但实际检测的结果,除了Runtime引擎表现不尽如人意外,其它引擎的性能还是在可接受范围内的。

    public class GetServiceBenchmark
    {
        private const int OperationsPerInvoke = 50000;
    
        private IServiceProvider _transientSp;
        private ServiceProviderMode _mode;
    
        [Params("Expressions", "Dynamic", "Runtime", "ILEmit")]
        public string Mode {
            set {
                _mode = (ServiceProviderMode)Enum.Parse(typeof(ServiceProviderMode), value);
            }
        }
    
        [Benchmark(Baseline = true, OperationsPerInvoke = OperationsPerInvoke)]
        public void NoDI()
        {
            for (int i = 0; i < OperationsPerInvoke; i++)
            {
                var temp = new A(new B(new C()));
                temp.Foo();
            }
        }
    
        [GlobalSetup(Target = nameof(Transient))]
        public void SetupTransient()
        {
            var services = new ServiceCollection();
            services.AddTransient<A>();
            services.AddTransient<B>();
            services.AddTransient<C>();
            _transientSp = services.BuildServiceProvider(new ServiceProviderOptions()
            {
                Mode = _mode
            });
        }
    
        [Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
        public void Transient()
        {
            for (int i = 0; i < OperationsPerInvoke; i++)
            {
                var temp = _transientSp.GetService<A>();
                temp.Foo();
            }
        }
    
        private class A
        {
            public A(B b)
            {
    
            }
    
            [MethodImpl(MethodImplOptions.NoInlining)]
            public void Foo()
            {
    
            }
        }
    
        private class B
        {
            public B(C c)
            {
    
            }
        }
    
        private class C
        {
    
        }
    }
    
    // ***** BenchmarkRunner: Finish  *****
    
    // * Export *
    
    // * Detailed results *
    GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Dynamic]
    Runtime = ; GC = 
    Mean = 5.5175 ns, StdErr = 0.0116 ns (0.21%); N = 15, StdDev = 0.0449 ns
    Min = 5.4490 ns, Q1 = 5.4860 ns, Median = 5.5207 ns, Q3 = 5.5641 ns, Max = 5.5972 ns
    IQR = 0.0781 ns, LowerFence = 5.3688 ns, UpperFence = 5.6813 ns
    ConfidenceInterval = [5.4695 ns; 5.5654 ns] (CI 99.9%), Margin = 0.0480 ns (0.87% of Mean)
    Skewness = 0.15, Kurtosis = 1.67
    
    
    GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Dynamic]
    Runtime = ; GC = 
    Mean = 43.1601 ns, StdErr = 0.0677 ns (0.16%); N = 15, StdDev = 0.2620 ns
    Min = 42.7731 ns, Q1 = 42.9117 ns, Median = 43.2403 ns, Q3 = 43.3580 ns, Max = 43.5392 ns
    IQR = 0.4464 ns, LowerFence = 42.2421 ns, UpperFence = 44.0276 ns
    ConfidenceInterval = [42.8800 ns; 43.4402 ns] (CI 99.9%), Margin = 0.2801 ns (0.65% of Mean)
    Skewness = -0.2, Kurtosis = 1.59
    
    
    GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Expressions]
    Runtime = ; GC = 
    Mean = 5.6964 ns, StdErr = 0.0388 ns (0.68%); N = 33, StdDev = 0.2226 ns
    Min = 5.5148 ns, Q1 = 5.5603 ns, Median = 5.6042 ns, Q3 = 5.6769 ns, Max = 6.2460 ns
    IQR = 0.1166 ns, LowerFence = 5.3854 ns, UpperFence = 5.8518 ns
    ConfidenceInterval = [5.5561 ns; 5.8368 ns] (CI 99.9%), Margin = 0.1404 ns (2.46% of Mean)
    Skewness = 1.48, Kurtosis = 3.69
    
    
    GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Expressions]
    Runtime = ; GC = 
    Mean = 43.6662 ns, StdErr = 0.0995 ns (0.23%); N = 13, StdDev = 0.3586 ns
    Min = 43.1083 ns, Q1 = 43.5089 ns, Median = 43.6051 ns, Q3 = 43.7178 ns, Max = 44.6669 ns
    IQR = 0.2089 ns, LowerFence = 43.1956 ns, UpperFence = 44.0311 ns
    ConfidenceInterval = [43.2368 ns; 44.0957 ns] (CI 99.9%), Margin = 0.4295 ns (0.98% of Mean)
    Skewness = 1.41, Kurtosis = 5.18
    
    
    GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=ILEmit]
    Runtime = ; GC = 
    Mean = 5.6016 ns, StdErr = 0.0071 ns (0.13%); N = 13, StdDev = 0.0255 ns
    Min = 5.5547 ns, Q1 = 5.5896 ns, Median = 5.5996 ns, Q3 = 5.6226 ns, Max = 5.6400 ns
    IQR = 0.0330 ns, LowerFence = 5.5401 ns, UpperFence = 5.6721 ns
    ConfidenceInterval = [5.5712 ns; 5.6321 ns] (CI 99.9%), Margin = 0.0305 ns (0.54% of Mean)
    Skewness = -0.47, Kurtosis = 2.12
    
    
    GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=ILEmit]
    Runtime = ; GC = 
    Mean = 43.1397 ns, StdErr = 0.0726 ns (0.17%); N = 15, StdDev = 0.2812 ns
    Min = 42.7061 ns, Q1 = 42.9064 ns, Median = 43.1052 ns, Q3 = 43.3093 ns, Max = 43.6443 ns
    IQR = 0.4028 ns, LowerFence = 42.3022 ns, UpperFence = 43.9135 ns
    ConfidenceInterval = [42.8392 ns; 43.4403 ns] (CI 99.9%), Margin = 0.3006 ns (0.70% of Mean)
    Skewness = 0.28, Kurtosis = 1.9
    
    
    GetServiceBenchmark.NoDI: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Runtime]
    Runtime = ; GC = 
    Mean = 6.4814 ns, StdErr = 0.0762 ns (1.18%); N = 100, StdDev = 0.7617 ns
    Min = 5.4979 ns, Q1 = 5.8327 ns, Median = 6.3039 ns, Q3 = 6.9775 ns, Max = 8.0420 ns
    IQR = 1.1448 ns, LowerFence = 4.1155 ns, UpperFence = 8.6947 ns
    ConfidenceInterval = [6.2231 ns; 6.7397 ns] (CI 99.9%), Margin = 0.2583 ns (3.99% of Mean)
    Skewness = 0.52, Kurtosis = 1.94
    
    
    GetServiceBenchmark.Transient: Job-NHLENA(Toolchain=InProcessToolchain, RunStrategy=Throughput) [Mode=Runtime]
    Runtime = ; GC = 
    Mean = 581.5066 ns, StdErr = 1.6962 ns (0.29%); N = 15, StdDev = 6.5695 ns
    Min = 571.4934 ns, Q1 = 576.3829 ns, Median = 580.8121 ns, Q3 = 587.2645 ns, Max = 596.3317 ns
    IQR = 10.8816 ns, LowerFence = 560.0605 ns, UpperFence = 603.5869 ns
    ConfidenceInterval = [574.4834 ns; 588.5297 ns] (CI 99.9%), Margin = 7.0232 ns (1.21% of Mean)
    Skewness = 0.58, Kurtosis = 2.48
    
    
    Total time: 00:03:11 (191.85 sec)
    

    第三方容器

    如果想用第三方容器替换ASP.NET Core原有的容器也是可以办到的。以最常见的Autofac为例,有两种实现方式:

    借助ConfigureContainer方法,要先在Program类中挂载AddAutofac方法。

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateWebHostBuilder(args).Build().Run();
        }
    
        public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
            WebHost.CreateDefaultBuilder(args).ConfigureServices(services => services.AddAutofac())
                .UseStartup<Startup>();
    }
    

    然后在Startup类中加入ConfigureContainer方法。

    public void ConfigureContainer(ContainerBuilder builder)
    {
        builder.RegisterModule(new DefaultModule());
    
    }
    

    例中的DefaultModule类按照Autofac的通用方式实现。

    public class DefaultModule : Module
    {
        protected override void Load(ContainerBuilder builder)
        {
            builder.RegisterType<HelloWorld>().As<IHelloWorld>();
        }
    }
    

    如果不想使用ConfigureContainer方法,也可以直接利用ConfigureServices方法:

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        services.AddMvc();
    
        var containerBuilder = new ContainerBuilder();
        containerBuilder.RegisterModule<DefaultModule>();
        containerBuilder.Populate(services);
        var container = containerBuilder.Build();
        return new AutofacServiceProvider(container);
    }
    

    需要注意的是,使用这种方式时,ConfigureServices方法的返回类型要从void改成IServiceProvider。

  • 相关阅读:
    【onenet-edp传输】1、调试上报数据点和端对端透传
    【PYQT5快速开发】重定义边框、QSS美化皮肤主题
    MySQL
    Flask-Login一个账号单用户在线
    DataTable按钮,排序,单元格颜色
    python
    MegaCli64 raid对应关系
    openstack server status
    js中使用JSON.parse转换json
    linux使用pyodbc和freetds连接sqlserver
  • 原文地址:https://www.cnblogs.com/kenwoo/p/9384835.html
Copyright © 2011-2022 走看看