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

Make lifetime a callsite property and add stackguards (#638)

This commit is contained in:
Pavel Krymets 2018-10-17 15:12:09 -07:00 коммит произвёл GitHub
Родитель ca90dc6871
Коммит d77b090567
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
34 изменённых файлов: 583 добавлений и 369 удалений

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

@ -2,6 +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.Diagnostics;
using System.Linq;
@ -14,12 +15,16 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class CallSiteFactory
{
private const int DefaultSlot = 0;
private readonly List<ServiceDescriptor> _descriptors;
private readonly Dictionary<Type, IServiceCallSite> _callSiteCache = new Dictionary<Type, IServiceCallSite>();
private readonly ConcurrentDictionary<Type, ServiceCallSite> _callSiteCache = new ConcurrentDictionary<Type, ServiceCallSite>();
private readonly Dictionary<Type, ServiceDescriptorCacheItem> _descriptorLookup = new Dictionary<Type, ServiceDescriptorCacheItem>();
private readonly StackGuard _stackGuard;
public CallSiteFactory(IEnumerable<ServiceDescriptor> descriptors)
{
_stackGuard = new StackGuard();
_descriptors = descriptors.ToList();
Populate(descriptors);
}
@ -66,57 +71,63 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
}
}
internal IServiceCallSite CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
internal ServiceCallSite GetCallSite(Type serviceType, CallSiteChain callSiteChain)
{
lock (_callSiteCache)
{
if (_callSiteCache.TryGetValue(serviceType, out var cachedCallSite))
{
return cachedCallSite;
}
IServiceCallSite callSite;
try
{
callSiteChain.CheckCircularDependency(serviceType);
callSite = TryCreateExact(serviceType, callSiteChain) ??
TryCreateOpenGeneric(serviceType, callSiteChain) ??
TryCreateEnumerable(serviceType, callSiteChain);
}
finally
{
callSiteChain.Remove(serviceType);
}
_callSiteCache[serviceType] = callSite;
return callSite;
}
#if NETCOREAPP2_0
return _callSiteCache.GetOrAdd(serviceType, (type, chain) => CreateCallSite(type, chain), callSiteChain);
#else
return _callSiteCache.GetOrAdd(serviceType, type => CreateCallSite(type, callSiteChain));
#endif
}
private IServiceCallSite TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
private ServiceCallSite CreateCallSite(Type serviceType, CallSiteChain callSiteChain)
{
if (!_stackGuard.TryEnterOnCurrentStack())
{
return _stackGuard.RunOnEmptyStack((type, chain) => CreateCallSite(type, chain), serviceType, callSiteChain);
}
ServiceCallSite callSite;
try
{
callSiteChain.CheckCircularDependency(serviceType);
callSite = TryCreateExact(serviceType, callSiteChain) ??
TryCreateOpenGeneric(serviceType, callSiteChain) ??
TryCreateEnumerable(serviceType, callSiteChain);
}
finally
{
callSiteChain.Remove(serviceType);
}
_callSiteCache[serviceType] = callSite;
return callSite;
}
private ServiceCallSite TryCreateExact(Type serviceType, CallSiteChain callSiteChain)
{
if (_descriptorLookup.TryGetValue(serviceType, out var descriptor))
{
return TryCreateExact(descriptor.Last, serviceType, callSiteChain);
return TryCreateExact(descriptor.Last, serviceType, callSiteChain, DefaultSlot);
}
return null;
}
private IServiceCallSite TryCreateOpenGeneric(Type serviceType, CallSiteChain callSiteChain)
private ServiceCallSite TryCreateOpenGeneric(Type serviceType, CallSiteChain callSiteChain)
{
if (serviceType.IsConstructedGenericType
&& _descriptorLookup.TryGetValue(serviceType.GetGenericTypeDefinition(), out var descriptor))
{
return TryCreateOpenGeneric(descriptor.Last, serviceType, callSiteChain);
return TryCreateOpenGeneric(descriptor.Last, serviceType, callSiteChain, DefaultSlot);
}
return null;
}
private IServiceCallSite TryCreateEnumerable(Type serviceType, CallSiteChain callSiteChain)
private ServiceCallSite TryCreateEnumerable(Type serviceType, CallSiteChain callSiteChain)
{
if (serviceType.IsConstructedGenericType &&
serviceType.GetGenericTypeDefinition() == typeof(IEnumerable<>))
@ -124,7 +135,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var itemType = serviceType.GenericTypeArguments.Single();
callSiteChain.Add(serviceType);
var callSites = new List<IServiceCallSite>();
var callSites = new List<ServiceCallSite>();
// If item type is not generic we can safely use descriptor cache
if (!itemType.IsConstructedGenericType &&
@ -134,8 +145,10 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
var descriptor = descriptors[i];
// Last service should get slot 0
var slot = descriptors.Count - i - 1;
// There may not be any open generics here
var callSite = TryCreateExact(descriptor, itemType, callSiteChain);
var callSite = TryCreateExact(descriptor, itemType, callSiteChain, slot);
Debug.Assert(callSite != null);
callSites.Add(callSite);
@ -143,17 +156,21 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
}
else
{
foreach (var descriptor in _descriptors)
var slot = 0;
// We are going in reverse so the last service in descriptor list gets slot 0
for (var i = _descriptors.Count - 1; i >= 0; i--)
{
var callSite = TryCreateExact(descriptor, itemType, callSiteChain) ??
TryCreateOpenGeneric(descriptor, itemType, callSiteChain);
var descriptor = _descriptors[i];
var callSite = TryCreateExact(descriptor, itemType, callSiteChain, slot) ??
TryCreateOpenGeneric(descriptor, itemType, callSiteChain, slot);
slot++;
if (callSite != null)
{
callSites.Add(callSite);
}
}
callSites.Reverse();
}
return new IEnumerableCallSite(itemType, callSites.ToArray());
@ -162,71 +179,51 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return null;
}
private IServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain)
private ServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot)
{
if (serviceType == descriptor.ServiceType)
{
IServiceCallSite callSite;
ServiceCallSite callSite;
var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot);
if (descriptor.ImplementationInstance != null)
{
callSite = new ConstantCallSite(descriptor.ServiceType, descriptor.ImplementationInstance);
}
else if (descriptor.ImplementationFactory != null)
{
callSite = new FactoryCallSite(descriptor.ServiceType, descriptor.ImplementationFactory);
callSite = new FactoryCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationFactory);
}
else if (descriptor.ImplementationType != null)
{
callSite = CreateConstructorCallSite(descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
callSite = CreateConstructorCallSite(lifetime, descriptor.ServiceType, descriptor.ImplementationType, callSiteChain);
}
else
{
throw new InvalidOperationException("Invalid service descriptor");
}
return ApplyLifetime(callSite, descriptor, descriptor.Lifetime);
return callSite;
}
return null;
}
private IServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain)
private ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot)
{
if (serviceType.IsConstructedGenericType &&
serviceType.GetGenericTypeDefinition() == descriptor.ServiceType)
{
Debug.Assert(descriptor.ImplementationType != null, "descriptor.ImplementationType != null");
var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot);
var closedType = descriptor.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments);
var constructorCallSite = CreateConstructorCallSite(serviceType, closedType, callSiteChain);
return ApplyLifetime(constructorCallSite, Tuple.Create(descriptor, serviceType), descriptor.Lifetime);
return CreateConstructorCallSite(lifetime, serviceType, closedType, callSiteChain);
}
return null;
}
private IServiceCallSite ApplyLifetime(IServiceCallSite serviceCallSite, object cacheKey, ServiceLifetime descriptorLifetime)
{
if (serviceCallSite is ConstantCallSite)
{
return serviceCallSite;
}
switch (descriptorLifetime)
{
case ServiceLifetime.Transient:
return new TransientCallSite(serviceCallSite);
case ServiceLifetime.Scoped:
return new ScopedCallSite(serviceCallSite, cacheKey);
case ServiceLifetime.Singleton:
return new SingletonCallSite(serviceCallSite, cacheKey);
default:
throw new ArgumentOutOfRangeException(nameof(descriptorLifetime));
}
}
private IServiceCallSite CreateConstructorCallSite(Type serviceType, Type implementationType, CallSiteChain callSiteChain)
private ServiceCallSite CreateConstructorCallSite(ResultCache lifetime, Type serviceType, Type implementationType,
CallSiteChain callSiteChain)
{
callSiteChain.Add(serviceType, implementationType);
@ -235,7 +232,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
.Where(constructor => constructor.IsPublic)
.ToArray();
IServiceCallSite[] parameterCallSites = null;
ServiceCallSite[] parameterCallSites = null;
if (constructors.Length == 0)
{
@ -247,7 +244,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var parameters = constructor.GetParameters();
if (parameters.Length == 0)
{
return new CreateInstanceCallSite(serviceType, implementationType);
return new ConstructorCallSite(lifetime, serviceType, constructor);
}
parameterCallSites = CreateArgumentCallSites(
@ -257,7 +254,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
parameters,
throwIfCallSiteNotFound: true);
return new ConstructorCallSite(serviceType, constructor, parameterCallSites);
return new ConstructorCallSite(lifetime, serviceType, constructor, parameterCallSites);
}
Array.Sort(constructors,
@ -316,23 +313,21 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
else
{
Debug.Assert(parameterCallSites != null);
return parameterCallSites.Length == 0 ?
(IServiceCallSite)new CreateInstanceCallSite(serviceType, implementationType) :
new ConstructorCallSite(serviceType, bestConstructor, parameterCallSites);
return new ConstructorCallSite(lifetime, serviceType, bestConstructor, parameterCallSites);
}
}
private IServiceCallSite[] CreateArgumentCallSites(
private ServiceCallSite[] CreateArgumentCallSites(
Type serviceType,
Type implementationType,
CallSiteChain callSiteChain,
ParameterInfo[] parameters,
bool throwIfCallSiteNotFound)
{
var parameterCallSites = new IServiceCallSite[parameters.Length];
var parameterCallSites = new ServiceCallSite[parameters.Length];
for (var index = 0; index < parameters.Length; index++)
{
var callSite = CreateCallSite(parameters[index].ParameterType, callSiteChain);
var callSite = GetCallSite(parameters[index].ParameterType, callSiteChain);
if (callSite == null && ParameterDefaultValue.TryGetDefaultValue(parameters[index], out var defaultValue))
{
@ -358,7 +353,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
}
public void Add(Type type, IServiceCallSite serviceCallSite)
public void Add(Type type, ServiceCallSite serviceCallSite)
{
_callSiteCache[type] = serviceCallSite;
}

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

@ -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.
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal enum CallSiteResultCacheLocation
{
Root,
Scope,
Dispose,
None
}
}

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

@ -1,27 +1,38 @@
using System;
using System.Runtime.ExceptionServices;
using System.Threading;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class CallSiteRuntimeResolver : CallSiteVisitor<ServiceProviderEngineScope, object>
internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>
{
public object Resolve(IServiceCallSite callSite, ServiceProviderEngineScope scope)
public object Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
{
return VisitCallSite(callSite, scope);
}
protected override object VisitTransient(TransientCallSite transientCallSite, ServiceProviderEngineScope scope)
{
return scope.CaptureDisposable(
VisitCallSite(transientCallSite.ServiceCallSite, scope));
}
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, ServiceProviderEngineScope scope)
{
object[] parameterValues = new object[constructorCallSite.ParameterCallSites.Length];
for (var index = 0; index < parameterValues.Length; index++)
return VisitCallSite(callSite, new RuntimeResolverContext
{
parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], scope);
Scope = scope
});
}
protected override object VisitDisposeCache(ServiceCallSite transientCallSite, RuntimeResolverContext context)
{
return context.Scope.CaptureDisposable(VisitCallSiteMain(transientCallSite, context));
}
protected override object VisitConstructor(ConstructorCallSite constructorCallSite, RuntimeResolverContext context)
{
object[] parameterValues;
if (constructorCallSite.ParameterCallSites.Length == 0)
{
parameterValues = Array.Empty<object>();
}
else
{
parameterValues = new object[constructorCallSite.ParameterCallSites.Length];
for (var index = 0; index < parameterValues.Length; index++)
{
parameterValues[index] = VisitCallSite(constructorCallSite.ParameterCallSites[index], context);
}
}
try
@ -36,55 +47,77 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
}
}
protected override object VisitSingleton(SingletonCallSite singletonCallSite, ServiceProviderEngineScope scope)
protected override object VisitRootCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
{
return VisitScoped(singletonCallSite, scope.Engine.Root);
return VisitCache(singletonCallSite, context, context.Scope.Engine.Root, RuntimeResolverLock.Root);
}
protected override object VisitScoped(ScopedCallSite scopedCallSite, ServiceProviderEngineScope scope)
protected override object VisitScopeCache(ServiceCallSite singletonCallSite, RuntimeResolverContext context)
{
lock (scope.ResolvedServices)
// Check if we are in the situation where scoped service was promoted to singleton
// and we need to lock the root
var requiredScope = context.Scope == context.Scope.Engine.Root ?
RuntimeResolverLock.Root :
RuntimeResolverLock.Scope;
return VisitCache(singletonCallSite, context, context.Scope, requiredScope);
}
private object VisitCache(ServiceCallSite scopedCallSite, RuntimeResolverContext context, ServiceProviderEngineScope serviceProviderEngine, RuntimeResolverLock lockType)
{
bool lockTaken = false;
var resolvedServices = serviceProviderEngine.ResolvedServices;
// Taking locks only once allows us to fork resolution process
// on another thread without causing the deadlock because we
// always know that we are going to wait the other thread to finish before
// releasing the lock
if ((context.AcquiredLocks & lockType) == 0)
{
if (!scope.ResolvedServices.TryGetValue(scopedCallSite.CacheKey, out var resolved))
Monitor.Enter(resolvedServices, ref lockTaken);
}
try
{
if (!resolvedServices.TryGetValue(scopedCallSite.Cache.Key, out var resolved))
{
resolved = VisitCallSite(scopedCallSite.ServiceCallSite, scope);
scope.CaptureDisposable(resolved);
scope.ResolvedServices.Add(scopedCallSite.CacheKey, resolved);
resolved = VisitCallSiteMain(scopedCallSite, new RuntimeResolverContext
{
Scope = serviceProviderEngine,
AcquiredLocks = context.AcquiredLocks | lockType
});
serviceProviderEngine.CaptureDisposable(resolved);
resolvedServices.Add(scopedCallSite.Cache.Key, resolved);
}
return resolved;
}
finally
{
if (lockTaken)
{
Monitor.Exit(resolvedServices);
}
}
}
protected override object VisitConstant(ConstantCallSite constantCallSite, ServiceProviderEngineScope scope)
protected override object VisitConstant(ConstantCallSite constantCallSite, RuntimeResolverContext context)
{
return constantCallSite.DefaultValue;
}
protected override object VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, ServiceProviderEngineScope scope)
protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, RuntimeResolverContext context)
{
try
{
return Activator.CreateInstance(createInstanceCallSite.ImplementationType);
}
catch (Exception ex) when (ex.InnerException != null)
{
ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
// The above line will always throw, but the compiler requires we throw explicitly.
throw;
}
return context.Scope;
}
protected override object VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, ServiceProviderEngineScope scope)
protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, RuntimeResolverContext context)
{
return scope;
return context.Scope.Engine;
}
protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, ServiceProviderEngineScope scope)
{
return scope.Engine;
}
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, ServiceProviderEngineScope scope)
protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
{
var array = Array.CreateInstance(
enumerableCallSite.ItemType,
@ -92,15 +125,29 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
for (var index = 0; index < enumerableCallSite.ServiceCallSites.Length; index++)
{
var value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], scope);
var value = VisitCallSite(enumerableCallSite.ServiceCallSites[index], context);
array.SetValue(value, index);
}
return array;
}
protected override object VisitFactory(FactoryCallSite factoryCallSite, ServiceProviderEngineScope scope)
protected override object VisitFactory(FactoryCallSite factoryCallSite, RuntimeResolverContext context)
{
return factoryCallSite.Factory(scope);
return factoryCallSite.Factory(context.Scope);
}
}
internal struct RuntimeResolverContext
{
public ServiceProviderEngineScope Scope { get; set; }
public RuntimeResolverLock AcquiredLocks { get; set; }
}
[Flags]
internal enum RuntimeResolverLock
{
Scope = 1,
Root = 2
}
}

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

