зеркало из
1
0
Форкнуть 0

Split ServiceProvider/Engine/Scope and allocation optimization (#565)

This commit is contained in:
Pavel Krymets 2017-08-07 11:11:08 -07:00 коммит произвёл GitHub
Родитель d51fab28fa
Коммит 06e2de235c
29 изменённых файлов: 735 добавлений и 371 удалений

Просмотреть файл

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