Fix NRE when default value is null and ServiceCallSite is not found (#44675)
authorMaryam Ariyan <maryam.ariyan@microsoft.com>
Wed, 18 Nov 2020 04:33:27 +0000 (20:33 -0800)
committerGitHub <noreply@github.com>
Wed, 18 Nov 2020 04:33:27 +0000 (20:33 -0800)
* Fix NRE when default value is null and ServiceCallSite is not found

src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/ConstantCallSite.cs
src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/CallSiteTests.cs
src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Tests/ServiceProviderCompilationTest.cs

index acc1d96..bd09485 100644 (file)
@@ -7,10 +7,12 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
 {
     internal class ConstantCallSite : ServiceCallSite
     {
+        private readonly Type _serviceType;
         internal object DefaultValue { get; }
 
         public ConstantCallSite(Type serviceType, object defaultValue): base(ResultCache.None)
         {
+            _serviceType = serviceType ?? throw new ArgumentNullException(nameof(serviceType));
             if (defaultValue != null && !serviceType.IsInstanceOfType(defaultValue))
             {
                 throw new ArgumentException(SR.Format(SR.ConstantCantBeConvertedToServiceType, defaultValue.GetType(), serviceType));
@@ -19,8 +21,8 @@ namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
             DefaultValue = defaultValue;
         }
 
-        public override Type ServiceType => DefaultValue.GetType();
-        public override Type ImplementationType => DefaultValue.GetType();
+        public override Type ServiceType => DefaultValue?.GetType() ?? _serviceType;
+        public override Type ImplementationType => DefaultValue?.GetType() ?? _serviceType;
         public override CallSiteKind Kind { get; } = CallSiteKind.Constant;
     }
 }
index 3dc3cec..c83e277 100644 (file)
@@ -264,6 +264,37 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
             Assert.NotNull(compileCallSite);
         }
 
+        [Theory]
+        [InlineData(ServiceProviderMode.Default)]
+        [InlineData(ServiceProviderMode.Dynamic)]
+        [InlineData(ServiceProviderMode.Runtime)]
+        [InlineData(ServiceProviderMode.Expressions)]
+        [InlineData(ServiceProviderMode.ILEmit)]
+        private void NoServiceCallsite_DefaultValueNull_DoesNotThrow(ServiceProviderMode mode)
+        {
+            var descriptors = new ServiceCollection();
+            descriptors.AddTransient<ServiceG>();
+
+            var provider = descriptors.BuildServiceProvider(mode);
+            ServiceF instance = ActivatorUtilities.CreateInstance<ServiceF>(provider);
+
+            Assert.NotNull(instance);
+        }
+
+        private interface IServiceG
+        {
+        }
+
+        private class ServiceG
+        {
+            public ServiceG(IServiceG service = null) { }
+        }
+
+        private class ServiceF
+        {
+            public ServiceF(ServiceG service) { }
+        }
+
         private class ServiceD
         {
             public ServiceD(IEnumerable<ServiceA> services)
index 5c6853e..8094dde 100644 (file)
@@ -12,6 +12,7 @@ namespace Microsoft.Extensions.DependencyInjection.Tests
     public class ServiceProviderCompilationTest
     {
         [Theory]
+        [InlineData(ServiceProviderMode.Default, typeof(I999))]
         [InlineData(ServiceProviderMode.Dynamic, typeof(I999))]
         [InlineData(ServiceProviderMode.Runtime, typeof(I999))]
         [InlineData(ServiceProviderMode.ILEmit, typeof(I999))]