Split ServiceProvider/Engine/Scope and allocation optimization (#565)
This commit is contained in:
Родитель
d51fab28fa
Коммит
06e2de235c
|
@ -1,7 +1,7 @@
|
|||
<Project>
|
||||
<PropertyGroup>
|
||||
<AspNetCoreVersion>2.1.0-*</AspNetCoreVersion>
|
||||
<BenchmarkDotNetVersion>0.10.3</BenchmarkDotNetVersion>
|
||||
<BenchmarkDotNetVersion>0.10.9</BenchmarkDotNetVersion>
|
||||
<InternalAspNetCoreSdkVersion>2.1.1-*</InternalAspNetCoreSdkVersion>
|
||||
<MoqVersion>4.7.49</MoqVersion>
|
||||
<NETStandardImplicitPackageVersion>2.0.0-*</NETStandardImplicitPackageVersion>
|
||||
|
|
|
@ -1,33 +0,0 @@
|
|||
using System;
|
||||
using System.Diagnostics;
|
||||
|
||||
namespace System.Collections.Concurrent
|
||||
{
|
||||
internal static class ConcurrentDictionaryExtensions
|
||||
{
|
||||
// From https://github.com/dotnet/corefx/issues/394#issuecomment-69494764
|
||||
// This lets us pass a state parameter allocation free GetOrAdd
|
||||
internal static TValue GetOrAdd<TKey, TValue, TArg>(this ConcurrentDictionary<TKey, TValue> dictionary, TKey key, Func<TKey, TArg, TValue> valueFactory, TArg arg)
|
||||
{
|
||||
Debug.Assert(dictionary != null);
|
||||
Debug.Assert(key != null);
|
||||
Debug.Assert(valueFactory != null);
|
||||
|
||||
while (true)
|
||||
{
|
||||
TValue value;
|
||||
if (dictionary.TryGetValue(key, out value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
value = valueFactory(key, arg);
|
||||
if (dictionary.TryAdd(key, value))
|
||||
{
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -4,3 +4,4 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
[assembly: InternalsVisibleTo("Microsoft.Extensions.DependencyInjection.Tests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
[assembly: InternalsVisibleTo("DI.Performance, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
|
||||
|
|
|
@ -10,136 +10,161 @@ using System.Threading;
|
|||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class CallSiteExpressionBuilder : CallSiteVisitor<ParameterExpression, Expression>
|
||||
internal class CallSiteExpressionBuilder : CallSiteVisitor<CallSiteExpressionBuilderContext, Expression>
|
||||
{
|
||||
private static readonly MethodInfo CaptureDisposableMethodInfo = GetMethodInfo<Func<ServiceProvider, object, object>>((a, b) => a.CaptureDisposable(b));
|
||||
private static readonly MethodInfo CaptureDisposableMethodInfo = GetMethodInfo<Func<ServiceProviderEngineScope, object, object>>((a, b) => a.CaptureDisposable(b));
|
||||
private static readonly MethodInfo TryGetValueMethodInfo = GetMethodInfo<Func<IDictionary<object, object>, object, object, bool>>((a, b, c) => a.TryGetValue(b, out c));
|
||||
private static readonly MethodInfo AddMethodInfo = GetMethodInfo<Action<IDictionary<object, object>, object, object>>((a, b, c) => a.Add(b, c));
|
||||
private static readonly MethodInfo MonitorEnterMethodInfo = GetMethodInfo<Action<object, bool>>((lockObj, lockTaken) => Monitor.Enter(lockObj, ref lockTaken));
|
||||
private static readonly MethodInfo MonitorExitMethodInfo = GetMethodInfo<Action<object>>(lockObj => Monitor.Exit(lockObj));
|
||||
private static readonly MethodInfo CallSiteRuntimeResolverResolve =
|
||||
GetMethodInfo<Func<CallSiteRuntimeResolver, IServiceCallSite, ServiceProvider, object>>((r, c, p) => r.Resolve(c, p));
|
||||
GetMethodInfo<Func<CallSiteRuntimeResolver, IServiceCallSite, ServiceProviderEngineScope, object>>((r, c, p) => r.Resolve(c, p));
|
||||
|
||||
private static readonly ParameterExpression ProviderParameter = Expression.Parameter(typeof(ServiceProvider));
|
||||
private static readonly MethodInfo ArrayEmptyMethodInfo = typeof(Array).GetMethod(nameof(Array.Empty));
|
||||
|
||||
private static readonly ParameterExpression ResolvedServices = Expression.Variable(typeof(IDictionary<object, object>),
|
||||
ProviderParameter.Name + "resolvedServices");
|
||||
private static readonly ParameterExpression ScopeParameter = Expression.Parameter(typeof(ServiceProviderEngineScope));
|
||||
|
||||
private static readonly ParameterExpression ResolvedServices = Expression.Variable(typeof(IDictionary<object, object>), ScopeParameter.Name + "resolvedServices");
|
||||
private static readonly BinaryExpression ResolvedServicesVariableAssignment =
|
||||
Expression.Assign(ResolvedServices,
|
||||
Expression.Property(ProviderParameter, nameof(ServiceProvider.ResolvedServices)));
|
||||
Expression.Property(ScopeParameter, nameof(ServiceProviderEngineScope.ResolvedServices)));
|
||||
|
||||
private static readonly ParameterExpression CaptureDisposableParameter = Expression.Parameter(typeof(object));
|
||||
private static readonly LambdaExpression CaptureDisposable = Expression.Lambda(
|
||||
Expression.Call(ProviderParameter, CaptureDisposableMethodInfo, CaptureDisposableParameter),
|
||||
Expression.Call(ScopeParameter, CaptureDisposableMethodInfo, CaptureDisposableParameter),
|
||||
CaptureDisposableParameter);
|
||||
|
||||
private readonly CallSiteRuntimeResolver _runtimeResolver;
|
||||
private bool _requiresResolvedServices;
|
||||
|
||||
public CallSiteExpressionBuilder(CallSiteRuntimeResolver runtimeResolver)
|
||||
private readonly IServiceScopeFactory _serviceScopeFactory;
|
||||
|
||||
private readonly ServiceProviderEngineScope _rootScope;
|
||||
|
||||
public CallSiteExpressionBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope)
|
||||
{
|
||||
if (runtimeResolver == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(runtimeResolver));
|
||||
}
|
||||
_runtimeResolver = runtimeResolver;
|
||||
_serviceScopeFactory = serviceScopeFactory;
|
||||
_rootScope = rootScope;
|
||||
}
|
||||
|
||||
public Func<ServiceProvider, object> Build(IServiceCallSite callSite)
|
||||
public Func<ServiceProviderEngineScope, object> Build(IServiceCallSite callSite)
|
||||
{
|
||||
if (callSite is SingletonCallSite)
|
||||
if (callSite is SingletonCallSite singletonCallSite)
|
||||
{
|
||||
// If root call site is singleton we can return Func calling
|
||||
// _runtimeResolver.Resolve directly and avoid Expression generation
|
||||
return (provider) => _runtimeResolver.Resolve(callSite, provider);
|
||||
if (TryResolveSingletonValue(singletonCallSite, out var value))
|
||||
{
|
||||
return scope => value;
|
||||
}
|
||||
|
||||
return scope => _runtimeResolver.Resolve(callSite, scope);
|
||||
}
|
||||
|
||||
return BuildExpression(callSite).Compile();
|
||||
}
|
||||
|
||||
private Expression<Func<ServiceProvider, object>> BuildExpression(IServiceCallSite callSite)
|
||||
private bool TryResolveSingletonValue(SingletonCallSite singletonCallSite, out object value)
|
||||
{
|
||||
var serviceExpression = VisitCallSite(callSite, ProviderParameter);
|
||||
|
||||
var body = new List<Expression>();
|
||||
if (_requiresResolvedServices)
|
||||
lock (_rootScope.ResolvedServices)
|
||||
{
|
||||
body.Add(ResolvedServicesVariableAssignment);
|
||||
serviceExpression = Lock(serviceExpression, ResolvedServices);
|
||||
return _rootScope.ResolvedServices.TryGetValue(singletonCallSite.CacheKey, out value);
|
||||
}
|
||||
|
||||
body.Add(serviceExpression);
|
||||
|
||||
var variables = _requiresResolvedServices
|
||||
? new[] { ResolvedServices }
|
||||
: Enumerable.Empty<ParameterExpression>();
|
||||
|
||||
return Expression.Lambda<Func<ServiceProvider, object>>(
|
||||
Expression.Block(variables, body),
|
||||
ProviderParameter);
|
||||
}
|
||||
|
||||
protected override Expression VisitSingleton(SingletonCallSite singletonCallSite, ParameterExpression provider)
|
||||
private Expression<Func<ServiceProviderEngineScope, object>> BuildExpression(IServiceCallSite callSite)
|
||||
{
|
||||
// Call to CallSiteRuntimeResolver.Resolve is being returned here
|
||||
// because in the current use case singleton service was already resolved and cached
|
||||
// to dictionary so there is no need to generate full tree at this point.
|
||||
var context = new CallSiteExpressionBuilderContext
|
||||
{
|
||||
ScopeParameter = ScopeParameter
|
||||
};
|
||||
|
||||
var serviceExpression = VisitCallSite(callSite, context);
|
||||
|
||||
if (context.RequiresResolvedServices)
|
||||
{
|
||||
return Expression.Lambda<Func<ServiceProviderEngineScope, object>>(
|
||||
Expression.Block(
|
||||
new [] { ResolvedServices },
|
||||
ResolvedServicesVariableAssignment,
|
||||
Lock(serviceExpression, ResolvedServices)),
|
||||
ScopeParameter);
|
||||
}
|
||||
|
||||
return Expression.Lambda<Func<ServiceProviderEngineScope, object>>(serviceExpression, ScopeParameter);
|
||||
}
|
||||
|
||||
protected override Expression VisitSingleton(SingletonCallSite singletonCallSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
if (TryResolveSingletonValue(singletonCallSite, out var value))
|
||||
{
|
||||
return Expression.Constant(value);
|
||||
}
|
||||
|
||||
return Expression.Call(
|
||||
Expression.Constant(_runtimeResolver),
|
||||
CallSiteRuntimeResolverResolve,
|
||||
Expression.Constant(singletonCallSite, typeof(IServiceCallSite)),
|
||||
provider);
|
||||
context.ScopeParameter);
|
||||
}
|
||||
|
||||
protected override Expression VisitConstant(ConstantCallSite constantCallSite, ParameterExpression provider)
|
||||
protected override Expression VisitConstant(ConstantCallSite constantCallSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
return Expression.Constant(constantCallSite.DefaultValue);
|
||||
}
|
||||
|
||||
protected override Expression VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, ParameterExpression provider)
|
||||
protected override Expression VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
return Expression.New(createInstanceCallSite.ImplementationType);
|
||||
}
|
||||
|
||||
protected override Expression VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, ParameterExpression provider)
|
||||
protected override Expression VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
return provider;
|
||||
return context.ScopeParameter;
|
||||
}
|
||||
|
||||
protected override Expression VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, ParameterExpression provider)
|
||||
protected override Expression VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
return Expression.New(typeof(ServiceScopeFactory).GetTypeInfo()
|
||||
.DeclaredConstructors
|
||||
.Single(),
|
||||
provider);
|
||||
return Expression.Constant(_serviceScopeFactory);
|
||||
}
|
||||
|
||||
protected override Expression VisitFactory(FactoryCallSite factoryCallSite, ParameterExpression provider)
|
||||
protected override Expression VisitFactory(FactoryCallSite factoryCallSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
return Expression.Invoke(Expression.Constant(factoryCallSite.Factory), provider);
|
||||
return Expression.Invoke(Expression.Constant(factoryCallSite.Factory), context.ScopeParameter);
|
||||
}
|
||||
|
||||
protected override Expression VisitIEnumerable(IEnumerableCallSite callSite, ParameterExpression provider)
|
||||
protected override Expression VisitIEnumerable(IEnumerableCallSite callSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
if (callSite.ServiceCallSites.Length == 0)
|
||||
{
|
||||
return Expression.Constant(ArrayEmptyMethodInfo
|
||||
.MakeGenericMethod(callSite.ItemType)
|
||||
.Invoke(obj: null, parameters: Array.Empty<object>()));
|
||||
}
|
||||
|
||||
return Expression.NewArrayInit(
|
||||
callSite.ItemType,
|
||||
callSite.ServiceCallSites.Select(cs =>
|
||||
Convert(
|
||||
VisitCallSite(cs, provider),
|
||||
VisitCallSite(cs, context),
|
||||
callSite.ItemType)));
|
||||
}
|
||||
|
||||
protected override Expression VisitTransient(TransientCallSite callSite, ParameterExpression provider)
|
||||
protected override Expression VisitTransient(TransientCallSite callSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
var implType = callSite.ServiceCallSite.ImplementationType;
|
||||
// Elide calls to GetCaptureDisposable if the implemenation type isn't disposable
|
||||
return TryCaptureDisposible(
|
||||
implType,
|
||||
provider,
|
||||
VisitCallSite(callSite.ServiceCallSite, provider));
|
||||
context.ScopeParameter,
|
||||
VisitCallSite(callSite.ServiceCallSite, context));
|
||||
}
|
||||
|
||||
private Expression TryCaptureDisposible(Type implType, ParameterExpression provider, Expression service)
|
||||
private Expression TryCaptureDisposible(Type implType, ParameterExpression scope, Expression service)
|
||||
{
|
||||
|
||||
if (implType != null &&
|
||||
|
@ -148,17 +173,17 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
return service;
|
||||
}
|
||||
|
||||
return Expression.Invoke(GetCaptureDisposable(provider),
|
||||
return Expression.Invoke(GetCaptureDisposable(scope),
|
||||
service);
|
||||
}
|
||||
|
||||
protected override Expression VisitConstructor(ConstructorCallSite callSite, ParameterExpression provider)
|
||||
protected override Expression VisitConstructor(ConstructorCallSite callSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
var parameters = callSite.ConstructorInfo.GetParameters();
|
||||
return Expression.New(
|
||||
callSite.ConstructorInfo,
|
||||
callSite.ParameterCallSites.Select((c, index) =>
|
||||
Convert(VisitCallSite(c, provider), parameters[index].ParameterType)));
|
||||
Convert(VisitCallSite(c, context), parameters[index].ParameterType)));
|
||||
}
|
||||
|
||||
private static Expression Convert(Expression expression, Type type)
|
||||
|
@ -172,7 +197,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
return Expression.Convert(expression, type);
|
||||
}
|
||||
|
||||
protected override Expression VisitScoped(ScopedCallSite callSite, ParameterExpression provider)
|
||||
protected override Expression VisitScoped(ScopedCallSite callSite, CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
var keyExpression = Expression.Constant(
|
||||
callSite.CacheKey,
|
||||
|
@ -180,7 +205,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
|
||||
var resolvedVariable = Expression.Variable(typeof(object), "resolved");
|
||||
|
||||
var resolvedServices = GetResolvedServices(provider);
|
||||
var resolvedServices = GetResolvedServices(context);
|
||||
|
||||
var tryGetValueExpression = Expression.Call(
|
||||
resolvedServices,
|
||||
|
@ -188,8 +213,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
keyExpression,
|
||||
resolvedVariable);
|
||||
|
||||
var service = VisitCallSite(callSite.ServiceCallSite, provider);
|
||||
var captureDisposible = TryCaptureDisposible(callSite.ImplementationType, provider, service);
|
||||
var service = VisitCallSite(callSite.ServiceCallSite, context);
|
||||
var captureDisposible = TryCaptureDisposible(callSite.ImplementationType, context.ScopeParameter, service);
|
||||
|
||||
var assignExpression = Expression.Assign(
|
||||
resolvedVariable,
|
||||
|
@ -222,22 +247,22 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
return mc.Method;
|
||||
}
|
||||
|
||||
public Expression GetCaptureDisposable(ParameterExpression provider)
|
||||
public Expression GetCaptureDisposable(ParameterExpression scope)
|
||||
{
|
||||
if (provider != ProviderParameter)
|
||||
if (scope != ScopeParameter)
|
||||
{
|
||||
throw new NotSupportedException("GetCaptureDisposable call is supported only for main provider");
|
||||
throw new NotSupportedException("GetCaptureDisposable call is supported only for main scope");
|
||||
}
|
||||
return CaptureDisposable;
|
||||
}
|
||||
|
||||
public Expression GetResolvedServices(ParameterExpression provider)
|
||||
public Expression GetResolvedServices(CallSiteExpressionBuilderContext context)
|
||||
{
|
||||
if (provider != ProviderParameter)
|
||||
if (context.ScopeParameter != ScopeParameter)
|
||||
{
|
||||
throw new NotSupportedException("GetResolvedServices call is supported only for main provider");
|
||||
throw new NotSupportedException("GetResolvedServices call is supported only for main scope");
|
||||
}
|
||||
_requiresResolvedServices = true;
|
||||
context.RequiresResolvedServices = true;
|
||||
return ResolvedServices;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System.Linq.Expressions;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class CallSiteExpressionBuilderContext
|
||||
{
|
||||
public ParameterExpression ScopeParameter { get; set; }
|
||||
public bool RequiresResolvedServices { get; set; }
|
||||
}
|
||||
}
|
|
@ -3,25 +3,25 @@ using System.Runtime.ExceptionServices;
|
|||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class CallSiteRuntimeResolver : CallSiteVisitor<ServiceProvider, object>
|
||||
internal class CallSiteRuntimeResolver : CallSiteVisitor<ServiceProviderEngineScope, object>
|
||||
{
|
||||
public object Resolve(IServiceCallSite callSite, ServiceProvider provider)
|
||||
public object Resolve(IServiceCallSite callSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
return VisitCallSite(callSite, provider);
|
||||
return VisitCallSite(callSite, scope);
|
||||
}
|
||||
|
||||
protected override object VisitTransient(TransientCallSite transientCallSite, ServiceProvider provider)
|
||||
protected override object VisitTransient(TransientCallSite transientCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
return provider.CaptureDisposable(
|
||||
VisitCallSite(transientCallSite.ServiceCallSite, provider));
|
||||
return scope.CaptureDisposable(
|
||||
VisitCallSite(transientCallSite.ServiceCallSite, scope));
|
||||
}
|
||||
|
||||
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProvider provider)
|
||||
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], provider);
|
||||
parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], scope);
|
||||
}
|
||||
|
||||
try
|
||||
|
@ -36,31 +36,31 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
}
|
||||
}
|
||||
|
||||
protected override object VisitSingleton(SingletonCallSite singletonCallSite, ServiceProvider provider)
|
||||
protected override object VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
return VisitScoped(singletonCallSite, provider.Root);
|
||||
return VisitScoped(singletonCallSite, scope.Engine.Root);
|
||||
}
|
||||
|
||||
protected override object VisitScoped(ScopedCallSite scopedCallSite, ServiceProvider provider)
|
||||
protected override object VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
lock (provider.ResolvedServices)
|
||||
lock (scope.ResolvedServices)
|
||||
{
|
||||
if (!provider.ResolvedServices.TryGetValue(scopedCallSite.CacheKey, out var resolved))
|
||||
if (!scope.ResolvedServices.TryGetValue(scopedCallSite.CacheKey, out var resolved))
|
||||
{
|
||||
resolved = VisitCallSite(scopedCallSite.ServiceCallSite, provider);
|
||||
provider.CaptureDisposable(resolved);
|
||||
provider.ResolvedServices.Add(scopedCallSite.CacheKey, resolved);
|
||||
resolved = VisitCallSite(scopedCallSite.ServiceCallSite, scope);
|
||||
scope.CaptureDisposable(resolved);
|
||||
scope.ResolvedServices.Add(scopedCallSite.CacheKey, resolved);
|
||||
}
|
||||
return resolved;
|
||||
}
|
||||
}
|
||||
|
||||
protected override object VisitConstant(ConstantCallSite constantCallSite, ServiceProvider provider)
|
||||
protected override object VisitConstant(ConstantCallSite constantCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
return constantCallSite.DefaultValue;
|
||||
}
|
||||
|
||||
protected override object VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, ServiceProvider provider)
|
||||
protected override object VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
@ -74,17 +74,17 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
}
|
||||
}
|
||||
|
||||
protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, ServiceProvider provider)
|
||||
protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
return provider;
|
||||
return scope;
|
||||
}
|
||||
|
||||
protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, ServiceProvider provider)
|
||||
protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
return new ServiceScopeFactory(provider);
|
||||
return scope.Engine;
|
||||
}
|
||||
|
||||
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, ServiceProvider provider)
|
||||
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
var array = Array.CreateInstance(
|
||||
enumerableCallSite.ItemType,
|
||||
|
@ -92,15 +92,15 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
|
||||
for (var index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)
|
||||
{
|
||||
var value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], provider);
|
||||
var value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], scope);
|
||||
array.SetValue(value, index);
|
||||
}
|
||||
return array;
|
||||
}
|
||||
|
||||
protected override object VisitFactory(FactoryCallSite factoryCallSite, ServiceProvider provider)
|
||||
protected override object VisitFactory(FactoryCallSite factoryCallSite, ServiceProviderEngineScope scope)
|
||||
{
|
||||
return factoryCallSite.Factory(provider);
|
||||
return factoryCallSite.Factory(scope);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -11,19 +11,19 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
// Keys are services being resolved via GetService, values - first scoped service in their call site tree
|
||||
private readonly ConcurrentDictionary<Type, Type> _scopedServices = new ConcurrentDictionary<Type, Type>();
|
||||
|
||||
public void ValidateCallSite(Type serviceType, IServiceCallSite callSite)
|
||||
public void ValidateCallSite(IServiceCallSite callSite)
|
||||
{
|
||||
var scoped = VisitCallSite(callSite, default(CallSiteValidatorState));
|
||||
if (scoped != null)
|
||||
{
|
||||
_scopedServices[serviceType] = scoped;
|
||||
_scopedServices[callSite.ServiceType] = scoped;
|
||||
}
|
||||
}
|
||||
|
||||
public void ValidateResolution(Type serviceType, ServiceProvider serviceProvider)
|
||||
public void ValidateResolution(Type serviceType, IServiceScope scope, IServiceScope rootScope)
|
||||
{
|
||||
Type scopedService;
|
||||
if (ReferenceEquals(serviceProvider, serviceProvider.Root)
|
||||
if (ReferenceEquals(scope, rootScope)
|
||||
&& _scopedServices.TryGetValue(serviceType, out scopedService))
|
||||
{
|
||||
if (serviceType == scopedService)
|
||||
|
|
|
@ -0,0 +1,22 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class CompiledServiceProviderEngine : ServiceProviderEngine
|
||||
{
|
||||
public CompiledServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
|
||||
{
|
||||
var realizedService = ExpressionBuilder.Build(callSite);
|
||||
RealizedServices[callSite.ServiceType] = realizedService;
|
||||
return realizedService;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class DynamicServiceProviderEngine : CompiledServiceProviderEngine
|
||||
{
|
||||
public DynamicServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
|
||||
{
|
||||
}
|
||||
|
||||
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);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal interface IServiceProviderEngine : IDisposable, IServiceProvider
|
||||
{
|
||||
IServiceScope RootScope { get; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal interface IServiceProviderEngineCallback
|
||||
{
|
||||
void OnCreate(IServiceCallSite callSite);
|
||||
void OnResolve(Type serviceType, IServiceScope scope);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class RuntimeServiceProviderEngine : ServiceProviderEngine
|
||||
{
|
||||
public RuntimeServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback) : base(serviceDescriptors, callback)
|
||||
{
|
||||
}
|
||||
|
||||
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
|
||||
{
|
||||
return scope =>
|
||||
{
|
||||
Func<ServiceProviderEngineScope, object> realizedService = p => RuntimeResolver.Resolve(callSite, p);
|
||||
|
||||
RealizedServices[callSite.ServiceType] = realizedService;
|
||||
return realizedService(scope);
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory
|
||||
{
|
||||
private readonly IServiceProviderEngineCallback _callback;
|
||||
|
||||
private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;
|
||||
|
||||
protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> serviceDescriptors, IServiceProviderEngineCallback callback)
|
||||
{
|
||||
_createServiceAccessor = CreateServiceAccessor;
|
||||
_callback = callback;
|
||||
|
||||
Root = new ServiceProviderEngineScope(this);
|
||||
RuntimeResolver = new CallSiteRuntimeResolver();
|
||||
ExpressionBuilder = new CallSiteExpressionBuilder(RuntimeResolver, this, Root);
|
||||
CallSiteFactory = new CallSiteFactory(serviceDescriptors);
|
||||
CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());
|
||||
CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite());
|
||||
}
|
||||
|
||||
internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> RealizedServices { get; } =
|
||||
new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>();
|
||||
|
||||
internal CallSiteFactory CallSiteFactory { get; }
|
||||
|
||||
protected CallSiteRuntimeResolver RuntimeResolver { get; }
|
||||
|
||||
protected CallSiteExpressionBuilder ExpressionBuilder { get; }
|
||||
|
||||
public ServiceProviderEngineScope Root { get; }
|
||||
|
||||
public IServiceScope RootScope => Root;
|
||||
|
||||
public object GetService(Type serviceType) => GetService(serviceType, Root);
|
||||
|
||||
protected abstract Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite);
|
||||
|
||||
public void Dispose() => Root.Dispose();
|
||||
|
||||
private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
|
||||
{
|
||||
var callSite = CallSiteFactory.CreateCallSite(serviceType, new HashSet<Type>());
|
||||
if (callSite != null)
|
||||
{
|
||||
_callback?.OnCreate(callSite);
|
||||
return RealizeService(callSite);
|
||||
}
|
||||
|
||||
return _ => null;
|
||||
}
|
||||
|
||||
internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope)
|
||||
{
|
||||
var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor);
|
||||
_callback?.OnResolve(serviceType, serviceProviderEngineScope);
|
||||
return realizedService.Invoke(serviceProviderEngineScope);
|
||||
}
|
||||
|
||||
public IServiceScope CreateScope()
|
||||
{
|
||||
return new ServiceProviderEngineScope(this);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,82 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class ServiceProviderEngineScope : IServiceScope, IServiceProvider
|
||||
{
|
||||
// For testing only
|
||||
internal Action<object> _captureDisposableCallback;
|
||||
|
||||
private List<IDisposable> _disposables = new List<IDisposable>();
|
||||
|
||||
private bool _disposeCalled;
|
||||
|
||||
public ServiceProviderEngineScope(ServiceProviderEngine engine)
|
||||
{
|
||||
Engine = engine;
|
||||
}
|
||||
|
||||
internal Dictionary<object, object> ResolvedServices { get; } = new Dictionary<object, object>();
|
||||
|
||||
public ServiceProviderEngine Engine { get; }
|
||||
|
||||
public object GetService(Type serviceType)
|
||||
{
|
||||
return Engine.GetService(serviceType, this);
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider => this;
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (ResolvedServices)
|
||||
{
|
||||
if (_disposeCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposeCalled = 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))
|
||||
{
|
||||
var disposable = service as IDisposable;
|
||||
if (disposable != null)
|
||||
{
|
||||
lock (ResolvedServices)
|
||||
{
|
||||
if (_disposables == null)
|
||||
{
|
||||
_disposables = new List<IDisposable>();
|
||||
}
|
||||
|
||||
_disposables.Add(disposable);
|
||||
}
|
||||
}
|
||||
}
|
||||
return service;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class ServiceScope : IServiceScope
|
||||
{
|
||||
private readonly ServiceProvider _scopedProvider;
|
||||
|
||||
public ServiceScope(ServiceProvider scopedProvider)
|
||||
{
|
||||
_scopedProvider = scopedProvider;
|
||||
}
|
||||
|
||||
public IServiceProvider ServiceProvider
|
||||
{
|
||||
get { return _scopedProvider; }
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
_scopedProvider.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,20 +0,0 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
||||
{
|
||||
internal class ServiceScopeFactory : IServiceScopeFactory
|
||||
{
|
||||
private readonly ServiceProvider _provider;
|
||||
|
||||
public ServiceScopeFactory(ServiceProvider provider)
|
||||
{
|
||||
_provider = provider;
|
||||
}
|
||||
|
||||
public IServiceScope CreateScope()
|
||||
{
|
||||
return new ServiceScope(new ServiceProvider(_provider));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,6 +8,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
|
|||
internal class ServiceScopeFactoryCallSite : IServiceCallSite
|
||||
{
|
||||
public Type ServiceType { get; } = typeof(IServiceScopeFactory);
|
||||
public Type ImplementationType { get; } = typeof(ServiceScopeFactory);
|
||||
public Type ImplementationType { get; } = typeof(ServiceProviderEngine);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,11 +2,7 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Reflection;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.DependencyInjection.ServiceLookup;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
|
@ -14,48 +10,34 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// <summary>
|
||||
/// The default IServiceProvider.
|
||||
/// </summary>
|
||||
public sealed class ServiceProvider : IServiceProvider, IDisposable
|
||||
public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback
|
||||
{
|
||||
// CallSiteRuntimeResolver is stateless so can be shared between all instances
|
||||
private static readonly CallSiteRuntimeResolver _callSiteRuntimeResolver = new CallSiteRuntimeResolver();
|
||||
private static readonly Func<Type, ServiceProvider, Func<ServiceProvider, object>> _createServiceAccessor = CreateServiceAccessor;
|
||||
private readonly IServiceProviderEngine _engine;
|
||||
|
||||
private readonly CallSiteValidator _callSiteValidator;
|
||||
private bool _disposeCalled;
|
||||
private List<IDisposable> _disposables;
|
||||
|
||||
// For testing only
|
||||
internal Action<object> _captureDisposableCallback;
|
||||
|
||||
internal ServiceProvider Root { get; }
|
||||
internal CallSiteFactory CallSiteFactory { get; }
|
||||
internal Dictionary<object, object> ResolvedServices { get; }
|
||||
internal ConcurrentDictionary<Type, Func<ServiceProvider, object>> RealizedServices { get; } = new ConcurrentDictionary<Type, Func<ServiceProvider, object>>();
|
||||
|
||||
internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
|
||||
{
|
||||
Root = this;
|
||||
|
||||
IServiceProviderEngineCallback callback = null;
|
||||
if (options.ValidateScopes)
|
||||
{
|
||||
callback = this;
|
||||
_callSiteValidator = new CallSiteValidator();
|
||||
}
|
||||
|
||||
CallSiteFactory = new CallSiteFactory(serviceDescriptors);
|
||||
ResolvedServices = new Dictionary<object, object>();
|
||||
|
||||
CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());
|
||||
CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite());
|
||||
}
|
||||
|
||||
// This constructor is called exclusively to create a child scope from the parent
|
||||
internal ServiceProvider(ServiceProvider parent)
|
||||
{
|
||||
Root = parent.Root;
|
||||
ResolvedServices = new Dictionary<object, object>();
|
||||
CallSiteFactory = parent.CallSiteFactory;
|
||||
RealizedServices = parent.RealizedServices;
|
||||
_callSiteValidator = parent._callSiteValidator;
|
||||
switch (options.Mode)
|
||||
{
|
||||
case ServiceProviderMode.Dynamic:
|
||||
_engine = new DynamicServiceProviderEngine(serviceDescriptors, callback);
|
||||
break;
|
||||
case ServiceProviderMode.Runtime:
|
||||
_engine = new RuntimeServiceProviderEngine(serviceDescriptors, callback);
|
||||
break;
|
||||
case ServiceProviderMode.Compiled:
|
||||
_engine = new CompiledServiceProviderEngine(serviceDescriptors, callback);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(options.Mode));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -63,92 +45,19 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// </summary>
|
||||
/// <param name="serviceType"></param>
|
||||
/// <returns></returns>
|
||||
public object GetService(Type serviceType)
|
||||
public object GetService(Type serviceType) => _engine.GetService(serviceType);
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose() => _engine.Dispose();
|
||||
|
||||
void IServiceProviderEngineCallback.OnCreate(IServiceCallSite callSite)
|
||||
{
|
||||
var realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor, this);
|
||||
|
||||
_callSiteValidator?.ValidateResolution(serviceType, this);
|
||||
|
||||
return realizedService.Invoke(this);
|
||||
_callSiteValidator.ValidateCallSite(callSite);
|
||||
}
|
||||
|
||||
private static Func<ServiceProvider, object> CreateServiceAccessor(Type serviceType, ServiceProvider serviceProvider)
|
||||
void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope)
|
||||
{
|
||||
var callSite = serviceProvider.CallSiteFactory.CreateCallSite(serviceType, new HashSet<Type>());
|
||||
if (callSite != null)
|
||||
{
|
||||
serviceProvider._callSiteValidator?.ValidateCallSite(serviceType, callSite);
|
||||
return RealizeService(serviceType, callSite);
|
||||
}
|
||||
|
||||
return _ => null;
|
||||
}
|
||||
|
||||
internal static Func<ServiceProvider, object> RealizeService(Type serviceType, IServiceCallSite callSite)
|
||||
{
|
||||
var callCount = 0;
|
||||
return provider =>
|
||||
{
|
||||
if (Interlocked.Increment(ref callCount) == 2)
|
||||
{
|
||||
Task.Run(() =>
|
||||
{
|
||||
var realizedService = new CallSiteExpressionBuilder(_callSiteRuntimeResolver)
|
||||
.Build(callSite);
|
||||
provider.RealizedServices[serviceType] = realizedService;
|
||||
});
|
||||
}
|
||||
|
||||
return _callSiteRuntimeResolver.Resolve(callSite, provider);
|
||||
};
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
lock (ResolvedServices)
|
||||
{
|
||||
if (_disposeCalled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_disposeCalled = true;
|
||||
if (_disposables != null)
|
||||
{
|
||||
for (int 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 (!object.ReferenceEquals(this, service))
|
||||
{
|
||||
var disposable = service as IDisposable;
|
||||
if (disposable != null)
|
||||
{
|
||||
lock (ResolvedServices)
|
||||
{
|
||||
if (_disposables == null)
|
||||
{
|
||||
_disposables = new List<IDisposable>();
|
||||
}
|
||||
|
||||
_disposables.Add(disposable);
|
||||
}
|
||||
}
|
||||
}
|
||||
return service;
|
||||
_callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
internal enum ServiceProviderMode
|
||||
{
|
||||
Dynamic,
|
||||
Runtime,
|
||||
Compiled
|
||||
}
|
||||
}
|
|
@ -2,8 +2,6 @@
|
|||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection
|
||||
{
|
||||
|
@ -19,5 +17,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
/// <c>true</c> to perform check verifying that scoped services never gets resolved from root provider; otherwise <c>false</c>.
|
||||
/// </summary>
|
||||
public bool ValidateScopes { get; set; }
|
||||
|
||||
internal ServiceProviderMode Mode { get; set; } = ServiceProviderMode.Dynamic;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp2.0</TargetFramework>
|
||||
<AssemblyName>Microsoft.Extensions.DependencyInjection.Performance</AssemblyName>
|
||||
<RootNamespace>Microsoft.Extensions.DependencyInjection.Performance</RootNamespace>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.Performance
|
||||
|
@ -17,30 +16,12 @@ namespace Microsoft.Extensions.DependencyInjection.Performance
|
|||
private IServiceProvider _transientSp;
|
||||
private IServiceScope _scopedSp;
|
||||
private IServiceProvider _singletonSp;
|
||||
private IServiceProvider _serviceScopeFactoryProvider;
|
||||
private IServiceProvider _serviceScope;
|
||||
private IServiceProvider _emptyEnumerable;
|
||||
|
||||
[Setup]
|
||||
public void Setup()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddTransient<A>();
|
||||
services.AddTransient<B>();
|
||||
services.AddTransient<C>();
|
||||
_transientSp = services.BuildServiceProvider();
|
||||
|
||||
|
||||
services = new ServiceCollection();
|
||||
services.AddScoped<A>();
|
||||
services.AddScoped<B>();
|
||||
services.AddScoped<C>();
|
||||
_scopedSp = services.BuildServiceProvider().CreateScope();
|
||||
|
||||
|
||||
services = new ServiceCollection();
|
||||
services.AddSingleton<A>();
|
||||
services.AddSingleton<B>();
|
||||
services.AddSingleton<C>();
|
||||
_singletonSp = services.BuildServiceProvider();
|
||||
}
|
||||
[Params(ServiceProviderMode.Compiled, ServiceProviderMode.Dynamic, ServiceProviderMode.Runtime)]
|
||||
internal ServiceProviderMode Mode { get; set; }
|
||||
|
||||
[Benchmark(Baseline = true, OperationsPerInvoke = OperationsPerInvoke)]
|
||||
public void NoDI()
|
||||
|
@ -52,6 +33,19 @@ namespace Microsoft.Extensions.DependencyInjection.Performance
|
|||
}
|
||||
}
|
||||
|
||||
[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()
|
||||
{
|
||||
|
@ -62,6 +56,19 @@ namespace Microsoft.Extensions.DependencyInjection.Performance
|
|||
}
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(Scoped))]
|
||||
public void SetupScoped()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddScoped<A>();
|
||||
services.AddScoped<B>();
|
||||
services.AddScoped<C>();
|
||||
_scopedSp = services.BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
}).CreateScope();
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
|
||||
public void Scoped()
|
||||
{
|
||||
|
@ -72,6 +79,19 @@ namespace Microsoft.Extensions.DependencyInjection.Performance
|
|||
}
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(Singleton))]
|
||||
public void SetupScopedSingleton()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
services.AddSingleton<A>();
|
||||
services.AddSingleton<B>();
|
||||
services.AddSingleton<C>();
|
||||
_singletonSp = services.BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
|
||||
public void Singleton()
|
||||
{
|
||||
|
@ -82,6 +102,60 @@ namespace Microsoft.Extensions.DependencyInjection.Performance
|
|||
}
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(ServiceScope))]
|
||||
public void ServiceScopeSetup()
|
||||
{
|
||||
_serviceScope = new ServiceCollection().BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
|
||||
public void ServiceScope()
|
||||
{
|
||||
for (int i = 0; i < OperationsPerInvoke; i++)
|
||||
{
|
||||
var temp = _serviceScope.CreateScope();
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(ServiceScopeProvider))]
|
||||
public void ServiceScopeProviderSetup()
|
||||
{
|
||||
_serviceScopeFactoryProvider = new ServiceCollection().BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
|
||||
public void ServiceScopeProvider()
|
||||
{
|
||||
for (int i = 0; i < OperationsPerInvoke; i++)
|
||||
{
|
||||
var temp = _serviceScopeFactoryProvider.GetService<IServiceScopeFactory>();
|
||||
}
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(EmptyEnumerable))]
|
||||
public void EmptyEnumerableSetup()
|
||||
{
|
||||
_emptyEnumerable = new ServiceCollection().BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
});
|
||||
}
|
||||
|
||||
[Benchmark(OperationsPerInvoke = OperationsPerInvoke)]
|
||||
public void EmptyEnumerable()
|
||||
{
|
||||
for (int i = 0; i < OperationsPerInvoke; i++)
|
||||
{
|
||||
_emptyEnumerable.GetService<IEnumerable<A>>();
|
||||
}
|
||||
}
|
||||
|
||||
private class A
|
||||
{
|
||||
public A(B b)
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace Microsoft.Extensions.DependencyInjection.Performance
|
|||
private IServiceProvider _transientSp;
|
||||
private IServiceProvider _transientSpScopeValidation;
|
||||
|
||||
[Setup]
|
||||
[GlobalSetup]
|
||||
public void Setup()
|
||||
{
|
||||
var services = new ServiceCollection();
|
||||
|
|
|
@ -0,0 +1,135 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using BenchmarkDotNet.Attributes;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.Performance
|
||||
{
|
||||
[Config(typeof(CoreConfig))]
|
||||
public class TimeToFirstServiceBenchmark
|
||||
{
|
||||
private IServiceProvider _transientSp;
|
||||
private IServiceScope _scopedSp;
|
||||
private IServiceProvider _singletonSp;
|
||||
private ServiceCollection _transientServices;
|
||||
private ServiceCollection _scopedServices;
|
||||
private ServiceCollection _singletonServices;
|
||||
|
||||
[Params(ServiceProviderMode.Compiled, ServiceProviderMode.Dynamic, ServiceProviderMode.Runtime)]
|
||||
internal ServiceProviderMode Mode { get; set; }
|
||||
|
||||
[Benchmark(Baseline = true)]
|
||||
public void NoDI()
|
||||
{
|
||||
var temp = new A(new B(new C()));
|
||||
temp.Foo();
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(BuildProvider))]
|
||||
public void SetupBuildProvider()
|
||||
{
|
||||
_transientServices = new ServiceCollection();
|
||||
_transientServices.AddTransient<A>();
|
||||
_transientServices.AddTransient<B>();
|
||||
_transientServices.AddTransient<C>();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void BuildProvider()
|
||||
{
|
||||
_transientSp = _transientServices.BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
});
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(Transient))]
|
||||
public void SetupTransient()
|
||||
{
|
||||
_transientServices = new ServiceCollection();
|
||||
_transientServices.AddTransient<A>();
|
||||
_transientServices.AddTransient<B>();
|
||||
_transientServices.AddTransient<C>();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Transient()
|
||||
{
|
||||
_transientSp = _transientServices.BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
});
|
||||
var temp = _transientSp.GetService<A>();
|
||||
temp.Foo();
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(Scoped))]
|
||||
public void SetupScoped()
|
||||
{
|
||||
_scopedServices = new ServiceCollection();
|
||||
_scopedServices.AddScoped<A>();
|
||||
_scopedServices.AddScoped<B>();
|
||||
_scopedServices.AddScoped<C>();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Scoped()
|
||||
{
|
||||
_scopedSp = _scopedServices.BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
}).CreateScope();
|
||||
var temp = _scopedSp.ServiceProvider.GetService<A>();
|
||||
temp.Foo();
|
||||
}
|
||||
|
||||
[GlobalSetup(Target = nameof(Singleton))]
|
||||
public void SetupSingleton()
|
||||
{
|
||||
_singletonServices = new ServiceCollection();
|
||||
_singletonServices.AddSingleton<A>();
|
||||
_singletonServices.AddSingleton<B>();
|
||||
_singletonServices.AddSingleton<C>();
|
||||
}
|
||||
|
||||
[Benchmark]
|
||||
public void Singleton()
|
||||
{
|
||||
_singletonSp = _singletonServices.BuildServiceProvider(new ServiceProviderOptions()
|
||||
{
|
||||
Mode = Mode
|
||||
});
|
||||
var temp = _singletonSp.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
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,14 +17,10 @@ namespace Microsoft.Extensions.DependencyInjection.Performance
|
|||
Add(JitOptimizationsValidator.FailOnError);
|
||||
Add(MemoryDiagnoser.Default);
|
||||
Add(StatisticColumn.OperationsPerSecond);
|
||||
|
||||
Add(Job.Default
|
||||
.With(BenchmarkDotNet.Environments.Runtime.Core)
|
||||
.WithRemoveOutliers(false)
|
||||
.With(RunStrategy.Throughput)
|
||||
.WithLaunchCount(3)
|
||||
.WithWarmupCount(5)
|
||||
.WithTargetCount(10));
|
||||
.With(RunStrategy.Throughput));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -81,17 +81,17 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
public void BuiltExpressionWillReturnResolvedServiceWhenAppropriate(
|
||||
ServiceDescriptor[] descriptors, Type serviceType, Func<object, object, bool> compare)
|
||||
{
|
||||
var provider = new ServiceProvider(descriptors, new ServiceProviderOptions { ValidateScopes = true });
|
||||
var provider = new DynamicServiceProviderEngine(descriptors, null);
|
||||
|
||||
var callSite = provider.CallSiteFactory.CreateCallSite(serviceType, new HashSet<Type>());
|
||||
var collectionCallSite = provider.CallSiteFactory.CreateCallSite(typeof(IEnumerable<>).MakeGenericType(serviceType), new HashSet<Type>());
|
||||
|
||||
var compiledCallSite = CompileCallSite(callSite);
|
||||
var compiledCollectionCallSite = CompileCallSite(collectionCallSite);
|
||||
var compiledCallSite = CompileCallSite(callSite, provider);
|
||||
var compiledCollectionCallSite = CompileCallSite(collectionCallSite, provider);
|
||||
|
||||
var service1 = Invoke(callSite, provider);
|
||||
var service2 = compiledCallSite(provider);
|
||||
var serviceEnumerator = ((IEnumerable)compiledCollectionCallSite(provider)).GetEnumerator();
|
||||
var service2 = compiledCallSite(provider.Root);
|
||||
var serviceEnumerator = ((IEnumerable)compiledCollectionCallSite(provider.Root)).GetEnumerator();
|
||||
|
||||
Assert.NotNull(service1);
|
||||
Assert.True(compare(service1, service2));
|
||||
|
@ -110,11 +110,11 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
descriptors.AddScoped<ServiceB>();
|
||||
descriptors.AddScoped<ServiceC>();
|
||||
|
||||
var provider = new ServiceProvider(descriptors, new ServiceProviderOptions { ValidateScopes = true });
|
||||
var provider = new DynamicServiceProviderEngine(descriptors, null);
|
||||
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new HashSet<Type>());
|
||||
var compiledCallSite = CompileCallSite(callSite);
|
||||
var compiledCallSite = CompileCallSite(callSite, provider);
|
||||
|
||||
var serviceC = (ServiceC)compiledCallSite(provider);
|
||||
var serviceC = (ServiceC)compiledCallSite(provider.Root);
|
||||
|
||||
Assert.NotNull(serviceC.ServiceB.ServiceA);
|
||||
Assert.Equal(serviceC, Invoke(callSite, provider));
|
||||
|
@ -132,15 +132,15 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
descriptors.Add(ServiceDescriptor.Describe(typeof(ServiceC), typeof(DisposableServiceC), lifetime));
|
||||
|
||||
var disposables = new List<object>();
|
||||
var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
|
||||
provider._captureDisposableCallback = obj =>
|
||||
var provider = new DynamicServiceProviderEngine(descriptors, null);
|
||||
provider.Root._captureDisposableCallback = obj =>
|
||||
{
|
||||
disposables.Add(obj);
|
||||
};
|
||||
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new HashSet<Type>());
|
||||
var compiledCallSite = CompileCallSite(callSite);
|
||||
var compiledCallSite = CompileCallSite(callSite, provider);
|
||||
|
||||
var serviceC = (DisposableServiceC)compiledCallSite(provider);
|
||||
var serviceC = (DisposableServiceC)compiledCallSite(provider.Root);
|
||||
|
||||
Assert.Equal(3, disposables.Count);
|
||||
}
|
||||
|
@ -158,15 +158,15 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
typeof(ServiceC), p => new DisposableServiceC(p.GetService<ServiceB>()), lifetime));
|
||||
|
||||
var disposables = new List<object>();
|
||||
var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
|
||||
provider._captureDisposableCallback = obj =>
|
||||
var provider = new DynamicServiceProviderEngine(descriptors, null);
|
||||
provider.Root._captureDisposableCallback = obj =>
|
||||
{
|
||||
disposables.Add(obj);
|
||||
};
|
||||
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new HashSet<Type>());
|
||||
var compiledCallSite = CompileCallSite(callSite);
|
||||
var compiledCallSite = CompileCallSite(callSite, provider);
|
||||
|
||||
var serviceC = (DisposableServiceC)compiledCallSite(provider);
|
||||
var serviceC = (DisposableServiceC)compiledCallSite(provider.Root);
|
||||
|
||||
Assert.Equal(3, disposables.Count);
|
||||
}
|
||||
|
@ -187,15 +187,15 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
descriptors.AddTransient<ServiceC>();
|
||||
|
||||
var disposables = new List<object>();
|
||||
var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
|
||||
provider._captureDisposableCallback = obj =>
|
||||
var provider = new DynamicServiceProviderEngine(descriptors, null);
|
||||
provider.Root._captureDisposableCallback = obj =>
|
||||
{
|
||||
disposables.Add(obj);
|
||||
};
|
||||
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new HashSet<Type>());
|
||||
var compiledCallSite = CompileCallSite(callSite);
|
||||
var compiledCallSite = CompileCallSite(callSite, provider);
|
||||
|
||||
var serviceC = (ServiceC)compiledCallSite(provider);
|
||||
var serviceC = (ServiceC)compiledCallSite(provider.Root);
|
||||
|
||||
Assert.Equal(0, disposables.Count);
|
||||
}
|
||||
|
@ -212,15 +212,15 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
descriptors.Add(ServiceDescriptor.Describe(typeof(ServiceD), typeof(ServiceD), lifetime));
|
||||
|
||||
var disposables = new List<object>();
|
||||
var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
|
||||
provider._captureDisposableCallback = obj =>
|
||||
var provider = new DynamicServiceProviderEngine(descriptors, null);
|
||||
provider.Root._captureDisposableCallback = obj =>
|
||||
{
|
||||
disposables.Add(obj);
|
||||
};
|
||||
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceD), new HashSet<Type>());
|
||||
var compiledCallSite = CompileCallSite(callSite);
|
||||
var compiledCallSite = CompileCallSite(callSite, provider);
|
||||
|
||||
var serviceD = (ServiceD)compiledCallSite(provider);
|
||||
var serviceD = (ServiceD)compiledCallSite(provider.Root);
|
||||
|
||||
Assert.Equal(0, disposables.Count);
|
||||
}
|
||||
|
@ -233,18 +233,18 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
descriptors.AddTransient<ClassWithThrowingCtor>();
|
||||
descriptors.AddTransient<IFakeService, FakeService>();
|
||||
|
||||
var provider = new ServiceProvider(descriptors, new ServiceProviderOptions { ValidateScopes = true });
|
||||
var provider = new DynamicServiceProviderEngine(descriptors, null);
|
||||
|
||||
var callSite1 = provider.CallSiteFactory.CreateCallSite(typeof(ClassWithThrowingEmptyCtor), new HashSet<Type>());
|
||||
var compiledCallSite1 = CompileCallSite(callSite1);
|
||||
var compiledCallSite1 = CompileCallSite(callSite1, provider);
|
||||
|
||||
var callSite2 = provider.CallSiteFactory.CreateCallSite(typeof(ClassWithThrowingCtor), new HashSet<Type>());
|
||||
var compiledCallSite2 = CompileCallSite(callSite2);
|
||||
var compiledCallSite2 = CompileCallSite(callSite2, provider);
|
||||
|
||||
var ex1 = Assert.Throws<Exception>(() => compiledCallSite1(provider));
|
||||
var ex1 = Assert.Throws<Exception>(() => compiledCallSite1(provider.Root));
|
||||
Assert.Equal(nameof(ClassWithThrowingEmptyCtor), ex1.Message);
|
||||
|
||||
var ex2 = Assert.Throws<Exception>(() => compiledCallSite2(provider));
|
||||
var ex2 = Assert.Throws<Exception>(() => compiledCallSite2(provider.Root));
|
||||
Assert.Equal(nameof(ClassWithThrowingCtor), ex2.Message);
|
||||
}
|
||||
|
||||
|
@ -311,14 +311,14 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
}
|
||||
}
|
||||
|
||||
private static object Invoke(IServiceCallSite callSite, ServiceProvider provider)
|
||||
private static object Invoke(IServiceCallSite callSite, ServiceProviderEngine provider)
|
||||
{
|
||||
return CallSiteRuntimeResolver.Resolve(callSite, provider);
|
||||
return CallSiteRuntimeResolver.Resolve(callSite, provider.Root);
|
||||
}
|
||||
|
||||
private static Func<ServiceProvider, object> CompileCallSite(IServiceCallSite callSite)
|
||||
private static Func<ServiceProviderEngineScope, object> CompileCallSite(IServiceCallSite callSite, ServiceProviderEngine engine)
|
||||
{
|
||||
return new CallSiteExpressionBuilder(CallSiteRuntimeResolver).Build(callSite);
|
||||
return new CallSiteExpressionBuilder(CallSiteRuntimeResolver, engine, engine.Root).Build(callSite);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.Tests
|
||||
{
|
||||
public class ServiceProviderCompiledContainerTests : ServiceProviderContainerTests
|
||||
{
|
||||
protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) =>
|
||||
collection.BuildServiceProvider(new ServiceProviderOptions { Mode = ServiceProviderMode.Compiled });
|
||||
}
|
||||
}
|
|
@ -10,11 +10,8 @@ using Xunit;
|
|||
|
||||
namespace Microsoft.Extensions.DependencyInjection.Tests
|
||||
{
|
||||
public class ServiceProviderContainerTests : DependencyInjectionSpecificationTests
|
||||
public abstract class ServiceProviderContainerTests : DependencyInjectionSpecificationTests
|
||||
{
|
||||
protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) =>
|
||||
collection.BuildServiceProvider();
|
||||
|
||||
[Fact]
|
||||
public void RethrowOriginalExceptionFromConstructor()
|
||||
{
|
||||
|
@ -23,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
serviceCollection.AddTransient<ClassWithThrowingCtor>();
|
||||
serviceCollection.AddTransient<IFakeService, FakeService>();
|
||||
|
||||
var provider = serviceCollection.BuildServiceProvider();
|
||||
var provider = CreateServiceProvider(serviceCollection);
|
||||
|
||||
var ex1 = Assert.Throws<Exception>(() => provider.GetService<ClassWithThrowingEmptyCtor>());
|
||||
Assert.Equal(nameof(ClassWithThrowingEmptyCtor), ex1.Message);
|
||||
|
@ -41,7 +38,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddTransient<ClassWithPrivateCtor>();
|
||||
serviceCollection.AddTransient<ClassDependsOnPrivateConstructorClass>();
|
||||
var serviceProvider = serviceCollection.BuildServiceProvider();
|
||||
var serviceProvider = CreateServiceProvider(serviceCollection);
|
||||
|
||||
// Act and Assert
|
||||
var ex = Assert.Throws<InvalidOperationException>(() => serviceProvider.GetServices<ClassDependsOnPrivateConstructorClass>());
|
||||
|
@ -108,10 +105,10 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
var serviceCollection = new ServiceCollection();
|
||||
serviceCollection.AddSingleton(disposable);
|
||||
|
||||
var provider = serviceCollection.BuildServiceProvider();
|
||||
var provider = CreateServiceProvider(serviceCollection);
|
||||
provider.GetService<Disposable>();
|
||||
|
||||
provider.Dispose();
|
||||
((IDisposable)provider).Dispose();
|
||||
|
||||
Assert.False(disposable.Disposed);
|
||||
}
|
||||
|
@ -124,7 +121,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
|
|||
serviceCollection.AddSingleton<IFakeService, FakeService>();
|
||||
serviceCollection.AddSingleton<ClassWithServiceAndOptionalArgsCtorWithStructs>();
|
||||
|
||||
var provider = serviceCollection.BuildServiceProvider();
|
||||
var provider = CreateServiceProvider(serviceCollection);
|
||||
var service = provider.GetService<ClassWithServiceAndOptionalArgsCtorWithStructs>();
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,13 @@
|
|||
// Copyright (c) .NET Foundation. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
|
||||
|
||||
using System;
|
||||
|
||||
namespace Microsoft.Extensions.DependencyInjection.Tests
|
||||
{
|
||||
public class ServiceProviderDynamicContainerTests : ServiceProviderContainerTests
|
||||
{
|
||||
protected override IServiceProvider CreateServiceProvider(IServiceCollection collection) =>
|
||||
collection.BuildServiceProvider();
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче