diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs index 24034d51e98b7..32f7596b39310 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs @@ -67,15 +67,6 @@ public void ServiceRealizationFailed(string? exceptionMessage) WriteEvent(6, exceptionMessage); } - [NonEvent] - public void ScopeDisposed(ServiceProviderEngine engine, ScopeState state) - { - if (IsEnabled(EventLevel.Verbose, EventKeywords.All)) - { - ScopeDisposed(engine.GetHashCode(), state.ResolvedServicesCount, state.DisposableServicesCount); - } - } - [NonEvent] public void ServiceResolved(Type serviceType) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopeState.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopeState.cs deleted file mode 100644 index 14570e149ee10..0000000000000 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopeState.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; -using System.Collections.Generic; -using Microsoft.Extensions.DependencyInjection.ServiceLookup; - -namespace Microsoft.Extensions.DependencyInjection -{ - internal class ScopeState - { - public Dictionary ResolvedServices { get; } - public List Disposables { get; set; } - - public int DisposableServicesCount => Disposables?.Count ?? 0; - public int ResolvedServicesCount => ResolvedServices.Count; - - public ScopeState() - { - ResolvedServices = new Dictionary(); - } - - public void Track(ServiceProviderEngine engine) - { - DependencyInjectionEventSource.Log.ScopeDisposed(engine, this); - } - } -} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs index 0789d35e33bc8..ce4dd0cb8943e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs @@ -58,23 +58,7 @@ public static ServiceProvider BuildServiceProvider(this IServiceCollection servi throw new ArgumentNullException(nameof(options)); } - IServiceProviderEngine engine; - -#if !NETSTANDARD2_1 - engine = new DynamicServiceProviderEngine(services); -#else - if (RuntimeFeature.IsDynamicCodeCompiled) - { - engine = new DynamicServiceProviderEngine(services); - } - else - { - // Don't try to compile Expressions/IL if they are going to get interpreted - engine = new RuntimeServiceProviderEngine(services); - } -#endif - - return new ServiceProvider(services, engine, options); + return new ServiceProvider(services, options); } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs index 7479759f2b527..e7ddb41fb0824 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs @@ -12,6 +12,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class CallSiteRuntimeResolver : CallSiteVisitor { + public static CallSiteRuntimeResolver Instance { get; } = new(); + + private CallSiteRuntimeResolver() + { + } + public object Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope) { return VisitCallSite(callSite, new RuntimeResolverContext @@ -66,7 +72,7 @@ protected override object VisitRootCache(ServiceCallSite callSite, RuntimeResolv } var lockType = RuntimeResolverLock.Root; - ServiceProviderEngineScope serviceProviderEngine = context.Scope.Engine.Root; + ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root; lock (callSite) { @@ -91,7 +97,7 @@ protected override object VisitScopeCache(ServiceCallSite callSite, RuntimeResol { // Check if we are in the situation where scoped service was promoted to singleton // and we need to lock the root - return context.Scope == context.Scope.Engine.Root ? + return context.Scope.IsRootScope ? VisitRootCache(callSite, context) : VisitCache(callSite, context, context.Scope, RuntimeResolverLock.Scope); } @@ -149,7 +155,7 @@ protected override object VisitServiceProvider(ServiceProviderCallSite servicePr protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, RuntimeResolverContext context) { - return context.Scope.Engine; + return serviceScopeFactoryCallSite.Value; } protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs index c7661d28bd500..b51b879c54395 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs @@ -2,7 +2,6 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { @@ -14,21 +13,11 @@ internal abstract class CompiledServiceProviderEngine : ServiceProviderEngine public ExpressionResolverBuilder ResolverBuilder { get; } #endif - public CompiledServiceProviderEngine(IEnumerable serviceDescriptors) - : base(serviceDescriptors) + public CompiledServiceProviderEngine(ServiceProvider provider) { -#if IL_EMIT - ResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root); -#else - ResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root); -#endif + ResolverBuilder = new(provider); } - protected override Func RealizeService(ServiceCallSite callSite) - { - Func realizedService = ResolverBuilder.Build(callSite); - RealizedServices[callSite.ServiceType] = realizedService; - return realizedService; - } + public override Func RealizeService(ServiceCallSite callSite) => ResolverBuilder.Build(callSite); } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs index fa41c2d47ec21..23914ec50c73b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs @@ -2,19 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; using System.Threading; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class DynamicServiceProviderEngine : CompiledServiceProviderEngine { - public DynamicServiceProviderEngine(IEnumerable serviceDescriptors) - : base(serviceDescriptors) + private readonly ServiceProvider _serviceProvider; + + public DynamicServiceProviderEngine(ServiceProvider serviceProvider): base(serviceProvider) { + _serviceProvider = serviceProvider; } - protected override Func RealizeService(ServiceCallSite callSite) + public override Func RealizeService(ServiceCallSite callSite) { int callCount = 0; return scope => @@ -29,7 +30,7 @@ protected override Func RealizeService(Servi // Resolve the result before we increment the call count, this ensures that singletons // won't cause any side effects during the compilation of the resolve function. - var result = RuntimeResolver.Resolve(callSite, scope); + var result = CallSiteRuntimeResolver.Instance.Resolve(callSite, scope); if (Interlocked.Increment(ref callCount) == 2) { @@ -46,7 +47,7 @@ protected override Func RealizeService(Servi { try { - base.RealizeService(callSite); + _serviceProvider.ReplaceServiceAccessor(callSite, base.RealizeService(callSite)); } catch (Exception ex) { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs index 8a7cff871add8..3fd07268b8424 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs @@ -44,39 +44,21 @@ internal sealed class ExpressionResolverBuilder : CallSiteVisitor> _scopeResolverCache; private readonly Func> _buildTypeDelegate; - public ExpressionResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope) + public ExpressionResolverBuilder(ServiceProvider serviceProvider) { - if (runtimeResolver == null) - { - throw new ArgumentNullException(nameof(runtimeResolver)); - } - + _rootScope = serviceProvider.Root; _scopeResolverCache = new ConcurrentDictionary>(); - _runtimeResolver = runtimeResolver; - _serviceScopeFactory = serviceScopeFactory; - _rootScope = rootScope; _buildTypeDelegate = (key, cs) => BuildNoCache(cs); } public Func Build(ServiceCallSite callSite) { - // Optimize singleton case - if (callSite.Cache.Location == CallSiteResultCacheLocation.Root) - { - object value = _runtimeResolver.Resolve(callSite, _rootScope); - return scope => value; - } - // Only scope methods are cached if (callSite.Cache.Location == CallSiteResultCacheLocation.Scope) { @@ -117,7 +99,7 @@ private Expression> BuildExpression(Ser protected override Expression VisitRootCache(ServiceCallSite singletonCallSite, object context) { - return Expression.Constant(_runtimeResolver.Resolve(singletonCallSite, _rootScope)); + return Expression.Constant(CallSiteRuntimeResolver.Instance.Resolve(singletonCallSite, _rootScope)); } protected override Expression VisitConstant(ConstantCallSite constantCallSite, object context) @@ -132,7 +114,7 @@ protected override Expression VisitServiceProvider(ServiceProviderCallSite servi protected override Expression VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, object context) { - return Expression.Constant(_serviceScopeFactory); + return Expression.Constant(serviceScopeFactoryCallSite.Value); } protected override Expression VisitFactory(FactoryCallSite factoryCallSite, object context) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs index eb6ea1e4ecb61..9de2fa8acbb09 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs @@ -2,23 +2,21 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class ExpressionsServiceProviderEngine : ServiceProviderEngine { private readonly ExpressionResolverBuilder _expressionResolverBuilder; - public ExpressionsServiceProviderEngine(IEnumerable serviceDescriptors) : base(serviceDescriptors) + + public ExpressionsServiceProviderEngine(ServiceProvider serviceProvider) { - _expressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root); + _expressionResolverBuilder = new ExpressionResolverBuilder(serviceProvider); } - protected override Func RealizeService(ServiceCallSite callSite) + public override Func RealizeService(ServiceCallSite callSite) { - Func realizedService = _expressionResolverBuilder.Build(callSite); - RealizedServices[callSite.ServiceType] = realizedService; - return realizedService; + return _expressionResolverBuilder.Build(callSite); } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs index 9c9ace369d9b8..227a2f8c3688f 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs @@ -25,7 +25,6 @@ internal sealed class ILEmitResolverBuilder : CallSiteVisitor[] Factories; } @@ -38,39 +37,21 @@ private struct GeneratedMethod public DynamicMethod DynamicMethod; } - private readonly CallSiteRuntimeResolver _runtimeResolver; - - private readonly IServiceScopeFactory _serviceScopeFactory; - private readonly ServiceProviderEngineScope _rootScope; private readonly ConcurrentDictionary _scopeResolverCache; private readonly Func _buildTypeDelegate; - public ILEmitResolverBuilder(CallSiteRuntimeResolver runtimeResolver, IServiceScopeFactory serviceScopeFactory, ServiceProviderEngineScope rootScope) : - base() + public ILEmitResolverBuilder(ServiceProvider serviceProvider) { - if (runtimeResolver == null) - { - throw new ArgumentNullException(nameof(runtimeResolver)); - } - _runtimeResolver = runtimeResolver; - _serviceScopeFactory = serviceScopeFactory; - _rootScope = rootScope; + _rootScope = serviceProvider.Root; _scopeResolverCache = new ConcurrentDictionary(); _buildTypeDelegate = (key, cs) => BuildTypeNoCache(cs); } public Func Build(ServiceCallSite callSite) { - // Optimize singleton case - if (callSite.Cache.Location == CallSiteResultCacheLocation.Root) - { - object value = _runtimeResolver.Resolve(callSite, _rootScope); - return scope => value; - } - return BuildType(callSite).Lambda; } @@ -166,7 +147,7 @@ protected override object VisitConstructor(ConstructorCallSite constructorCallSi protected override object VisitRootCache(ServiceCallSite callSite, ILEmitResolverBuilderContext argument) { - AddConstant(argument, _runtimeResolver.Resolve(callSite, _rootScope)); + AddConstant(argument, CallSiteRuntimeResolver.Instance.Resolve(callSite, _rootScope)); return null; } @@ -205,9 +186,7 @@ protected override object VisitServiceProvider(ServiceProviderCallSite servicePr protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, ILEmitResolverBuilderContext argument) { - // this.ScopeFactory - argument.Generator.Emit(OpCodes.Ldarg_0); - argument.Generator.Emit(OpCodes.Ldfld, typeof(ILEmitResolverBuilderRuntimeContext).GetField(nameof(ILEmitResolverBuilderRuntimeContext.ScopeFactory))); + AddConstant(argument, serviceScopeFactoryCallSite.Value); return null; } @@ -426,8 +405,7 @@ private ILEmitResolverBuilderRuntimeContext GenerateMethodBody(ServiceCallSite c return new ILEmitResolverBuilderRuntimeContext { Constants = context.Constants?.ToArray(), - Factories = context.Factories?.ToArray(), - ScopeFactory = _serviceScopeFactory + Factories = context.Factories?.ToArray() }; } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs index ca8a1ad1a124e..f0bcebf5df782 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs @@ -2,23 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class ILEmitServiceProviderEngine : ServiceProviderEngine { private readonly ILEmitResolverBuilder _expressionResolverBuilder; - public ILEmitServiceProviderEngine(IEnumerable serviceDescriptors) : base(serviceDescriptors) + public ILEmitServiceProviderEngine(ServiceProvider serviceProvider) { - _expressionResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root); + _expressionResolverBuilder = new ILEmitResolverBuilder(serviceProvider); } - protected override Func RealizeService(ServiceCallSite callSite) + public override Func RealizeService(ServiceCallSite callSite) { - Func realizedService = _expressionResolverBuilder.Build(callSite); - RealizedServices[callSite.ServiceType] = realizedService; - return realizedService; + return _expressionResolverBuilder.Build(callSite); } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngine.cs deleted file mode 100644 index 1400c7990dbf8..0000000000000 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngine.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.Extensions.DependencyInjection.ServiceLookup -{ - internal interface IServiceProviderEngine : IServiceProvider, IDisposable, IAsyncDisposable - { - IServiceScope RootScope { get; } - void InitializeCallback(IServiceProviderEngineCallback callback); - void ValidateService(ServiceDescriptor descriptor); - } -} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngineCallback.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngineCallback.cs deleted file mode 100644 index 0b0eee170fba4..0000000000000 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngineCallback.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System; - -namespace Microsoft.Extensions.DependencyInjection.ServiceLookup -{ - internal interface IServiceProviderEngineCallback - { - void OnCreate(ServiceCallSite callSite); - void OnResolve(Type serviceType, IServiceScope scope); - } -} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs index 45a41c97fba74..c656092efff96 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs @@ -2,24 +2,20 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Generic; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class RuntimeServiceProviderEngine : ServiceProviderEngine { - public RuntimeServiceProviderEngine(IEnumerable serviceDescriptors) : base(serviceDescriptors) - { - } + public static RuntimeServiceProviderEngine Instance { get; } = new RuntimeServiceProviderEngine(); - protected override Func RealizeService(ServiceCallSite callSite) + private RuntimeServiceProviderEngine() { } + + public override Func RealizeService(ServiceCallSite callSite) { return scope => { - Func realizedService = p => RuntimeResolver.Resolve(callSite, p); - - RealizedServices[callSite.ServiceType] = realizedService; - return realizedService(scope); + return CallSiteRuntimeResolver.Instance.Resolve(callSite, scope); }; } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs index ca33439284172..77cd2533759d9 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs @@ -2,117 +2,11 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Threading.Tasks; namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal abstract class ServiceProviderEngine : IServiceProviderEngine, IServiceScopeFactory + internal abstract class ServiceProviderEngine { - private IServiceProviderEngineCallback _callback; - - private readonly Func> _createServiceAccessor; - - private bool _disposed; - - protected ServiceProviderEngine(IEnumerable serviceDescriptors) - { - _createServiceAccessor = CreateServiceAccessor; - Root = new ServiceProviderEngineScope(this); - RuntimeResolver = new CallSiteRuntimeResolver(); - CallSiteFactory = new CallSiteFactory(serviceDescriptors); - CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); - CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite()); - RealizedServices = new ConcurrentDictionary>(); - } - - internal ConcurrentDictionary> RealizedServices { get; } - - internal CallSiteFactory CallSiteFactory { get; } - - protected CallSiteRuntimeResolver RuntimeResolver { get; } - - public ServiceProviderEngineScope Root { get; } - - public IServiceScope RootScope => Root; - - void IServiceProviderEngine.InitializeCallback(IServiceProviderEngineCallback callback) - { - _callback = callback; - } - - public void ValidateService(ServiceDescriptor descriptor) - { - if (descriptor.ServiceType.IsGenericType && !descriptor.ServiceType.IsConstructedGenericType) - { - return; - } - - try - { - ServiceCallSite callSite = CallSiteFactory.GetCallSite(descriptor, new CallSiteChain()); - if (callSite != null) - { - _callback?.OnCreate(callSite); - } - } - catch (Exception e) - { - throw new InvalidOperationException($"Error while validating the service descriptor '{descriptor}': {e.Message}", e); - } - } - - public object GetService(Type serviceType) => GetService(serviceType, Root); - - protected abstract Func RealizeService(ServiceCallSite callSite); - - public void Dispose() - { - _disposed = true; - Root.Dispose(); - } - - public ValueTask DisposeAsync() - { - _disposed = true; - return Root.DisposeAsync(); - } - - internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) - { - if (_disposed) - { - ThrowHelper.ThrowObjectDisposedException(); - } - - Func realizedService = RealizedServices.GetOrAdd(serviceType, _createServiceAccessor); - _callback?.OnResolve(serviceType, serviceProviderEngineScope); - DependencyInjectionEventSource.Log.ServiceResolved(serviceType); - return realizedService.Invoke(serviceProviderEngineScope); - } - - public IServiceScope CreateScope() - { - if (_disposed) - { - ThrowHelper.ThrowObjectDisposedException(); - } - - return new ServiceProviderEngineScope(this); - } - - private Func CreateServiceAccessor(Type serviceType) - { - ServiceCallSite callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain()); - if (callSite != null) - { - DependencyInjectionEventSource.Log.CallSiteBuilt(serviceType, callSite); - _callback?.OnCreate(callSite); - return RealizeService(callSite); - } - - return _ => null; - } + public abstract Func RealizeService(ServiceCallSite callSite); } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs index d1464e5ff070c..fce0f12152a80 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs @@ -8,28 +8,30 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { - internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IAsyncDisposable + internal sealed class ServiceProviderEngineScope : IServiceScope, IServiceProvider, IAsyncDisposable, IServiceScopeFactory { // For testing only internal Action _captureDisposableCallback; private bool _disposed; - private readonly ScopeState _state; + private List _disposables; - public ServiceProviderEngineScope(ServiceProviderEngine engine) + public ServiceProviderEngineScope(ServiceProvider provider) { - Engine = engine; - _state = new ScopeState(); + ResolvedServices = new Dictionary(); + RootProvider = provider; } - internal Dictionary ResolvedServices => _state.ResolvedServices; + internal Dictionary ResolvedServices { get; } // This lock protects state on the scope, in particular, for the root scope, it protects - // the list of disposable entries only, since ResolvedServices is a concurrent dictionary. + // the list of disposable entries only, since ResolvedServices are cached on CallSites // For other scopes, it protects ResolvedServices and the list of disposables - internal object Sync => _state; + internal object Sync => ResolvedServices; - public ServiceProviderEngine Engine { get; } + public bool IsRootScope => this == RootProvider.Root; + + internal ServiceProvider RootProvider { get; } public object GetService(Type serviceType) { @@ -38,12 +40,12 @@ public object GetService(Type serviceType) ThrowHelper.ThrowObjectDisposedException(); } - return Engine.GetService(serviceType, this); + return RootProvider.GetService(serviceType, this); } public IServiceProvider ServiceProvider => this; - public bool IsRootScope => this == Engine.Root; + public IServiceScope CreateScope() => RootProvider.CreateScope(); internal object CaptureDisposable(object service) { @@ -54,26 +56,35 @@ internal object CaptureDisposable(object service) return service; } + bool disposed = false; lock (Sync) { if (_disposed) { - if (service is IDisposable disposable) - { - disposable.Dispose(); - } - else - { - // sync over async, for the rare case that an object only implements IAsyncDisposable and may end up starving the thread pool. - Task.Run(() => ((IAsyncDisposable)service).DisposeAsync().AsTask()).GetAwaiter().GetResult(); - } + disposed = true; + } + else + { + _disposables ??= new List(); - ThrowHelper.ThrowObjectDisposedException(); + _disposables.Add(service); } + } - _state.Disposables ??= new List(); + // Don't run customer code under the lock + if (disposed) + { + if (service is IDisposable disposable) + { + disposable.Dispose(); + } + else + { + // sync over async, for the rare case that an object only implements IAsyncDisposable and may end up starving the thread pool. + Task.Run(() => ((IAsyncDisposable)service).DisposeAsync().AsTask()).GetAwaiter().GetResult(); + } - _state.Disposables.Add(service); + ThrowHelper.ThrowObjectDisposedException(); } return service; @@ -168,7 +179,7 @@ private List BeginDispose() } // Track statistics about the scope (number of disposable objects and number of disposed services) - _state.Track(Engine); + DependencyInjectionEventSource.Log.ScopeDisposed(RootProvider.GetHashCode(), ResolvedServices.Count, _disposables?.Count ?? 0); // We've transitioned to the disposed state, so future calls to // CaptureDisposable will immediately dispose the object. @@ -179,7 +190,7 @@ private List BeginDispose() // trying to get a cached singleton service. If it doesn't find it // it will try to create a new one which will result in an ObjectDisposedException. - return _state.Disposables; + return _disposables; } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs index fc95d601f5e9f..116c6037cab7d 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs @@ -7,8 +7,9 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup { internal sealed class ServiceScopeFactoryCallSite : ServiceCallSite { - public ServiceScopeFactoryCallSite() : base(ResultCache.None) + public ServiceScopeFactoryCallSite(IServiceScopeFactory value) : base(ResultCache.None) { + Value = value; } public override Type ServiceType { get; } = typeof(IServiceScopeFactory); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/SingletonCallSite.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/SingletonCallSite.cs deleted file mode 100644 index 3134c23c5d137..0000000000000 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/SingletonCallSite.cs +++ /dev/null @@ -1,6 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -namespace Microsoft.Extensions.DependencyInjection.ServiceLookup -{ -} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs index 39f19a5037533..228fb45e3aa22 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs @@ -2,7 +2,9 @@ // The .NET Foundation licenses this file to you under the MIT license. using System; +using System.Collections.Concurrent; using System.Collections.Generic; +using System.Runtime.CompilerServices; using System.Threading.Tasks; using Microsoft.Extensions.DependencyInjection.ServiceLookup; @@ -11,19 +13,36 @@ namespace Microsoft.Extensions.DependencyInjection /// /// The default IServiceProvider. /// - public sealed class ServiceProvider : IServiceProvider, IDisposable, IServiceProviderEngineCallback, IAsyncDisposable + public sealed class ServiceProvider : IServiceProvider, IDisposable, IAsyncDisposable { - private readonly IServiceProviderEngine _engine; - private readonly CallSiteValidator _callSiteValidator; - internal ServiceProvider(IEnumerable serviceDescriptors, IServiceProviderEngine engine, ServiceProviderOptions options) + private readonly Func> _createServiceAccessor; + + // Internal for testing + internal ServiceProviderEngine _engine; + + private bool _disposed; + + private ConcurrentDictionary> _realizedServices; + + internal CallSiteFactory CallSiteFactory { get; } + + internal ServiceProviderEngineScope Root { get; } + + internal ServiceProvider(IEnumerable serviceDescriptors, ServiceProviderOptions options) { - _engine = engine; + _engine = GetEngine(); + _createServiceAccessor = CreateServiceAccessor; + _realizedServices = new ConcurrentDictionary>(); + + Root = new ServiceProviderEngineScope(this); + CallSiteFactory = new CallSiteFactory(serviceDescriptors); + CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite()); + CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite(Root)); if (options.ValidateScopes) { - _engine.InitializeCallback(this); _callSiteValidator = new CallSiteValidator(); } @@ -34,7 +53,7 @@ internal ServiceProvider(IEnumerable serviceDescriptors, ISer { try { - _engine.ValidateService(serviceDescriptor); + ValidateService(serviceDescriptor); } catch (Exception e) { @@ -48,6 +67,7 @@ internal ServiceProvider(IEnumerable serviceDescriptors, ISer throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray()); } } + } /// @@ -55,28 +75,120 @@ internal ServiceProvider(IEnumerable serviceDescriptors, ISer /// /// The type of the service to get. /// The service that was produced. - public object GetService(Type serviceType) => _engine.GetService(serviceType); + public object GetService(Type serviceType) => GetService(serviceType, Root); /// public void Dispose() { - _engine.Dispose(); + _disposed = true; + Root.Dispose(); } - void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite) + /// + public ValueTask DisposeAsync() { - _callSiteValidator.ValidateCallSite(callSite); + _disposed = true; + return Root.DisposeAsync(); } - void IServiceProviderEngineCallback.OnResolve(Type serviceType, IServiceScope scope) + private void OnCreate(ServiceCallSite callSite) { - _callSiteValidator.ValidateResolution(serviceType, scope, _engine.RootScope); + _callSiteValidator?.ValidateCallSite(callSite); } - /// - public ValueTask DisposeAsync() + private void OnResolve(Type serviceType, IServiceScope scope) + { + _callSiteValidator?.ValidateResolution(serviceType, scope, Root); + } + + internal object GetService(Type serviceType, ServiceProviderEngineScope serviceProviderEngineScope) + { + if (_disposed) + { + ThrowHelper.ThrowObjectDisposedException(); + } + + Func realizedService = _realizedServices.GetOrAdd(serviceType, _createServiceAccessor); + OnResolve(serviceType, serviceProviderEngineScope); + DependencyInjectionEventSource.Log.ServiceResolved(serviceType); + return realizedService.Invoke(serviceProviderEngineScope); + } + + private void ValidateService(ServiceDescriptor descriptor) + { + if (descriptor.ServiceType.IsGenericType && !descriptor.ServiceType.IsConstructedGenericType) + { + return; + } + + try + { + ServiceCallSite callSite = CallSiteFactory.GetCallSite(descriptor, new CallSiteChain()); + if (callSite != null) + { + OnCreate(callSite); + } + } + catch (Exception e) + { + throw new InvalidOperationException($"Error while validating the service descriptor '{descriptor}': {e.Message}", e); + } + } + + private Func CreateServiceAccessor(Type serviceType) + { + ServiceCallSite callSite = CallSiteFactory.GetCallSite(serviceType, new CallSiteChain()); + if (callSite != null) + { + DependencyInjectionEventSource.Log.CallSiteBuilt(serviceType, callSite); + OnCreate(callSite); + + // Optimize singleton case + if (callSite.Cache.Location == CallSiteResultCacheLocation.Root) + { + object value = CallSiteRuntimeResolver.Instance.Resolve(callSite, Root); + return scope => value; + } + + return _engine.RealizeService(callSite); + } + + return _ => null; + } + + internal void ReplaceServiceAccessor(ServiceCallSite callSite, Func accessor) { - return _engine.DisposeAsync(); + _realizedServices[callSite.ImplementationType] = accessor; + } + + internal IServiceScope CreateScope() + { + if (_disposed) + { + ThrowHelper.ThrowObjectDisposedException(); + } + + return new ServiceProviderEngineScope(this); + } + + private ServiceProviderEngine GetEngine() + { + ServiceProviderEngine engine; + +#if !NETSTANDARD2_1 + engine = new DynamicServiceProviderEngine(this); +#else + if (RuntimeFeature.IsDynamicCodeCompiled) + { + engine = new DynamicServiceProviderEngine(this); + } + else + { + // Don't try to compile Expressions/IL if they are going to get interpreted + engine = RuntimeServiceProviderEngine.Instance; + } +#endif + return engine; } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/CallSiteTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/CallSiteTests.cs index da75b6af9a4a6..c79594a92eb8e 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/CallSiteTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/CallSiteTests.cs @@ -12,8 +12,6 @@ namespace Microsoft.Extensions.DependencyInjection.Tests { public class CallSiteTests { - private static readonly CallSiteRuntimeResolver CallSiteRuntimeResolver = new CallSiteRuntimeResolver(); - public static IEnumerable TestServiceDescriptors(ServiceLifetime lifetime) { Func compare; @@ -81,7 +79,7 @@ public static IEnumerable TestServiceDescriptors(ServiceLifetime lifet public void BuiltExpressionWillReturnResolvedServiceWhenAppropriate( ServiceDescriptor[] descriptors, Type serviceType, Func compare) { - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); var callSite = provider.CallSiteFactory.GetCallSite(serviceType, new CallSiteChain()); var collectionCallSite = provider.CallSiteFactory.GetCallSite(typeof(IEnumerable<>).MakeGenericType(serviceType), new CallSiteChain()); @@ -112,7 +110,7 @@ public void BuiltExpressionCanResolveNestedScopedService() descriptors.AddScoped(); descriptors.AddScoped(); - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); var callSite = provider.CallSiteFactory.GetCallSite(typeof(ServiceC), new CallSiteChain()); var compiledCallSite = CompileCallSite(callSite, provider); @@ -136,7 +134,7 @@ public void BuildExpressionAddsDisposableCaptureForDisposableServices(ServiceLif descriptors.Add(ServiceDescriptor.Describe(typeof(ServiceC), typeof(DisposableServiceC), lifetime)); var disposables = new List(); - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); provider.Root._captureDisposableCallback = obj => { disposables.Add(obj); @@ -162,7 +160,7 @@ public void BuildExpressionAddsDisposableCaptureForDisposableFactoryServices(Ser typeof(ServiceC), p => new DisposableServiceC(p.GetService()), lifetime)); var disposables = new List(); - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); provider.Root._captureDisposableCallback = obj => { disposables.Add(obj); @@ -191,7 +189,7 @@ public void BuildExpressionElidesDisposableCaptureForNonDisposableServices(Servi descriptors.AddTransient(); var disposables = new List(); - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); provider.Root._captureDisposableCallback = obj => { disposables.Add(obj); @@ -216,7 +214,7 @@ public void BuildExpressionElidesDisposableCaptureForEnumerableServices(ServiceL descriptors.Add(ServiceDescriptor.Describe(typeof(ServiceD), typeof(ServiceD), lifetime)); var disposables = new List(); - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); provider.Root._captureDisposableCallback = obj => { disposables.Add(obj); @@ -237,7 +235,7 @@ public void BuiltExpressionRethrowsOriginalExceptionFromConstructor() descriptors.AddTransient(); descriptors.AddTransient(); - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); var callSite1 = provider.CallSiteFactory.GetCallSite(typeof(ClassWithThrowingEmptyCtor), new CallSiteChain()); var compiledCallSite1 = CompileCallSite(callSite1, provider); @@ -260,7 +258,7 @@ public void DoesNotThrowWhenServiceIsUsedAsEnumerableAndNotInOneCallSite() descriptors.AddTransient(); descriptors.AddTransient(); - var provider = new DynamicServiceProviderEngine(descriptors); + var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default); var callSite1 = provider.CallSiteFactory.GetCallSite(typeof(ServiceE), new CallSiteChain()); var compileCallSite = CompileCallSite(callSite1, provider); @@ -377,12 +375,12 @@ public void Dispose() private static object Invoke(ServiceCallSite callSite, ServiceProviderEngineScope scope) { - return CallSiteRuntimeResolver.Resolve(callSite, scope); + return CallSiteRuntimeResolver.Instance.Resolve(callSite, scope); } - private static Func CompileCallSite(ServiceCallSite callSite, ServiceProviderEngine engine) + private static Func CompileCallSite(ServiceCallSite callSite, ServiceProvider provider) { - return new ExpressionResolverBuilder(CallSiteRuntimeResolver, engine, engine.Root).Build(callSite); + return new ExpressionResolverBuilder(provider).Build(callSite); } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceCollectionContainerBuilderTestExtensions.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceCollectionContainerBuilderTestExtensions.cs index 3a8c24e51336b..d7a07150bf76a 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceCollectionContainerBuilderTestExtensions.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceCollectionContainerBuilderTestExtensions.cs @@ -17,16 +17,17 @@ public static ServiceProvider BuildServiceProvider(this IServiceCollection servi return services.BuildServiceProvider(options); } - IServiceProviderEngine engine = mode switch + var provider = new ServiceProvider(services, ServiceProviderOptions.Default); + ServiceProviderEngine engine = mode switch { - ServiceProviderMode.Dynamic => new DynamicServiceProviderEngine(services), - ServiceProviderMode.Runtime => new RuntimeServiceProviderEngine(services), - ServiceProviderMode.Expressions => new ExpressionsServiceProviderEngine(services), - ServiceProviderMode.ILEmit => new ILEmitServiceProviderEngine(services), + ServiceProviderMode.Dynamic => new DynamicServiceProviderEngine(provider), + ServiceProviderMode.Runtime => RuntimeServiceProviderEngine.Instance, + ServiceProviderMode.Expressions => new ExpressionsServiceProviderEngine(provider), + ServiceProviderMode.ILEmit => new ILEmitServiceProviderEngine(provider), _ => throw new NotSupportedException() }; - - return new ServiceProvider(services, engine, options); + provider._engine = engine; + return provider; } } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs index 95fde1c98e8ea..5c19caa922e76 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs @@ -1,7 +1,6 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. -using System; using Microsoft.Extensions.DependencyInjection.Specification.Fakes; using Xunit; @@ -12,25 +11,11 @@ public class ServiceProviderEngineScopeTests [Fact] public void DoubleDisposeWorks() { - var engine = new FakeEngine(); - var serviceProviderEngineScope = new ServiceProviderEngineScope(engine); + var provider = new ServiceProvider(new ServiceCollection(), ServiceProviderOptions.Default); + var serviceProviderEngineScope = new ServiceProviderEngineScope(provider); serviceProviderEngineScope.ResolvedServices.Add(new ServiceCacheKey(typeof(IFakeService), 0), null); serviceProviderEngineScope.Dispose(); serviceProviderEngineScope.Dispose(); } - - private class FakeEngine : ServiceProviderEngine - { - public FakeEngine() : - base(Array.Empty()) - { - } - - protected override Func RealizeService(ServiceCallSite callSite) - { - return scope => null; - } - - } } }