@ -11,7 +11,7 @@ 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(IServiceCallSite callSite)
public void ValidateCallSite(ServiceCallSite callSite)
{
var scoped = VisitCallSite(callSite, default);
if (scoped != null)
@ -40,11 +40,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
}
}
protected override Type VisitTransient(TransientCallSite transientCallSite, CallSiteValidatorState state)
{
return VisitCallSite(transientCallSite.ServiceCallSite, state);
}
protected override Type VisitConstructor(ConstructorCallSite constructorCallSite, CallSiteValidatorState state)
{
Type result = null;
@ -74,16 +69,16 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return result;
}
protected override Type VisitSingleton(SingletonCallSite singletonCallSite, CallSiteValidatorState state)
protected override Type VisitRootCache(ServiceCallSite singletonCallSite, CallSiteValidatorState state)
{
state.Singleton = singletonCallSite;
return VisitCallSite(singletonCallSite.ServiceCallSite, state);
return VisitCallSiteMain(singletonCallSite, state);
}
protected override Type VisitScoped(ScopedCallSite scopedCallSite, CallSiteValidatorState state)
protected override Type VisitScopeCache(ServiceCallSite scopedCallSite, CallSiteValidatorState state)
{
// We are fine with having ServiceScopeService requested by singletons
if (scopedCallSite.ServiceCallSite is ServiceScopeFactoryCallSite)
if (scopedCallSite is ServiceScopeFactoryCallSite)
{
return null;
}
@ -97,14 +92,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
));
}
VisitCallSite(scopedCallSite.ServiceCallSite, state);
VisitCallSiteMain(scopedCallSite, state);
return scopedCallSite.ServiceType;
}
protected override Type VisitConstant(ConstantCallSite constantCallSite, CallSiteValidatorState state) => null;
protected override Type VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, CallSiteValidatorState state) => null;
protected override Type VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, CallSiteValidatorState state) => null;
protected override Type VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, CallSiteValidatorState state) => null;
@ -113,7 +106,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
internal struct CallSiteValidatorState
{
public SingletonCallSite Singleton { get; set; }
public ServiceCallSite Singleton { get; set; }
}
}
}

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

