Remove indirection in DependencyInjection (#52140)
authorPavel Krymets <pavel@krymets.com>
Wed, 26 May 2021 06:52:11 +0000 (23:52 -0700)
committerGitHub <noreply@github.com>
Wed, 26 May 2021 06:52:11 +0000 (23:52 -0700)
- Remove indirection
- Get rid of scope state and lock on ResolvedServices
- Don't run customer code under the lock
- No public API changes

21 files changed:
src/libraries/Microsoft.Extensions.DependencyInjection/src/DependencyInjectionEventSource.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopeState.cs [deleted file]
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceCollectionContainerBuilderExtensions.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteRuntimeResolver.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CompiledServiceProviderEngine.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/DynamicServiceProviderEngine.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionResolverBuilder.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/Expressions/ExpressionsServiceProviderEngine.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitResolverBuilder.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ILEmit/ILEmitServiceProviderEngine.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngine.cs [deleted file]
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/IServiceProviderEngineCallback.cs [deleted file]
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/RuntimeServiceProviderEngine.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngine.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceProviderEngineScope.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ServiceScopeFactoryCallSite.cs
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/SingletonCallSite.cs [deleted file]
src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceProvider.cs
src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/CallSiteTests.cs
src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceCollectionContainerBuilderTestExtensions.cs
src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderEngineScopeTests.cs

index 24034d5..32f7596 100644 (file)
@@ -68,15 +68,6 @@ namespace Microsoft.Extensions.DependencyInjection
         }
 
         [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)
         {
             if (IsEnabled(EventLevel.Verbose, EventKeywords.All))
diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopeState.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ScopeState.cs
deleted file mode 100644 (file)
index 14570e1..0000000
+++ /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<ServiceCacheKey, object> ResolvedServices { get; }
-        public List<object> Disposables { get; set; }
-
-        public int DisposableServicesCount => Disposables?.Count ?? 0;
-        public int ResolvedServicesCount => ResolvedServices.Count;
-
-        public ScopeState()
-        {
-            ResolvedServices = new Dictionary<ServiceCacheKey, object>();
-        }
-
-        public void Track(ServiceProviderEngine engine)
-        {
-            DependencyInjectionEventSource.Log.ScopeDisposed(engine, this);
-        }
-    }
-}
index 0789d35..ce4dd0c 100644 (file)
@@ -58,23 +58,7 @@ namespace Microsoft.Extensions.DependencyInjection
                 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);
         }
     }
 }
index 7479759..e7ddb41 100644 (file)
@@ -12,6 +12,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 {
     internal sealed class CallSiteRuntimeResolver : CallSiteVisitor<RuntimeResolverContext, object>
     {
+        public static CallSiteRuntimeResolver Instance { get; } = new();
+
+        private CallSiteRuntimeResolver()
+        {
+        }
+
         public object Resolve(ServiceCallSite callSite, ServiceProviderEngineScope scope)
         {
             return VisitCallSite(callSite, new RuntimeResolverContext
@@ -66,7 +72,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
             }
 
             var lockType = RuntimeResolverLock.Root;
-            ServiceProviderEngineScope serviceProviderEngine = context.Scope.Engine.Root;
+            ServiceProviderEngineScope serviceProviderEngine = context.Scope.RootProvider.Root;
 
             lock (callSite)
             {
@@ -91,7 +97,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
         {
             // 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
         protected override object VisitServiceScopeFactory(ServiceScopeFactoryCallSite serviceScopeFactoryCallSite, RuntimeResolverContext context)
         {
-            return context.Scope.Engine;
+            return serviceScopeFactoryCallSite.Value;
         }
 
         protected override object VisitIEnumerable(IEnumerableCallSite enumerableCallSite, RuntimeResolverContext context)
index c7661d2..b51b879 100644 (file)
@@ -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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
         public ExpressionResolverBuilder ResolverBuilder { get; }
 #endif
 
-        public CompiledServiceProviderEngine(IEnumerable<ServiceDescriptor> 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<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
-        {
-            Func<ServiceProviderEngineScope, object> realizedService = ResolverBuilder.Build(callSite);
-            RealizedServices[callSite.ServiceType] = realizedService;
-            return realizedService;
-        }
+        public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite) => ResolverBuilder.Build(callSite);
     }
 }
index fa41c2d..23914ec 100644 (file)
@@ -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<ServiceDescriptor> serviceDescriptors)
-            : base(serviceDescriptors)
+        private readonly ServiceProvider _serviceProvider;
+
+        public DynamicServiceProviderEngine(ServiceProvider serviceProvider): base(serviceProvider)
         {
+            _serviceProvider = serviceProvider;
         }
 
-        protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
+        public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
         {
             int callCount = 0;
             return scope =>
@@ -29,7 +30,7 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
                 // 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
                     {
                         try
                         {
-                            base.RealizeService(callSite);
+                            _serviceProvider.ReplaceServiceAccessor(callSite, base.RealizeService(callSite));
                         }
                         catch (Exception ex)
                         {
index 8a7cff8..3fd0726 100644 (file)
@@ -44,39 +44,21 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
                     Expression.Call(ScopeParameter, CaptureDisposableMethodInfo, CaptureDisposableParameter),
                     CaptureDisposableParameter);
 
-        private readonly CallSiteRuntimeResolver _runtimeResolver;
-
-        private readonly IServiceScopeFactory _serviceScopeFactory;
-
         private readonly ServiceProviderEngineScope _rootScope;
 
         private readonly ConcurrentDictionary<ServiceCacheKey, Func<ServiceProviderEngineScope, object>> _scopeResolverCache;
 
         private readonly Func<ServiceCacheKey, ServiceCallSite, Func<ServiceProviderEngineScope, object>> _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<ServiceCacheKey, Func<ServiceProviderEngineScope, object>>();
-            _runtimeResolver = runtimeResolver;
-            _serviceScopeFactory = serviceScopeFactory;
-            _rootScope = rootScope;
             _buildTypeDelegate = (key, cs) => BuildNoCache(cs);
         }
 
         public Func<ServiceProviderEngineScope, object> 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
         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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
         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)
index eb6ea1e..9de2fa8 100644 (file)
@@ -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<ServiceDescriptor> serviceDescriptors) : base(serviceDescriptors)
+
+        public ExpressionsServiceProviderEngine(ServiceProvider serviceProvider)
         {
-            _expressionResolverBuilder = new ExpressionResolverBuilder(RuntimeResolver, this, Root);
+            _expressionResolverBuilder = new ExpressionResolverBuilder(serviceProvider);
         }
 
-        protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
+        public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
         {
-            Func<ServiceProviderEngineScope, object> realizedService = _expressionResolverBuilder.Build(callSite);
-            RealizedServices[callSite.ServiceType] = realizedService;
-            return realizedService;
+            return _expressionResolverBuilder.Build(callSite);
         }
     }
 }
index 9c9ace3..227a2f8 100644 (file)
@@ -25,7 +25,6 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
         private sealed class ILEmitResolverBuilderRuntimeContext
         {
-            public IServiceScopeFactory ScopeFactory;
             public object[] Constants;
             public Func<IServiceProvider, object>[] Factories;
         }
@@ -38,39 +37,21 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
             public DynamicMethod DynamicMethod;
         }
 
-        private readonly CallSiteRuntimeResolver _runtimeResolver;
-
-        private readonly IServiceScopeFactory _serviceScopeFactory;
-
         private readonly ServiceProviderEngineScope _rootScope;
 
         private readonly ConcurrentDictionary<ServiceCacheKey, GeneratedMethod> _scopeResolverCache;
 
         private readonly Func<ServiceCacheKey, ServiceCallSite, GeneratedMethod> _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<ServiceCacheKey, GeneratedMethod>();
             _buildTypeDelegate = (key, cs) => BuildTypeNoCache(cs);
         }
 
         public Func<ServiceProviderEngineScope, object> 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
         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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 
         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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
             return new ILEmitResolverBuilderRuntimeContext
             {
                 Constants = context.Constants?.ToArray(),
-                Factories = context.Factories?.ToArray(),
-                ScopeFactory = _serviceScopeFactory
+                Factories = context.Factories?.ToArray()
             };
         }
 
index ca8a1ad..f0bcebf 100644 (file)
@@ -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<ServiceDescriptor> serviceDescriptors) : base(serviceDescriptors)
+        public ILEmitServiceProviderEngine(ServiceProvider serviceProvider)
         {
-            _expressionResolverBuilder = new ILEmitResolverBuilder(RuntimeResolver, this, Root);
+            _expressionResolverBuilder = new ILEmitResolverBuilder(serviceProvider);
         }
 