@ -1,10 +1,40 @@
using System;
using System.Runtime.CompilerServices;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal abstract class CallSiteVisitor<TArgument, TResult>
{
protected virtual TResult VisitCallSite(IServiceCallSite callSite, TArgument argument)
private readonly StackGuard _stackGuard;
protected CallSiteVisitor()
{
_stackGuard = new StackGuard();
}
protected virtual TResult VisitCallSite(ServiceCallSite callSite, TArgument argument)
{
if (!_stackGuard.TryEnterOnCurrentStack())
{
return _stackGuard.RunOnEmptyStack((c, a) => VisitCallSite(c, a), callSite, argument);
}
switch (callSite.Cache.Location)
{
case CallSiteResultCacheLocation.Root:
return VisitRootCache(callSite, argument);
case CallSiteResultCacheLocation.Scope:
return VisitScopeCache(callSite, argument);
case CallSiteResultCacheLocation.Dispose:
return VisitDisposeCache(callSite, argument);
case CallSiteResultCacheLocation.None:
return VisitNoCache(callSite, argument);
default:
throw new ArgumentOutOfRangeException();
}
}
protected virtual TResult VisitCallSiteMain(ServiceCallSite callSite, TArgument argument)
{
switch (callSite.Kind)
{
@ -14,16 +44,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return VisitIEnumerable((IEnumerableCallSite)callSite, argument);
case CallSiteKind.Constructor:
return VisitConstructor((ConstructorCallSite)callSite, argument);
case CallSiteKind.Transient:
return VisitTransient((TransientCallSite)callSite, argument);
case CallSiteKind.Singleton:
return VisitSingleton((SingletonCallSite)callSite, argument);
case CallSiteKind.Scope:
return VisitScoped((ScopedCallSite)callSite, argument);
case CallSiteKind.Constant:
return VisitConstant((ConstantCallSite)callSite, argument);
case CallSiteKind.CreateInstance:
return VisitCreateInstance((CreateInstanceCallSite)callSite, argument);
case CallSiteKind.ServiceProvider:
return VisitServiceProvider((ServiceProviderCallSite)callSite, argument);
case CallSiteKind.ServiceScopeFactory:
@ -33,18 +55,30 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
}
}
protected abstract TResult VisitTransient(TransientCallSite transientCallSite, TArgument argument);
protected virtual TResult VisitNoCache(ServiceCallSite callSite, TArgument argument)
{
return VisitCallSiteMain(callSite, argument);
}
protected virtual TResult VisitDisposeCache(ServiceCallSite callSite, TArgument argument)
{
return VisitCallSiteMain(callSite, argument);
}
protected virtual TResult VisitRootCache(ServiceCallSite callSite, TArgument argument)
{
return VisitCallSiteMain(callSite, argument);
}
protected virtual TResult VisitScopeCache(ServiceCallSite callSite, TArgument argument)
{
return VisitCallSiteMain(callSite, argument);
}
protected abstract TResult VisitConstructor(ConstructorCallSite constructorCallSite, TArgument argument);
protected abstract TResult VisitSingleton(SingletonCallSite singletonCallSite, TArgument argument);
protected abstract TResult VisitScoped(ScopedCallSite scopedCallSite, TArgument argument);
protected abstract TResult VisitConstant(ConstantCallSite constantCallSite, TArgument argument);
protected abstract TResult VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, TArgument argument);
protected abstract TResult VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, TArgument argument);
protected abstract TResult VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, TArgument argument);

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

@ -15,7 +15,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
ExpressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
}
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
var realizedService = ExpressionResolverBuilder.Build(callSite);
RealizedServices[callSite.ServiceType] = realizedService;

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

@ -5,17 +5,17 @@ using System;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class ConstantCallSite : IServiceCallSite
internal class ConstantCallSite : ServiceCallSite
{
internal object DefaultValue { get; }
public ConstantCallSite(Type serviceType, object defaultValue)
public ConstantCallSite(Type serviceType, object defaultValue): base(ResultCache.None)
{
DefaultValue = defaultValue;
}
public Type ServiceType => DefaultValue.GetType();
public Type ImplementationType => DefaultValue.GetType();
public CallSiteKind Kind { get; } = CallSiteKind.Constant;
public override Type ServiceType => DefaultValue.GetType();
public override Type ImplementationType => DefaultValue.GetType();
public override CallSiteKind Kind { get; } = CallSiteKind.Constant;
}
}

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

@ -6,21 +6,25 @@ using System.Reflection;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class ConstructorCallSite : IServiceCallSite
internal class ConstructorCallSite : ServiceCallSite
{
internal ConstructorInfo ConstructorInfo { get; }
internal IServiceCallSite[] ParameterCallSites { get; }
internal ServiceCallSite[] ParameterCallSites { get; }
public ConstructorCallSite(Type serviceType, ConstructorInfo constructorInfo, IServiceCallSite[] parameterCallSites)
public ConstructorCallSite(ResultCache cache, Type serviceType, ConstructorInfo constructorInfo) : this(cache, serviceType, constructorInfo, Array.Empty<ServiceCallSite>())
{
}
public ConstructorCallSite(ResultCache cache, Type serviceType, ConstructorInfo constructorInfo, ServiceCallSite[] parameterCallSites) : base(cache)
{
ServiceType = serviceType;
ConstructorInfo = constructorInfo;
ParameterCallSites = parameterCallSites;
}
public Type ServiceType { get; }
public override Type ServiceType { get; }
public Type ImplementationType => ConstructorInfo.DeclaringType;
public CallSiteKind Kind { get; } = CallSiteKind.Constructor;
public override Type ImplementationType => ConstructorInfo.DeclaringType;
public override CallSiteKind Kind { get; } = CallSiteKind.Constructor;
}
}

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

@ -6,17 +6,4 @@ using System.Runtime.ExceptionServices;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class CreateInstanceCallSite : IServiceCallSite
{
public Type ServiceType { get; }
public Type ImplementationType { get; }
public CallSiteKind Kind { get; } = CallSiteKind.CreateInstance;
public CreateInstanceCallSite(Type serviceType, Type implementationType)
{
ServiceType = serviceType;
ImplementationType = implementationType;
}
}
}

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

@ -14,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
}
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
var callCount = 0;
return scope =>

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

@ -14,18 +14,18 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal static readonly MethodInfo InvokeFactoryMethodInfo = GetMethodInfo<Action<Func<IServiceProvider, object>, IServiceProvider>>((a, b) => a.Invoke(b));
internal static readonly MethodInfo CaptureDisposableMethodInfo = GetMethodInfo<Func<ServiceProviderEngineScope, object, object>>((a, b) => a.CaptureDisposable(b));
internal static readonly MethodInfo TryGetValueMethodInfo = GetMethodInfo<Func<IDictionary<object, object>, object, object, bool>>((a, b, c) => a.TryGetValue(b, out c));
internal static readonly MethodInfo AddMethodInfo = GetMethodInfo<Action<IDictionary<object, object>, object, object>>((a, b, c) => a.Add(b, c));
internal static readonly MethodInfo TryGetValueMethodInfo = GetMethodInfo<Func<IDictionary<ServiceCacheKey, object>, ServiceCacheKey, object, bool>>((a, b, c) => a.TryGetValue(b, out c));
internal static readonly MethodInfo AddMethodInfo = GetMethodInfo<Action<IDictionary<ServiceCacheKey, object>, ServiceCacheKey, object>>((a, b, c) => a.Add(b, c));
internal static readonly MethodInfo MonitorEnterMethodInfo = GetMethodInfo<Action<object, bool>>((lockObj, lockTaken) => Monitor.Enter(lockObj, ref lockTaken));
internal static readonly MethodInfo MonitorExitMethodInfo = GetMethodInfo<Action<object>>(lockObj => Monitor.Exit(lockObj));
internal static readonly MethodInfo CallSiteRuntimeResolverResolve =
GetMethodInfo<Func<CallSiteRuntimeResolver, IServiceCallSite, ServiceProviderEngineScope, object>>((r, c, p) => r.Resolve(c, p));
GetMethodInfo<Func<CallSiteRuntimeResolver, ServiceCallSite, ServiceProviderEngineScope, object>>((r, c, p) => r.Resolve(c, p));
internal static readonly MethodInfo ArrayEmptyMethodInfo = typeof(Array).GetMethod(nameof(Array.Empty));
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 ParameterExpression ResolvedServices = Expression.Variable(typeof(IDictionary<ServiceCacheKey, object>), ScopeParameter.Name + "resolvedServices");
private static readonly BinaryExpression ResolvedServicesVariableAssignment =
Expression.Assign(ResolvedServices,
Expression.Property(ScopeParameter, nameof(ServiceProviderEngineScope.ResolvedServices)));
@ -41,7 +41,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
private readonly ServiceProviderEngineScope _rootScope;
public ExpressionResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope)
public ExpressionResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope):
base()
{
if (runtimeResolver == null)
{
@ -52,13 +53,13 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
_rootScope = rootScope;
}
public Func<ServiceProviderEngineScope, object> Build(IServiceCallSite callSite)
public Func<ServiceProviderEngineScope, object> Build(ServiceCallSite callSite)
{
if (callSite is SingletonCallSite singletonCallSite)
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
// If root call site is singleton we can return Func calling
// _runtimeResolver.Resolve directly and avoid Expression generation
if (TryResolveSingletonValue(singletonCallSite, out var value))
if (TryResolveSingletonValue(callSite, out var value))
{
return scope => value;
}
@ -69,15 +70,15 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return BuildExpression(callSite).Compile();
}
private bool TryResolveSingletonValue(SingletonCallSite singletonCallSite, out object value)
private bool TryResolveSingletonValue(ServiceCallSite singletonCallSite, out object value)
{
lock (_rootScope.ResolvedServices)
{
return _rootScope.ResolvedServices.TryGetValue(singletonCallSite.CacheKey, out value);
return _rootScope.ResolvedServices.TryGetValue(singletonCallSite.Cache.Key, out value);
}
}
private Expression<Func<ServiceProviderEngineScope, object>> BuildExpression(IServiceCallSite callSite)
private Expression<Func<ServiceProviderEngineScope, object>> BuildExpression(ServiceCallSite callSite)
{
var context = new CallSiteExpressionBuilderContext
{
@ -99,7 +100,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return Expression.Lambda<Func<ServiceProviderEngineScope, object>>(serviceExpression, ScopeParameter);
}
protected override Expression VisitSingleton(SingletonCallSite singletonCallSite, CallSiteExpressionBuilderContext context)
protected override Expression VisitRootCache(ServiceCallSite singletonCallSite, CallSiteExpressionBuilderContext context)
{
if (TryResolveSingletonValue(singletonCallSite, out var value))
{
@ -109,7 +110,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return Expression.Call(
Expression.Constant(_runtimeResolver),
CallSiteRuntimeResolverResolve,
Expression.Constant(singletonCallSite, typeof(IServiceCallSite)),
Expression.Constant(singletonCallSite, typeof(ServiceCallSite)),
context.ScopeParameter);
}
@ -118,11 +119,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return Expression.Constant(constantCallSite.DefaultValue);
}
protected override Expression VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, CallSiteExpressionBuilderContext context)
{
return Expression.New(createInstanceCallSite.ImplementationType);
}
protected override Expression VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, CallSiteExpressionBuilderContext context)
{
return context.ScopeParameter;
@ -155,14 +151,14 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
callSite.ItemType)));
}
protected override Expression VisitTransient(TransientCallSite callSite, CallSiteExpressionBuilderContext context)
protected override Expression VisitDisposeCache(ServiceCallSite callSite, CallSiteExpressionBuilderContext context)
{
var implType = callSite.ServiceCallSite.ImplementationType;
var implType = callSite.ImplementationType;
// Elide calls to GetCaptureDisposable if the implementation type isn't disposable
return TryCaptureDisposible(
implType,
context.ScopeParameter,
VisitCallSite(callSite.ServiceCallSite, context));
VisitCallSiteMain(callSite, context));
}
private Expression TryCaptureDisposible(Type implType, ParameterExpression scope, Expression service)
@ -180,10 +176,18 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
protected override Expression VisitConstructor(ConstructorCallSite callSite, CallSiteExpressionBuilderContext context)
{
var parameters = callSite.ConstructorInfo.GetParameters();
var parameterExpressions = new Expression[callSite.ParameterCallSites.Length];
for (int i = 0; i < parameterExpressions.Length; i++)
Expression[] parameterExpressions;
if (callSite.ParameterCallSites.Length == 0)
{
parameterExpressions[i] = Convert(VisitCallSite(callSite.ParameterCallSites[i], context), parameters[i].ParameterType);
parameterExpressions = Array.Empty<Expression>();
}
else
{
parameterExpressions = new Expression[callSite.ParameterCallSites.Length];
for (int i = 0; i < parameterExpressions.Length; i++)
{
parameterExpressions[i] = Convert(VisitCallSite(callSite.ParameterCallSites[i], context), parameters[i].ParameterType);
}
}
return Expression.New(callSite.ConstructorInfo, parameterExpressions);
}
@ -199,17 +203,17 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return Expression.Convert(expression, type);
}
protected override Expression VisitScoped(ScopedCallSite callSite, CallSiteExpressionBuilderContext context)
protected override Expression VisitScopeCache(ServiceCallSite callSite, CallSiteExpressionBuilderContext context)
{
return BuildScopedExpression(callSite, context, VisitCallSite(callSite.ServiceCallSite, context));
return BuildScopedExpression(callSite, context, VisitCallSiteMain(callSite, context));
}
// Move off the main stack
private Expression BuildScopedExpression(ScopedCallSite callSite, CallSiteExpressionBuilderContext context, Expression service)
private Expression BuildScopedExpression(ServiceCallSite callSite, CallSiteExpressionBuilderContext context, Expression service)
{
var keyExpression = Expression.Constant(
callSite.CacheKey,
typeof(object));
callSite.Cache.Key,
typeof(ServiceCacheKey));
var resolvedVariable = Expression.Variable(typeof(object), "resolved");

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