-        protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
+        public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
         {
-            Func<ServiceProviderEngineScope, object> 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 (file)
index 1400c79..0000000
+++ /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 (file)
index 0b0eee1..0000000
+++ /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);
-    }
-}
index 45a41c9..c656092 100644 (file)
@@ -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<ServiceDescriptor> serviceDescriptors) : base(serviceDescriptors)
-        {
-        }
+        public static RuntimeServiceProviderEngine Instance { get; } = new RuntimeServiceProviderEngine();
 
-        protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
+        private RuntimeServiceProviderEngine() { }
+
+        public override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
         {
             return scope =>
             {
-                Func<ServiceProviderEngineScope, object> realizedService = p => RuntimeResolver.Resolve(callSite, p);
-
-                RealizedServices[callSite.ServiceType] = realizedService;
-                return realizedService(scope);
+                return CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
             };
         }
     }
index ca33439..77cd253 100644 (file)
 // 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<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;
-
-        private bool _disposed;
-
-        protected ServiceProviderEngine(IEnumerable<ServiceDescriptor> 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<Type, Func<ServiceProviderEngineScope, object>>();
-        }
-
-        internal ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> 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<ServiceProviderEngineScope, object> 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<ServiceProviderEngineScope, object> 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<ServiceProviderEngineScope, object> 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<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite);
     }
 }
index d1464e5..fce0f12 100644 (file)
@@ -8,28 +8,30 @@ using Microsoft.Extensions.Internal;
 
 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<object> _captureDisposableCallback;
 
         private bool _disposed;
-        private readonly ScopeState _state;
+        private List<object> _disposables;
 
-        public ServiceProviderEngineScope(ServiceProviderEngine engine)
+        public ServiceProviderEngineScope(ServiceProvider provider)
         {
-            Engine = engine;
-            _state = new ScopeState();
+            ResolvedServices = new Dictionary<ServiceCacheKey, object>();
+            RootProvider = provider;
         }
 
-        internal Dictionary<ServiceCacheKey, object> ResolvedServices => _state.ResolvedServices;
+        internal Dictionary<ServiceCacheKey, object> 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
                 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
                 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<object>();
 
-                    ThrowHelper.ThrowObjectDisposedException();
+                    _disposables.Add(service);
                 }
+            }
 
-                _state.Disposables ??= new List<object>();
+            // 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
                 }
 
                 // 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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
                 // 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;
             }
         }
     }
index fc95d60..116c603 100644 (file)
@@ -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 (file)
index 3134c23..0000000
+++ /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
-{
-}
index 39f19a5..228fb45 100644 (file)
@@ -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
     /// <summary>
     /// The default IServiceProvider.
     /// </summary>
-    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<ServiceDescriptor> serviceDescriptors, IServiceProviderEngine engine, ServiceProviderOptions options)
+        private readonly Func<Type, Func<ServiceProviderEngineScope, object>> _createServiceAccessor;
+
+        // Internal for testing
+        internal ServiceProviderEngine _engine;
+
+        private bool _disposed;
+
+        private ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>> _realizedServices;
+
+        internal CallSiteFactory CallSiteFactory { get; }
+
+        internal ServiceProviderEngineScope Root { get; }
+
+        internal ServiceProvider(IEnumerable<ServiceDescriptor> serviceDescriptors, ServiceProviderOptions options)
         {
-            _engine = engine;
+            _engine = GetEngine();
+            _createServiceAccessor = CreateServiceAccessor;
+            _realizedServices = new ConcurrentDictionary<Type, Func<ServiceProviderEngineScope, object>>();
+
+            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 @@ namespace Microsoft.Extensions.DependencyInjection
                 {
                     try
                     {
-                        _engine.ValidateService(serviceDescriptor);
+                        ValidateService(serviceDescriptor);
                     }
                     catch (Exception e)
                     {
@@ -48,6 +67,7 @@ namespace Microsoft.Extensions.DependencyInjection
                     throw new AggregateException("Some services are not able to be constructed", exceptions.ToArray());
                 }
             }
+
         }
 
         /// <summary>
@@ -55,28 +75,120 @@ namespace Microsoft.Extensions.DependencyInjection
         /// </summary>
         /// <param name="serviceType">The type of the service to get.</param>
         /// <returns>The service that was produced.</returns>