@ -11,7 +11,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
_expressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
}
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
var realizedService = _expressionResolverBuilder.Build(callSite);
RealizedServices[callSite.ServiceType] = realizedService;

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

@ -5,19 +5,19 @@ using System;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class FactoryCallSite : IServiceCallSite
internal class FactoryCallSite : ServiceCallSite
{
public Func<IServiceProvider, object> Factory { get; }
public FactoryCallSite(Type serviceType, Func<IServiceProvider, object> factory)
public FactoryCallSite(ResultCache cache, Type serviceType, Func<IServiceProvider, object> factory) : base(cache)
{
Factory = factory;
ServiceType = serviceType;
}
public Type ServiceType { get; }
public Type ImplementationType => null;
public override Type ServiceType { get; }
public override Type ImplementationType => null;
public CallSiteKind Kind { get; } = CallSiteKind.Factory;
public override CallSiteKind Kind { get; } = CallSiteKind.Factory;
}
}

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

@ -6,19 +6,19 @@ using System.Collections.Generic;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class IEnumerableCallSite : IServiceCallSite
internal class IEnumerableCallSite : ServiceCallSite
{
internal Type ItemType { get; }
internal IServiceCallSite[] ServiceCallSites { get; }
internal ServiceCallSite[] ServiceCallSites { get; }
public IEnumerableCallSite(Type itemType, IServiceCallSite[] serviceCallSites)
public IEnumerableCallSite(Type itemType, ServiceCallSite[] serviceCallSites) : base(ResultCache.None)
{
ItemType = itemType;
ServiceCallSites = serviceCallSites;
}
public Type ServiceType => typeof(IEnumerable<>).MakeGenericType(ItemType);
public Type ImplementationType => ItemType.MakeArrayType();
public CallSiteKind Kind { get; } = CallSiteKind.IEnumerable;
public override Type ServiceType => typeof(IEnumerable<>).MakeGenericType(ItemType);
public override Type ImplementationType => ItemType.MakeArrayType();
public override CallSiteKind Kind { get; } = CallSiteKind.IEnumerable;
}
}

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

@ -20,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
internal static ILEmitCallSiteAnalyzer Instance { get; } = new ILEmitCallSiteAnalyzer();
protected override ILEmitCallSiteAnalysisResult VisitTransient(TransientCallSite transientCallSite, object argument) => VisitCallSite(transientCallSite.ServiceCallSite, argument);
protected override ILEmitCallSiteAnalysisResult VisitDisposeCache(ServiceCallSite transientCallSite, object argument) => VisitCallSiteMain(transientCallSite, argument);
protected override ILEmitCallSiteAnalysisResult VisitConstructor(ConstructorCallSite constructorCallSite, object argument)
{
@ -32,17 +32,15 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return result;
}
protected override ILEmitCallSiteAnalysisResult VisitSingleton(SingletonCallSite singletonCallSite, object argument) => VisitCallSite(singletonCallSite.ServiceCallSite, argument);
protected override ILEmitCallSiteAnalysisResult VisitRootCache(ServiceCallSite singletonCallSite, object argument) => VisitCallSiteMain(singletonCallSite, argument);
protected override ILEmitCallSiteAnalysisResult VisitScoped(ScopedCallSite scopedCallSite, object argument)
protected override ILEmitCallSiteAnalysisResult VisitScopeCache(ServiceCallSite scopedCallSite, object argument)
{
return new ILEmitCallSiteAnalysisResult(ScopedILSize, hasScope: true).Add(VisitCallSite(scopedCallSite.ServiceCallSite, argument));
return new ILEmitCallSiteAnalysisResult(ScopedILSize, hasScope: true).Add(VisitCallSiteMain(scopedCallSite, argument));
}
protected override ILEmitCallSiteAnalysisResult VisitConstant(ConstantCallSite constantCallSite, object argument) => new ILEmitCallSiteAnalysisResult(ConstantILSize);
protected override ILEmitCallSiteAnalysisResult VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, object argument) => new ILEmitCallSiteAnalysisResult(ConstructorILSize);
protected override ILEmitCallSiteAnalysisResult VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, object argument) => new ILEmitCallSiteAnalysisResult(ServiceProviderSize);
protected override ILEmitCallSiteAnalysisResult VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, object argument) => new ILEmitCallSiteAnalysisResult(ConstantILSize);
@ -59,6 +57,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
protected override ILEmitCallSiteAnalysisResult VisitFactory(FactoryCallSite factoryCallSite, object argument) => new ILEmitCallSiteAnalysisResult(FactoryILSize);
public ILEmitCallSiteAnalysisResult CollectGenerationInfo(IServiceCallSite callSite) => VisitCallSite(callSite, null);
public ILEmitCallSiteAnalysisResult CollectGenerationInfo(ServiceCallSite callSite) => VisitCallSite(callSite, null);
}
}

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

@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Reflection.Emit;
@ -19,6 +20,9 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
private static readonly FieldInfo RootField = typeof(ILEmitResolverBuilderRuntimeContext).GetField(nameof(ILEmitResolverBuilderRuntimeContext.Root));
private static readonly FieldInfo FactoriesField = typeof(ILEmitResolverBuilderRuntimeContext).GetField(nameof(ILEmitResolverBuilderRuntimeContext.Factories));
private static readonly FieldInfo ConstantsField = typeof(ILEmitResolverBuilderRuntimeContext).GetField(nameof(ILEmitResolverBuilderRuntimeContext.Constants));
private static readonly MethodInfo GetTypeFromHandleMethod = typeof(Type).GetMethod(nameof(Type.GetTypeFromHandle));
private static readonly ConstructorInfo CacheKeyCtor = typeof(ServiceCacheKey).GetConstructors().First();
private class ILEmitResolverBuilderRuntimeContext
{
@ -35,7 +39,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
private readonly ServiceProviderEngineScope _rootScope;
public ILEmitResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope)
public ILEmitResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope) :
base()
{
if (runtimeResolver == null)
{
@ -46,13 +51,13 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
_rootScope = rootScope;
}
public Func<ServiceProviderEngineScope, object> Build(IServiceCallSite callSite)
public Func<ServiceProviderEngineScope, object> Build(ServiceCallSite callSite)
{
if (callSite is SingletonCallSite singletonCallSite)
if (callSite.Cache.Location == CallSiteResultCacheLocation.Root)
{
// If root call site is singleton we can return Func calling
// _runtimeResolver.Resolve directly and avoid Expression generation
if (TryResolveSingletonValue(singletonCallSite, out var value))
if (TryResolveSingletonValue(callSite, out var value))
{
return scope => value;
}
@ -63,12 +68,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return BuildType(callSite);
}
protected override Expression VisitTransient(TransientCallSite transientCallSite, ILEmitResolverBuilderContext argument)
protected override Expression VisitDisposeCache(ServiceCallSite transientCallSite, ILEmitResolverBuilderContext argument)
{
// RuntimeScope.CaptureDisposables([create value])
var shouldCapture = BeginCaptureDisposable(transientCallSite.ServiceCallSite.ImplementationType, argument);
var shouldCapture = BeginCaptureDisposable(transientCallSite.ImplementationType, argument);
VisitCallSite(transientCallSite.ServiceCallSite, argument);
VisitCallSiteMain(transientCallSite, argument);
if (shouldCapture)
{
@ -88,9 +93,9 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return null;
}
protected override Expression VisitSingleton(SingletonCallSite singletonCallSite, ILEmitResolverBuilderContext argument)
protected override Expression VisitRootCache(ServiceCallSite callSite, ILEmitResolverBuilderContext argument)
{
if (TryResolveSingletonValue(singletonCallSite, out var value))
if (TryResolveSingletonValue(callSite, out var value))
{
AddConstant(argument, value);
return null;
@ -101,7 +106,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
argument.Generator.Emit(OpCodes.Ldarg_0);
argument.Generator.Emit(OpCodes.Ldfld, RuntimeResolverField);
AddConstant(argument, singletonCallSite);
AddConstant(argument, callSite);
argument.Generator.Emit(OpCodes.Ldarg_0);
argument.Generator.Emit(OpCodes.Ldfld, RootField);
@ -110,7 +115,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return null;
}
protected override Expression VisitScoped(ScopedCallSite scopedCallSite, ILEmitResolverBuilderContext argument)
protected override Expression VisitScopeCache(ServiceCallSite scopedCallSite, ILEmitResolverBuilderContext argument)
{
// var cacheKey = scopedCallSite.CacheKey;
@ -125,13 +130,13 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
// }
var resultLocal = argument.Generator.DeclareLocal(scopedCallSite.ServiceType);
var cacheKeyLocal = argument.Generator.DeclareLocal(typeof(object));
var cacheKeyLocal = argument.Generator.DeclareLocal(typeof(ServiceCacheKey));
var endLabel = argument.Generator.DefineLabel();
// Resolved services would be 0 local
argument.Generator.Emit(OpCodes.Ldloc_0);
AddConstant(argument, scopedCallSite.CacheKey);
AddCacheKey(argument, scopedCallSite.Cache.Key);
// Duplicate cache key
argument.Generator.Emit(OpCodes.Dup);
// and store to local
@ -145,9 +150,9 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
// Jump to create new if nothing in cache
argument.Generator.Emit(OpCodes.Brtrue, endLabel);
var shouldCapture = BeginCaptureDisposable(scopedCallSite.ServiceCallSite.ImplementationType, argument);
var shouldCapture = BeginCaptureDisposable(scopedCallSite.ImplementationType, argument);
VisitCallSite(scopedCallSite.ServiceCallSite, argument);
VisitCallSiteMain(scopedCallSite, argument);
if (shouldCapture)
{
@ -178,13 +183,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
return null;
}
protected override Expression VisitCreateInstance(CreateInstanceCallSite createInstanceCallSite, ILEmitResolverBuilderContext argument)
{
// new Type
argument.Generator.Emit(OpCodes.Newobj, createInstanceCallSite.ImplementationType.GetConstructor(Type.EmptyTypes));
return null;
}
protected override Expression VisitServiceProvider(ServiceProviderCallSite serviceProviderCallSite, ILEmitResolverBuilderContext argument)
{
// [return] ProviderScope
@ -269,8 +267,17 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
argument.Constants.Add(value);
}
private void AddCacheKey(ILEmitResolverBuilderContext argument, ServiceCacheKey key)
{
// new ServiceCacheKey(typeof(key.Type), key.Slot)
argument.Generator.Emit(OpCodes.Ldtoken, key.Type);
argument.Generator.Emit(OpCodes.Call, GetTypeFromHandleMethod);
argument.Generator.Emit(OpCodes.Ldc_I4, key.Slot);
argument.Generator.Emit(OpCodes.Newobj, CacheKeyCtor);
}
private Func<ServiceProviderEngineScope, object> BuildType(IServiceCallSite callSite)
private Func<ServiceProviderEngineScope, object> BuildType(ServiceCallSite callSite)
{
// We need to skip visibility checks because services/constructors might be private
var dynamicMethod = new DynamicMethod("ResolveService",
@ -298,13 +305,13 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
GenerateMethodBody(callSite, method.GetILGenerator(), info);
type.CreateTypeInfo();
assembly.Save(assemblyName+".dll");
assembly.Save(assemblyName + ".dll");
#endif
return (Func<ServiceProviderEngineScope, object>)dynamicMethod.CreateDelegate(typeof(Func<ServiceProviderEngineScope, object>), runtimeContext);
}
private ILEmitResolverBuilderRuntimeContext GenerateMethodBody(IServiceCallSite callSite, ILGenerator generator, ILEmitCallSiteAnalysisResult info)
private ILEmitResolverBuilderRuntimeContext GenerateMethodBody(ServiceCallSite callSite, ILGenerator generator, ILEmitCallSiteAnalysisResult info)
{
var context = new ILEmitResolverBuilderContext()
{
@ -384,11 +391,11 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
};
}
private bool TryResolveSingletonValue(SingletonCallSite singletonCallSite, out object value)
private bool TryResolveSingletonValue(ServiceCallSite callSite, out object value)
{
lock (_rootScope.ResolvedServices)
{
return _rootScope.ResolvedServices.TryGetValue(singletonCallSite.CacheKey, out value);
return _rootScope.ResolvedServices.TryGetValue(callSite.Cache.Key, out value);
}
}

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

@ -14,7 +14,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
_expressionResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);
}
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
var realizedService = _expressionResolverBuilder.Build(callSite);
RealizedServices[callSite.ServiceType] = realizedService;

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

@ -7,7 +7,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal interface IServiceProviderEngineCallback
{
void OnCreate(IServiceCallSite callSite);
void OnCreate(ServiceCallSite callSite);
void OnResolve(Type serviceType, IServiceScope scope);
}
}

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

@ -0,0 +1,45 @@
// 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.Diagnostics;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal struct ResultCache
{
public static ResultCache None { get; } = new ResultCache(CallSiteResultCacheLocation.None, ServiceCacheKey.Empty);
internal ResultCache(CallSiteResultCacheLocation lifetime, ServiceCacheKey cacheKey)
{
Location = lifetime;
Key = cacheKey;
}
public ResultCache(ServiceLifetime lifetime, Type type, int slot)
{
Debug.Assert(lifetime == ServiceLifetime.Transient || type != null);
switch (lifetime)
{
case ServiceLifetime.Singleton:
Location = CallSiteResultCacheLocation.Root;
break;
case ServiceLifetime.Scoped:
Location = CallSiteResultCacheLocation.Scope;
break;
case ServiceLifetime.Transient:
Location = CallSiteResultCacheLocation.Dispose;
break;
default:
Location = CallSiteResultCacheLocation.None;
break;
}
Key = new ServiceCacheKey(type, slot);
}
public CallSiteResultCacheLocation Location { get; set; }
public ServiceCacheKey Key { get; set; }
}
}

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

@ -12,7 +12,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
}
protected override Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite)
protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
{
return scope =>
{

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

@ -1,23 +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 ScopedCallSite : IServiceCallSite
{
internal IServiceCallSite ServiceCallSite { get; }
public object CacheKey { get; }
public ScopedCallSite(IServiceCallSite serviceCallSite, object cacheKey)
{
ServiceCallSite = serviceCallSite;
CacheKey = cacheKey;
}
public Type ServiceType => ServiceCallSite.ServiceType;
public Type ImplementationType => ServiceCallSite.ImplementationType;
public virtual CallSiteKind Kind { get; } = CallSiteKind.Scope;
}
}

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

@ -0,0 +1,49 @@
// 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 struct ServiceCacheKey: IEquatable<ServiceCacheKey>
{
public static ServiceCacheKey Empty { get; } = new ServiceCacheKey(null, 0);
/// <summary>
/// Type of service being cached
/// </summary>
public Type Type { get; }
/// <summary>
/// Reverse index of the service when resolved in <code>IEnumerable&lt;Type&gt;</code> where default instance gets slot 0.
/// For example for service collection
/// IService Impl1
/// IService Impl2
/// IService Impl3
/// We would get the following cache keys:
/// Impl1 2
/// Impl2 1
/// Impl3 0
/// </summary>
public int Slot { get; }
public ServiceCacheKey(Type type, int slot)
{
Type = type;
Slot = slot;
}
public bool Equals(ServiceCacheKey other)
{
return Type == other.Type && Slot == other.Slot;
}
public override int GetHashCode()
{
unchecked
{
return (Type.GetHashCode() * 397) ^ Slot;
}
}
}
}

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