-        public object GetService(Type serviceType) => _engine.GetService(serviceType);
+        public object GetService(Type serviceType) => GetService(serviceType, Root);
 
         /// <inheritdoc />
         public void Dispose()
         {
-            _engine.Dispose();
+            _disposed = true;
+            Root.Dispose();
         }
 
-        void IServiceProviderEngineCallback.OnCreate(ServiceCallSite callSite)
+        /// <inheritdoc/>
+        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);
         }
 
-        /// <inheritdoc/>
-        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<ServiceProviderEngineScope, object> 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<ServiceProviderEngineScope, object> 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<ServiceProviderEngineScope, object> 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;
         }
     }
 }
index da75b6a..c79594a 100644 (file)
@@ -12,8 +12,6 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
 {
     public class CallSiteTests
     {
-        private static readonly CallSiteRuntimeResolver CallSiteRuntimeResolver = new CallSiteRuntimeResolver();
-
         public static IEnumerable<object[]> TestServiceDescriptors(ServiceLifetime lifetime)
         {
             Func<object, object, bool> compare;
@@ -81,7 +79,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
         public void BuiltExpressionWillReturnResolvedServiceWhenAppropriate(
             ServiceDescriptor[] descriptors, Type serviceType, Func<object, object, bool> 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 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
             descriptors.AddScoped<ServiceB>();
             descriptors.AddScoped<ServiceC>();
 
-            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 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
             descriptors.Add(ServiceDescriptor.Describe(typeof(ServiceC), typeof(DisposableServiceC), lifetime));
 
             var disposables = new List<object>();
-            var provider = new DynamicServiceProviderEngine(descriptors);
+            var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
             provider.Root._captureDisposableCallback = obj =>
             {
                 disposables.Add(obj);
@@ -162,7 +160,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
                 typeof(ServiceC), p => new DisposableServiceC(p.GetService<ServiceB>()), lifetime));
 
             var disposables = new List<object>();
-            var provider = new DynamicServiceProviderEngine(descriptors);
+            var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
             provider.Root._captureDisposableCallback = obj =>
             {
                 disposables.Add(obj);
@@ -191,7 +189,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
             descriptors.AddTransient<ServiceC>();
 
             var disposables = new List<object>();
-            var provider = new DynamicServiceProviderEngine(descriptors);
+            var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
             provider.Root._captureDisposableCallback = obj =>
             {
                 disposables.Add(obj);
@@ -216,7 +214,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
             descriptors.Add(ServiceDescriptor.Describe(typeof(ServiceD), typeof(ServiceD), lifetime));
 
             var disposables = new List<object>();
-            var provider = new DynamicServiceProviderEngine(descriptors);
+            var provider = new ServiceProvider(descriptors, ServiceProviderOptions.Default);
             provider.Root._captureDisposableCallback = obj =>
             {
                 disposables.Add(obj);
@@ -237,7 +235,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
             descriptors.AddTransient<ClassWithThrowingCtor>();
             descriptors.AddTransient<IFakeService, FakeService>();
 
-            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 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
             descriptors.AddTransient<ServiceD>();
             descriptors.AddTransient<ServiceE>();
 
-            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 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
 
         private static object Invoke(ServiceCallSite callSite, ServiceProviderEngineScope scope)
         {
-            return CallSiteRuntimeResolver.Resolve(callSite, scope);
+            return CallSiteRuntimeResolver.Instance.Resolve(callSite, scope);
         }
 
-        private static Func<ServiceProviderEngineScope, object> CompileCallSite(ServiceCallSite callSite, ServiceProviderEngine engine)
+        private static Func<ServiceProviderEngineScope, object> CompileCallSite(ServiceCallSite callSite, ServiceProvider provider)
         {
-            return new ExpressionResolverBuilder(CallSiteRuntimeResolver, engine, engine.Root).Build(callSite);
+            return new ExpressionResolverBuilder(provider).Build(callSite);
         }
     }
 }
index 3a8c24e..d7a0715 100644 (file)
@@ -17,16 +17,17 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
                 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;
         }
     }
 }
index 95fde1c..5c19caa 100644 (file)
@@ -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 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
         [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<ServiceDescriptor>())
-            {
-            }
-
-            protected override Func<ServiceProviderEngineScope, object> RealizeService(ServiceCallSite callSite)
-            {
-                return scope => null;
-            }
-
-        }
     }
 }