@ -8,10 +8,16 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
/// <summary>
/// Summary description for IServiceCallSite
/// </summary>
internal interface IServiceCallSite
internal abstract class ServiceCallSite
{
Type ServiceType { get; }
Type ImplementationType { get; }
CallSiteKind Kind { get; }
protected ServiceCallSite(ResultCache cache)
{
Cache = cache;
}
public abstract Type ServiceType { get; }
public abstract Type ImplementationType { get; }
public abstract CallSiteKind Kind { get; }
public ResultCache Cache { get; }
}
}

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

@ -5,10 +5,14 @@ using System;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class ServiceProviderCallSite : IServiceCallSite
internal class ServiceProviderCallSite : ServiceCallSite
{
public Type ServiceType { get; } = typeof(IServiceProvider);
public Type ImplementationType { get; } = typeof(ServiceProvider);
public CallSiteKind Kind { get; } = CallSiteKind.ServiceProvider;
public ServiceProviderCallSite() : base(ResultCache.None)
{
}
public override Type ServiceType { get; } = typeof(IServiceProvider);
public override Type ImplementationType { get; } = typeof(ServiceProvider);
public override CallSiteKind Kind { get; } = CallSiteKind.ServiceProvider;
}
}

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

@ -39,7 +39,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
public object GetService(Type serviceType) => GetService(serviceType, Root);
protected abstract Func<ServiceProviderEngineScope, object> RealizeService(IServiceCallSite callSite);
protected abstract Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite);
public void Dispose()
{
@ -71,7 +71,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
private Func<ServiceProviderEngineScope, object> CreateServiceAccessor(Type serviceType)
{
var callSite = CallSiteFactory.CreateCallSite(serviceType, new CallSiteChain());
var callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
if (callSite != null)
{
_callback?.OnCreate(callSite);

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

@ -20,7 +20,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
Engine = engine;
}
internal Dictionary<object, object> ResolvedServices { get; } = new Dictionary<object, object>();
internal Dictionary<ServiceCacheKey, object> ResolvedServices { get; } = new Dictionary<ServiceCacheKey, object>();
public ServiceProviderEngine Engine { get; }

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

@ -5,10 +5,14 @@ using System;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class ServiceScopeFactoryCallSite : IServiceCallSite
internal class ServiceScopeFactoryCallSite : ServiceCallSite
{
public Type ServiceType { get; } = typeof(IServiceScopeFactory);
public Type ImplementationType { get; } = typeof(ServiceProviderEngine);
public CallSiteKind Kind { get; } = CallSiteKind.ServiceScopeFactory;
public ServiceScopeFactoryCallSite() : base(ResultCache.None)
{
}
public override Type ServiceType { get; } = typeof(IServiceScopeFactory);
public override Type ImplementationType { get; } = typeof(ServiceProviderEngine);
public override CallSiteKind Kind { get; } = CallSiteKind.ServiceScopeFactory;
}
}

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

@ -3,12 +3,4 @@
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal class SingletonCallSite : ScopedCallSite
{
public SingletonCallSite(IServiceCallSite serviceCallSite, object cacheKey) : base(serviceCallSite, cacheKey)
{
}
public override CallSiteKind Kind { get; } = CallSiteKind.Singleton;
}
}

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

@ -0,0 +1,81 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.CompilerServices;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
internal sealed class StackGuard
{
private const int MaxExecutionStackCount = 1024;
private int _executionStackCount;
public bool TryEnterOnCurrentStack()
{
#if NETCOREAPP2_0
if (RuntimeHelpers.TryEnsureSufficientExecutionStack())
{
return true;
}
#else
try
{
RuntimeHelpers.EnsureSufficientExecutionStack();
return true;
}
catch (InsufficientExecutionStackException)
{
}
#endif
if (_executionStackCount < MaxExecutionStackCount)
{
return false;
}
throw new InsufficientExecutionStackException();
}
public TR RunOnEmptyStack<T1, T2, TR>(Func<T1, T2, TR> action, T1 arg1, T2 arg2)
{
return RunOnEmptyStackCore(s =>
{
var t = (Tuple<Func<T1, T2, TR>, T1, T2>)s;
return t.Item1(t.Item2, t.Item3);
}, Tuple.Create(action, arg1, arg2));
}
private R RunOnEmptyStackCore<R>(Func<object, R> action, object state)
{
_executionStackCount++;
try
{
// Using default scheduler rather than picking up the current scheduler.
Task<R> task = Task.Factory.StartNew(action, state, CancellationToken.None, TaskCreationOptions.DenyChildAttach, TaskScheduler.Default);
TaskAwaiter<R> awaiter = task.GetAwaiter();
// Avoid AsyncWaitHandle lazy allocation of ManualResetEvent in the rare case we finish quickly.
if (!awaiter.IsCompleted)
{
// Task.Wait has the potential of inlining the task's execution on the current thread; avoid this.
((IAsyncResult)task).AsyncWaitHandle.WaitOne();
}
// Using awaiter here to unwrap AggregateException.
return awaiter.GetResult();
}
finally
{
_executionStackCount--;
}
}
}
}

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

@ -1,21 +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 TransientCallSite : IServiceCallSite
{
internal IServiceCallSite ServiceCallSite { get; }
public TransientCallSite(IServiceCallSite serviceCallSite)
{
ServiceCallSite = serviceCallSite;
}
public Type ServiceType => ServiceCallSite.ServiceType;
public Type ImplementationType => ServiceCallSite.ImplementationType;
public CallSiteKind Kind { get; } = CallSiteKind.Transient;
}
}

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

@ -55,7 +55,7 @@ namespace Microsoft.Extensions.DependencyInjection
/// <inheritdoc />
public void Dispose() => _engine.Dispose();
void IServiceProviderEngineCallback.OnCreate(IServiceCallSite callSite)
void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite)
{
_callSiteValidator.ValidateCallSite(callSite);
}

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

@ -83,8 +83,8 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
{
var provider = new DynamicServiceProviderEngine(descriptors, null);
var callSite = provider.CallSiteFactory.CreateCallSite(serviceType, new CallSiteChain());
var collectionCallSite = provider.CallSiteFactory.CreateCallSite(typeof(IEnumerable<>).MakeGenericType(serviceType), new CallSiteChain());
var callSite = provider.CallSiteFactory.GetCallSite(serviceType, new CallSiteChain());
var collectionCallSite = provider.CallSiteFactory.GetCallSite(typeof(IEnumerable<>).MakeGenericType(serviceType), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
var compiledCollectionCallSite = CompileCallSite(collectionCallSite, provider);
@ -111,7 +111,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
descriptors.AddScoped<ServiceC>();
var provider = new DynamicServiceProviderEngine(descriptors, null);
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new CallSiteChain());
var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceC), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
var serviceC = (ServiceC)compiledCallSite(provider.Root);
@ -137,7 +137,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
{
disposables.Add(obj);
};
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new CallSiteChain());
var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceC), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
var serviceC = (DisposableServiceC)compiledCallSite(provider.Root);
@ -163,7 +163,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
{
disposables.Add(obj);
};
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new CallSiteChain());
var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceC), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
var serviceC = (DisposableServiceC)compiledCallSite(provider.Root);
@ -192,7 +192,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
{
disposables.Add(obj);
};
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceC), new CallSiteChain());
var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceC), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
var serviceC = (ServiceC)compiledCallSite(provider.Root);
@ -217,7 +217,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
{
disposables.Add(obj);
};
var callSite = provider.CallSiteFactory.CreateCallSite(typeof(ServiceD), new CallSiteChain());
var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceD), new CallSiteChain());
var compiledCallSite = CompileCallSite(callSite, provider);
var serviceD = (ServiceD)compiledCallSite(provider.Root);
@ -235,10 +235,10 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
var provider = new DynamicServiceProviderEngine(descriptors, null);
var callSite1 = provider.CallSiteFactory.CreateCallSite(typeof(ClassWithThrowingEmptyCtor), new CallSiteChain());
var callSite1 = provider.CallSiteFactory.GetCallSite(typeof(ClassWithThrowingEmptyCtor), new CallSiteChain());
var compiledCallSite1 = CompileCallSite(callSite1, provider);
var callSite2 = provider.CallSiteFactory.CreateCallSite(typeof(ClassWithThrowingCtor), new CallSiteChain());
var callSite2 = provider.CallSiteFactory.GetCallSite(typeof(ClassWithThrowingCtor), new CallSiteChain());
var compiledCallSite2 = CompileCallSite(callSite2, provider);
var ex1 = Assert.Throws<Exception>(() => compiledCallSite1(provider.Root));
@ -311,12 +311,12 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
}
}
private static object Invoke(IServiceCallSite callSite, ServiceProviderEngine provider)
private static object Invoke(ServiceCallSite callSite, ServiceProviderEngine provider)
{
return CallSiteRuntimeResolver.Resolve(callSite, provider.Root);
}
private static Func<ServiceProviderEngineScope, object> CompileCallSite(IServiceCallSite callSite, ServiceProviderEngine engine)
private static Func<ServiceProviderEngineScope, object> CompileCallSite(ServiceCallSite callSite, ServiceProviderEngine engine)
{
return new ExpressionResolverBuilder(CallSiteRuntimeResolver, engine, engine.Root).Build(callSite);
}

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

@ -42,8 +42,9 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var callSite = callSiteFactory(type);
// Assert
var transientCall = Assert.IsType<TransientCallSite>(callSite);
Assert.IsType<CreateInstanceCallSite>(transientCall.ServiceCallSite);
Assert.Equal(CallSiteResultCacheLocation.Dispose, callSite.Cache.Location);
var ctroCallSite = Assert.IsType<ConstructorCallSite>(callSite);
Assert.Empty(ctroCallSite.ParameterCallSites);
}
[Theory]
@ -65,8 +66,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var callSite = callSiteFactory(type);
// Assert
var transientCall = Assert.IsType<TransientCallSite>(callSite);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(transientCall.ServiceCallSite);
Assert.Equal(CallSiteResultCacheLocation.Dispose, callSite.Cache.Location);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
Assert.Equal(new[] { typeof(IFakeService) }, GetParameters(constructorCallSite));
}
@ -86,8 +87,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var callSite = callSiteFactory(type);
// Assert
var transientCall = Assert.IsType<TransientCallSite>(callSite);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(transientCall.ServiceCallSite);
Assert.Equal(CallSiteResultCacheLocation.Dispose, callSite.Cache.Location);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
Assert.Equal(
new[] { typeof(IEnumerable<IFakeService>), typeof(IEnumerable<IFactoryService>) },
GetParameters(constructorCallSite));
@ -105,12 +106,13 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var callSite = callSiteFactory(type);
// Assert
var transientCall = Assert.IsType<TransientCallSite>(callSite);
Assert.IsType<CreateInstanceCallSite>(transientCall.ServiceCallSite);
Assert.Equal(CallSiteResultCacheLocation.Dispose, callSite.Cache.Location);
var ctorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
Assert.Empty(ctorCallSite.ParameterCallSites);
}
public static TheoryData CreateCallSite_PicksConstructorWithTheMostNumberOfResolvedParametersData =>
new TheoryData<Type, Func<Type, object>, Type[]>
new TheoryData<Type, Func<Type, ServiceCallSite>, Type[]>
{
{
typeof(TypeWithSupersetConstructors),
@ -192,17 +194,17 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
[Theory]
[MemberData(nameof(CreateCallSite_PicksConstructorWithTheMostNumberOfResolvedParametersData))]
public void CreateCallSite_PicksConstructorWithTheMostNumberOfResolvedParameters(
private void CreateCallSite_PicksConstructorWithTheMostNumberOfResolvedParameters(
Type type,
Func<Type, object> callSiteFactory,
Func<Type, ServiceCallSite> callSiteFactory,
Type[] expectedConstructorParameters)
{
// Act
var callSite = callSiteFactory(type);
// Assert
var transientCall = Assert.IsType<TransientCallSite>(callSite);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(transientCall.ServiceCallSite);
Assert.Equal(CallSiteResultCacheLocation.Dispose, callSite.Cache.Location);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
Assert.Equal(expectedConstructorParameters, GetParameters(constructorCallSite));
}
@ -235,8 +237,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
[Theory]
[MemberData(nameof(CreateCallSite_ConsidersConstructorsWithDefaultValuesData))]
public void CreateCallSite_ConsidersConstructorsWithDefaultValues(
Func<Type, object> callSiteFactory,
private void CreateCallSite_ConsidersConstructorsWithDefaultValues(
Func<Type, ServiceCallSite> callSiteFactory,
Type[] expectedConstructorParameters)
{
// Arrange
@ -246,8 +248,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var callSite = callSiteFactory(type);
// Assert
var transientCall = Assert.IsType<TransientCallSite>(callSite);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(transientCall.ServiceCallSite);
Assert.Equal(CallSiteResultCacheLocation.Dispose, callSite.Cache.Location);
var constructorCallSite = Assert.IsType<ConstructorCallSite>(callSite);
Assert.Equal(expectedConstructorParameters, GetParameters(constructorCallSite));
}
@ -398,7 +400,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
Assert.StartsWith(expectedMessage, ex.Message);
}
private static Func<Type, object> GetCallSiteFactory(params ServiceDescriptor[] descriptors)
private static Func<Type, ServiceCallSite> GetCallSiteFactory(params ServiceDescriptor[] descriptors)
{
var collection = new ServiceCollection();
foreach (var descriptor in descriptors)
@ -408,7 +410,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
var callSiteFactory = new CallSiteFactory(collection.ToArray());
return type => callSiteFactory.CreateCallSite(type, new CallSiteChain());
return type => callSiteFactory.GetCallSite(type, new CallSiteChain());
}
private static IEnumerable<Type> GetParameters(ConstructorCallSite constructorCallSite) =>

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

@ -11,17 +11,10 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
public class ServiceProviderCompilationTest
{
[Theory]
#if DEBUG
[InlineData(ServiceProviderMode.Dynamic, typeof(I150))]
[InlineData(ServiceProviderMode.Runtime, typeof(I150))]
[InlineData(ServiceProviderMode.ILEmit, typeof(I150))]
[InlineData(ServiceProviderMode.Expressions, typeof(I150))]
#else
[InlineData(ServiceProviderMode.Dynamic, typeof(I200))]
[InlineData(ServiceProviderMode.Runtime, typeof(I200))]
[InlineData(ServiceProviderMode.ILEmit, typeof(I200))]
[InlineData(ServiceProviderMode.Expressions, typeof(I200))]
#endif
[InlineData(ServiceProviderMode.Dynamic, typeof(I999))]
[InlineData(ServiceProviderMode.Runtime, typeof(I999))]
[InlineData(ServiceProviderMode.ILEmit, typeof(I999))]
[InlineData(ServiceProviderMode.Expressions, typeof(I999))]
private async Task CompilesInLimitedStackSpace(ServiceProviderMode mode, Type serviceType)
{
// Arrange