From 05b0be11ef4aba5349b802f545e4a68488bce15c Mon Sep 17 00:00:00 2001 From: Jimmy Bogard Date: Wed, 1 Apr 2020 08:29:28 -0500 Subject: [PATCH 1/5] Porting changes from https://github.com/dotnet/extensions/pull/536 --- .../src/ServiceLookup/CallSiteFactory.cs | 22 +- .../DependencyInjectionSpecificationTests.cs | 154 +++++++++ .../Fakes/AbstractClass.cs | 11 + .../Fakes/ClassImplementingIComparable.cs | 13 + .../Fakes/ClassImplementingIEnumerable.cs | 14 + .../Fakes/ClassInheritingAbstractClass.cs | 21 ++ .../Fakes/ClassWithAbstractClassConstraint.cs | 14 + .../Fakes/ClassWithClassConstraint.cs | 12 + .../Fakes/ClassWithInterfaceConstraint.cs | 16 + .../Fakes/ClassWithNewConstraint.cs | 12 + .../Fakes/ClassWithNoConstraint.cs | 11 + .../ClassWithSelfReferencingConstraint.cs | 16 + .../Fakes/ClassWithStructConstraint.cs | 12 + .../ConstrainedFakeOpenGenericService.cs | 16 + .../Fakes/IFakeOpenGenericService.cs | 2 +- .../ServiceLookup/CallSiteFactoryTest.cs | 293 ++++++++++++++++++ 16 files changed, 634 insertions(+), 5 deletions(-) create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/AbstractClass.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIComparable.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIEnumerable.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassInheritingAbstractClass.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithAbstractClassConstraint.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithClassConstraint.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithInterfaceConstraint.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNewConstraint.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNoConstraint.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithSelfReferencingConstraint.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithStructConstraint.cs create mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ConstrainedFakeOpenGenericService.cs diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index 13f93b99bfbaec..fbfc5724b1fd82 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -119,7 +119,7 @@ private ServiceCallSite TryCreateOpenGeneric(Type serviceType, CallSiteChain cal if (serviceType.IsConstructedGenericType && _descriptorLookup.TryGetValue(serviceType.GetGenericTypeDefinition(), out var descriptor)) { - return TryCreateOpenGeneric(descriptor.Last, serviceType, callSiteChain, DefaultSlot); + return TryCreateOpenGeneric(descriptor.Last, serviceType, callSiteChain, DefaultSlot, true); } return null; @@ -165,7 +165,7 @@ private ServiceCallSite TryCreateEnumerable(Type serviceType, CallSiteChain call { var descriptor = _descriptors[i]; var callSite = TryCreateExact(descriptor, itemType, callSiteChain, slot) ?? - TryCreateOpenGeneric(descriptor, itemType, callSiteChain, slot); + TryCreateOpenGeneric(descriptor, itemType, callSiteChain, slot, false); if (callSite != null) { @@ -231,14 +231,28 @@ private ServiceCallSite TryCreateExact(ServiceDescriptor descriptor, Type servic return null; } - private ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot) + private ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type serviceType, CallSiteChain callSiteChain, int slot, bool throwOnConstraintViolation) { if (serviceType.IsConstructedGenericType && serviceType.GetGenericTypeDefinition() == descriptor.ServiceType) { Debug.Assert(descriptor.ImplementationType != null, "descriptor.ImplementationType != null"); var lifetime = new ResultCache(descriptor.Lifetime, serviceType, slot); - var closedType = descriptor.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments); + Type closedType; + try + { + closedType = descriptor.ImplementationType.MakeGenericType(serviceType.GenericTypeArguments); + } + catch (ArgumentException ex) + { + if (throwOnConstraintViolation) + { + throw new InvalidOperationException(Resources.FormatGenericConstraintViolation(serviceType, descriptor.ImplementationType), ex); + } + + return null; + } + return CreateConstructorCallSite(lifetime, serviceType, closedType, callSiteChain); } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/DependencyInjectionSpecificationTests.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/DependencyInjectionSpecificationTests.cs index beed0c8661e4b8..4c717eae8e7289 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/DependencyInjectionSpecificationTests.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/DependencyInjectionSpecificationTests.cs @@ -578,6 +578,160 @@ public void OpenGenericServicesCanBeResolved() Assert.Same(singletonService, genericService.Value); } + [Fact] + public void ConstrainedOpenGenericServicesCanBeResolved() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>)); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ConstrainedFakeOpenGenericService<>)); + var poco = new PocoClass(); + collection.AddSingleton(poco); + collection.AddSingleton(); + var provider = CreateServiceProvider(collection); + // Act + var allServices = provider.GetServices>().ToList(); + var constrainedServices = provider.GetServices>().ToList(); + var singletonService = provider.GetService(); + // Assert + Assert.Equal(2, allServices.Count); + Assert.Same(poco, allServices[0].Value); + Assert.Same(poco, allServices[1].Value); + Assert.Equal(1, constrainedServices.Count); + Assert.Same(singletonService, constrainedServices[0].Value); + } + + [Fact] + public void ConstrainedOpenGenericServicesReturnsEmptyWithNoMatches() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ConstrainedFakeOpenGenericService<>)); + collection.AddSingleton(); + var provider = CreateServiceProvider(collection); + // Act + var constrainedServices = provider.GetServices>().ToList(); + // Assert + Assert.Equal(0, constrainedServices.Count); + } + + [Fact] + public void InterfaceConstrainedOpenGenericServicesCanBeResolved() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>)); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithInterfaceConstraint<>)); + var enumerableVal = new ClassImplementingIEnumerable(); + collection.AddSingleton(enumerableVal); + collection.AddSingleton(); + var provider = CreateServiceProvider(collection); + // Act + var allServices = provider.GetServices>().ToList(); + var constrainedServices = provider.GetServices>().ToList(); + var singletonService = provider.GetService(); + // Assert + Assert.Equal(2, allServices.Count); + Assert.Same(enumerableVal, allServices[0].Value); + Assert.Same(enumerableVal, allServices[1].Value); + Assert.Equal(1, constrainedServices.Count); + Assert.Same(singletonService, constrainedServices[0].Value); + } + + [Fact] + public void PublicNoArgCtorConstrainedOpenGenericServicesCanBeResolved() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithNoConstraints<>)); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithNewConstraint<>)); + var provider = CreateServiceProvider(collection); + // Act + var allServices = provider.GetServices>().ToList(); + var constrainedServices = provider.GetServices>().ToList(); + // Assert + Assert.Equal(2, allServices.Count); + Assert.Equal(1, constrainedServices.Count); + } + + [Fact] + public void ClassConstrainedOpenGenericServicesCanBeResolved() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithNoConstraints<>)); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithClassConstraint<>)); + var provider = CreateServiceProvider(collection); + // Act + var allServices = provider.GetServices>().ToList(); + var constrainedServices = provider.GetServices>().ToList(); + // Assert + Assert.Equal(2, allServices.Count); + Assert.Equal(1, constrainedServices.Count); + } + + [Fact] + public void StructConstrainedOpenGenericServicesCanBeResolved() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithNoConstraints<>)); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithStructConstraint<>)); + var provider = CreateServiceProvider(collection); + // Act + var allServices = provider.GetServices>().ToList(); + var constrainedServices = provider.GetServices>().ToList(); + // Assert + Assert.Equal(2, allServices.Count); + Assert.Equal(1, constrainedServices.Count); + } + + [Fact] + public void AbstractClassConstrainedOpenGenericServicesCanBeResolved() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>)); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithAbstractClassConstraint<>)); + var poco = new PocoClass(); + collection.AddSingleton(poco); + var classInheritingClassInheritingAbstractClass = new ClassInheritingClassInheritingAbstractClass(); + collection.AddSingleton(classInheritingClassInheritingAbstractClass); + var provider = CreateServiceProvider(collection); + // Act + var allServices = provider.GetServices>().ToList(); + var constrainedServices = provider.GetServices>().ToList(); + // Assert + Assert.Equal(2, allServices.Count); + Assert.Same(classInheritingClassInheritingAbstractClass, allServices[0].Value); + Assert.Same(classInheritingClassInheritingAbstractClass, allServices[1].Value); + Assert.Equal(1, constrainedServices.Count); + Assert.Same(poco, constrainedServices[0].Value); + } + + [Fact] + public void SelfReferencingConstrainedOpenGenericServicesCanBeResolved() + { + // Arrange + var collection = new TestServiceCollection(); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>)); + collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(ClassWithSelfReferencingConstraint<>)); + var poco = new PocoClass(); + collection.AddSingleton(poco); + var selfComparable = new ClassImplementingIComparable(); + collection.AddSingleton(selfComparable); + var provider = CreateServiceProvider(collection); + // Act + var allServices = provider.GetServices>().ToList(); + var constrainedServices = provider.GetServices>().ToList(); + // Assert + Assert.Equal(2, allServices.Count); + Assert.Same(selfComparable, allServices[0].Value); + Assert.Same(selfComparable, allServices[1].Value); + Assert.Equal(1, constrainedServices.Count); + Assert.Same(poco, constrainedServices[0].Value); + } + [Fact] public void ClosedServicesPreferredOverOpenGenericServices() { diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/AbstractClass.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/AbstractClass.cs new file mode 100644 index 00000000000000..c6ccfd34870819 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/AbstractClass.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public abstract class AbstractClass + { + + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIComparable.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIComparable.cs new file mode 100644 index 00000000000000..02ffe2e9c7961b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIComparable.cs @@ -0,0 +1,13 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassImplementingIComparable : IComparable + { + public int CompareTo(ClassImplementingIComparable other) => 0; + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIEnumerable.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIEnumerable.cs new file mode 100644 index 00000000000000..20fe4e2fc7ce63 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassImplementingIEnumerable.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; +using System.Collections; + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassImplementingIEnumerable : IEnumerable + { + public IEnumerator GetEnumerator() => throw new NotImplementedException(); + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassInheritingAbstractClass.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassInheritingAbstractClass.cs new file mode 100644 index 00000000000000..ec37f2b103a2c5 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassInheritingAbstractClass.cs @@ -0,0 +1,21 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassInheritingAbstractClass : AbstractClass + { + + } + + public class ClassAlsoInheritingAbstractClass : AbstractClass + { + + } + + public class ClassInheritingClassInheritingAbstractClass : ClassInheritingAbstractClass + { + + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithAbstractClassConstraint.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithAbstractClassConstraint.cs new file mode 100644 index 00000000000000..e5519863348d66 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithAbstractClassConstraint.cs @@ -0,0 +1,14 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassWithAbstractClassConstraint : IFakeOpenGenericService + where T : AbstractClass + { + public ClassWithAbstractClassConstraint(T value) => Value = value; + + public T Value { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithClassConstraint.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithClassConstraint.cs new file mode 100644 index 00000000000000..b180203a688a71 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithClassConstraint.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassWithClassConstraint : IFakeOpenGenericService + where T : class + { + public T Value { get; } = default; + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithInterfaceConstraint.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithInterfaceConstraint.cs new file mode 100644 index 00000000000000..efd2c9c6cef63d --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithInterfaceConstraint.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System.Collections; + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassWithInterfaceConstraint : IFakeOpenGenericService + where T : IEnumerable + { + public ClassWithInterfaceConstraint(T value) => Value = value; + + public T Value { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNewConstraint.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNewConstraint.cs new file mode 100644 index 00000000000000..143986cfa11200 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNewConstraint.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassWithNewConstraint : IFakeOpenGenericService + where T : new() + { + public T Value { get; } = new T(); + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNoConstraint.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNoConstraint.cs new file mode 100644 index 00000000000000..848e7551cd6a30 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithNoConstraint.cs @@ -0,0 +1,11 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassWithNoConstraints : IFakeOpenGenericService + { + public T Value { get; } = default; + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithSelfReferencingConstraint.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithSelfReferencingConstraint.cs new file mode 100644 index 00000000000000..0e464290229407 --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithSelfReferencingConstraint.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +using System; + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassWithSelfReferencingConstraint : IFakeOpenGenericService + where T : IComparable + { + public ClassWithSelfReferencingConstraint(T value) => Value = value; + + public T Value { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithStructConstraint.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithStructConstraint.cs new file mode 100644 index 00000000000000..06355eb905ffed --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ClassWithStructConstraint.cs @@ -0,0 +1,12 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ClassWithStructConstraint : IFakeOpenGenericService + where T : struct + { + public T Value { get; } = default; + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ConstrainedFakeOpenGenericService.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ConstrainedFakeOpenGenericService.cs new file mode 100644 index 00000000000000..940e3621a8772b --- /dev/null +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/ConstrainedFakeOpenGenericService.cs @@ -0,0 +1,16 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes +{ + public class ConstrainedFakeOpenGenericService : IFakeOpenGenericService + where TVal : PocoClass + { + public ConstrainedFakeOpenGenericService(TVal value) + { + Value = value; + } + public TVal Value { get; } + } +} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/IFakeOpenGenericService.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/IFakeOpenGenericService.cs index 12dc7142557384..d4697a14b2e31d 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/IFakeOpenGenericService.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.Specification.Tests/Fakes/IFakeOpenGenericService.cs @@ -4,7 +4,7 @@ namespace Microsoft.Extensions.DependencyInjection.Specification.Fakes { - public interface IFakeOpenGenericService + public interface IFakeOpenGenericService { TValue Value { get; } } diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/ServiceLookup/CallSiteFactoryTest.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/ServiceLookup/CallSiteFactoryTest.cs index edb64427689ce2..6d7327283c9b59 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/ServiceLookup/CallSiteFactoryTest.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/ServiceLookup/CallSiteFactoryTest.cs @@ -112,6 +112,299 @@ public void CreateCallSite_UsesNullaryConstructorIfServicesCannotBeInjectedIntoO Assert.Empty(ctorCallSite.ParameterCallSites); } + [Fact] + public void CreateCallSite_Throws_IfClosedTypeDoesNotSatisfyStructGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithStructConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var nonMatchingType = typeof(IFakeOpenGenericService); + // Assert + var ex = Assert.Throws(() => callSiteFactory(nonMatchingType)); + Assert.Equal($"Generic constraints violated for type '{nonMatchingType}' while attempting to activate '{implementationType}'.", ex.Message); + } + + [Fact] + public void CreateCallSite_ReturnsService_IfClosedTypeSatisfiesStructGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithStructConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var matchingType = typeof(IFakeOpenGenericService); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + Assert.NotNull(matchingCallSite); + } + + [Fact] + public void CreateCallSite_Throws_IfClosedTypeDoesNotSatisfyClassGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithClassConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var nonMatchingType = typeof(IFakeOpenGenericService); + // Assert + var ex = Assert.Throws(() => callSiteFactory(nonMatchingType)); + Assert.Equal($"Generic constraints violated for type '{nonMatchingType}' while attempting to activate '{implementationType}'.", ex.Message); + } + + [Fact] + public void CreateCallSite_ReturnsService_IfClosedTypeSatisfiesClassGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithClassConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var matchingType = typeof(IFakeOpenGenericService); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + Assert.NotNull(matchingCallSite); + } + + [Fact] + public void CreateCallSite_Throws_IfClosedTypeDoesNotSatisfyNewGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithNewConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var nonMatchingType = typeof(IFakeOpenGenericService); + // Assert + var ex = Assert.Throws(() => callSiteFactory(nonMatchingType)); + Assert.Equal($"Generic constraints violated for type '{nonMatchingType}' while attempting to activate '{implementationType}'.", ex.Message); + } + + [Fact] + public void CreateCallSite_ReturnsService_IfClosedTypeSatisfiesNewGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithNewConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor, new ServiceDescriptor(typeof(TypeWithParameterlessPublicConstructor), new TypeWithParameterlessPublicConstructor())); + // Act + var matchingType = typeof(IFakeOpenGenericService); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + Assert.NotNull(matchingCallSite); + } + + [Fact] + public void CreateCallSite_Throws_IfClosedTypeDoesNotSatisfyInterfaceGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithInterfaceConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var nonMatchingType = typeof(IFakeOpenGenericService); + // Assert + var ex = Assert.Throws(() => callSiteFactory(nonMatchingType)); + Assert.Equal($"Generic constraints violated for type '{nonMatchingType}' while attempting to activate '{implementationType}'.", ex.Message); + } + + [Fact] + public void CreateCallSite_ReturnsService_IfClosedTypeSatisfiesInterfaceGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithInterfaceConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor, new ServiceDescriptor(typeof(string), "")); + // Act + var matchingType = typeof(IFakeOpenGenericService); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + Assert.NotNull(matchingCallSite); + } + + [Fact] + public void CreateCallSite_Throws_IfClosedTypeDoesNotSatisfyAbstractClassGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithAbstractClassConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var nonMatchingType = typeof(IFakeOpenGenericService); + // Assert + var ex = Assert.Throws(() => callSiteFactory(nonMatchingType)); + Assert.Equal($"Generic constraints violated for type '{nonMatchingType}' while attempting to activate '{implementationType}'.", ex.Message); + } + + [Fact] + public void CreateCallSite_ReturnsService_IfClosedTypeSatisfiesAbstractClassGenericConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithAbstractClassConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor, new ServiceDescriptor(typeof(ClassInheritingAbstractClass), new ClassInheritingAbstractClass())); + // Act + var matchingType = typeof(IFakeOpenGenericService); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + Assert.NotNull(matchingCallSite); + } + + [Fact] + public void CreateCallSite_Throws_IfClosedTypeDoesNotSatisfySelfReferencingConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithSelfReferencingConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var nonMatchingType = typeof(IFakeOpenGenericService); + // Assert + var ex = Assert.Throws(() => callSiteFactory(nonMatchingType)); + Assert.Equal($"Generic constraints violated for type '{nonMatchingType}' while attempting to activate '{implementationType}'.", ex.Message); + } + + [Fact] + public void CreateCallSite_Throws_IfComplexClosedTypeDoesNotSatisfySelfReferencingConstraint() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithSelfReferencingConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor); + // Act + var nonMatchingType = typeof(IFakeOpenGenericService); + // Assert + var ex = Assert.Throws(() => callSiteFactory(nonMatchingType)); + Assert.Equal($"Generic constraints violated for type '{nonMatchingType}' while attempting to activate '{implementationType}'.", ex.Message); + } + + [Fact] + public void CreateCallSite_ReturnsService_IfClosedTypeSatisfiesSelfReferencing() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var implementationType = typeof(ClassWithSelfReferencingConstraint<>); + var descriptor = new ServiceDescriptor(serviceType, implementationType, ServiceLifetime.Transient); + var callSiteFactory = GetCallSiteFactory(descriptor, new ServiceDescriptor(typeof(string), "")); + // Act + var matchingType = typeof(IFakeOpenGenericService); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + Assert.NotNull(matchingCallSite); + } + + [Fact] + public void CreateCallSite_ReturnsEmpty_IfClosedTypeSatisfiesBaseClassConstraintButRegisteredTypeNotExactMatch() + { + // Arrange + var classInheritingAbstractClassImplementationType = typeof(ClassWithAbstractClassConstraint); + var classInheritingAbstractClassDescriptor = new ServiceDescriptor(typeof(IFakeOpenGenericService), classInheritingAbstractClassImplementationType, ServiceLifetime.Transient); + var classAlsoInheritingAbstractClassImplementationType = typeof(ClassWithAbstractClassConstraint); + var classAlsoInheritingAbstractClassDescriptor = new ServiceDescriptor(typeof(IFakeOpenGenericService), classAlsoInheritingAbstractClassImplementationType, ServiceLifetime.Transient); + var classInheritingClassInheritingAbstractClassImplementationType = typeof(ClassWithAbstractClassConstraint); + var classInheritingClassInheritingAbstractClassDescriptor = new ServiceDescriptor(typeof(IFakeOpenGenericService), classInheritingClassInheritingAbstractClassImplementationType, ServiceLifetime.Transient); + var notMatchingServiceType = typeof(IFakeOpenGenericService); + var notMatchingType = typeof(FakeService); + var notMatchingDescriptor = new ServiceDescriptor(notMatchingServiceType, notMatchingType, ServiceLifetime.Transient); + + var callSiteFactory = GetCallSiteFactory(classInheritingAbstractClassDescriptor, classAlsoInheritingAbstractClassDescriptor, classInheritingClassInheritingAbstractClassDescriptor, notMatchingDescriptor); + // Act + var matchingType = typeof(IEnumerable>); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + var enumerableCall = Assert.IsType(matchingCallSite); + + Assert.Empty(enumerableCall.ServiceCallSites); + } + + [Fact] + public void CreateCallSite_ReturnsMatchingTypes_IfClosedTypeSatisfiesBaseClassConstraintAndRegisteredType() + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService); + var classInheritingAbstractClassImplementationType = typeof(ClassWithAbstractClassConstraint); + var classInheritingAbstractClassDescriptor = new ServiceDescriptor(serviceType, classInheritingAbstractClassImplementationType, ServiceLifetime.Transient); + var classAlsoInheritingAbstractClassImplementationType = typeof(ClassWithAbstractClassConstraint); + var classAlsoInheritingAbstractClassDescriptor = new ServiceDescriptor(serviceType, classAlsoInheritingAbstractClassImplementationType, ServiceLifetime.Transient); + var classInheritingClassInheritingAbstractClassImplementationType = typeof(ClassWithAbstractClassConstraint); + var classInheritingClassInheritingAbstractClassDescriptor = new ServiceDescriptor(serviceType, classInheritingClassInheritingAbstractClassImplementationType, ServiceLifetime.Transient); + var notMatchingServiceType = typeof(IFakeOpenGenericService); + var notMatchingType = typeof(FakeService); + var notMatchingDescriptor = new ServiceDescriptor(notMatchingServiceType, notMatchingType, ServiceLifetime.Transient); + + var descriptors = new[] + { + classInheritingAbstractClassDescriptor, + new ServiceDescriptor(typeof(ClassInheritingAbstractClass), new ClassInheritingAbstractClass()), + classAlsoInheritingAbstractClassDescriptor, + new ServiceDescriptor(typeof(ClassAlsoInheritingAbstractClass), new ClassAlsoInheritingAbstractClass()), + classInheritingClassInheritingAbstractClassDescriptor, + new ServiceDescriptor(typeof(ClassInheritingClassInheritingAbstractClass), new ClassInheritingClassInheritingAbstractClass()), + notMatchingDescriptor + }; + var callSiteFactory = GetCallSiteFactory(descriptors); + // Act + var matchingType = typeof(IEnumerable<>).MakeGenericType(serviceType); + var matchingCallSite = callSiteFactory(matchingType); + // Assert + var enumerableCall = Assert.IsType(matchingCallSite); + + var matchingTypes = new[] + { + classInheritingAbstractClassImplementationType, + classAlsoInheritingAbstractClassImplementationType, + classInheritingClassInheritingAbstractClassImplementationType + }; + Assert.Equal(matchingTypes.Length, enumerableCall.ServiceCallSites.Length); + Assert.Equal(matchingTypes, enumerableCall.ServiceCallSites.Select(scs => scs.ImplementationType).ToArray()); + } + + [Theory] + [InlineData(typeof(IFakeOpenGenericService), default(int), new[] { typeof(FakeOpenGenericService), typeof(ClassWithStructConstraint), typeof(ClassWithNewConstraint), typeof(ClassWithSelfReferencingConstraint) })] + [InlineData(typeof(IFakeOpenGenericService), "", new[] { typeof(FakeOpenGenericService), typeof(ClassWithClassConstraint), typeof(ClassWithInterfaceConstraint), typeof(ClassWithSelfReferencingConstraint) })] + [InlineData(typeof(IFakeOpenGenericService), new[] { 1, 2, 3 }, new[] { typeof(FakeOpenGenericService), typeof(ClassWithClassConstraint), typeof(ClassWithInterfaceConstraint) })] + public void CreateCallSite_ReturnsMatchingTypesThatMatchCorrectConstraints(Type closedServiceType, object value, Type[] matchingImplementationTypes) + { + // Arrange + var serviceType = typeof(IFakeOpenGenericService<>); + var noConstraintImplementationType = typeof(FakeOpenGenericService<>); + var noConstraintDescriptor = new ServiceDescriptor(serviceType, noConstraintImplementationType, ServiceLifetime.Transient); + var structImplementationType = typeof(ClassWithStructConstraint<>); + var structDescriptor = new ServiceDescriptor(serviceType, structImplementationType, ServiceLifetime.Transient); + var classImplementationType = typeof(ClassWithClassConstraint<>); + var classDescriptor = new ServiceDescriptor(serviceType, classImplementationType, ServiceLifetime.Transient); + var newImplementationType = typeof(ClassWithNewConstraint<>); + var newDescriptor = new ServiceDescriptor(serviceType, newImplementationType, ServiceLifetime.Transient); + var interfaceImplementationType = typeof(ClassWithInterfaceConstraint<>); + var interfaceDescriptor = new ServiceDescriptor(serviceType, interfaceImplementationType, ServiceLifetime.Transient); + var selfConstraintImplementationType = typeof(ClassWithSelfReferencingConstraint<>); + var selfConstraintDescriptor = new ServiceDescriptor(serviceType, selfConstraintImplementationType, ServiceLifetime.Transient); + var serviceValueType = closedServiceType.GenericTypeArguments[0]; + var serviceValueDescriptor = new ServiceDescriptor(serviceValueType, value); + var callSiteFactory = GetCallSiteFactory(noConstraintDescriptor, structDescriptor, classDescriptor, newDescriptor, interfaceDescriptor, selfConstraintDescriptor, serviceValueDescriptor); + var collectionType = typeof(IEnumerable<>).MakeGenericType(closedServiceType); + // Act + var callSite = callSiteFactory(collectionType); + // Assert + var enumerableCall = Assert.IsType(callSite); + Assert.Equal(matchingImplementationTypes.Length, enumerableCall.ServiceCallSites.Length); + Assert.Equal(matchingImplementationTypes, enumerableCall.ServiceCallSites.Select(scs => scs.ImplementationType).ToArray()); + } + public static TheoryData CreateCallSite_PicksConstructorWithTheMostNumberOfResolvedParametersData => new TheoryData, Type[]> { From 3e43d9c62d0d51d3fc93383b7928410ad57c62d7 Mon Sep 17 00:00:00 2001 From: Jimmy Bogard Date: Wed, 1 Apr 2020 09:21:52 -0500 Subject: [PATCH 2/5] Adding resource string --- .../src/Resources/Strings.resx | 3 +++ .../src/ServiceLookup/CallSiteFactory.cs | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx index 354fe5fccbfa5a..134d98fdd448c8 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/Resources/Strings.resx @@ -159,4 +159,7 @@ '{0}' type only implements IAsyncDisposable. Use DisposeAsync to dispose the container. + + Generic constraints violated for type '{0}' while attempting to activate '{1}'. + \ No newline at end of file diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index fbfc5724b1fd82..e1b09b20f29c8b 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -247,7 +247,7 @@ private ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type { if (throwOnConstraintViolation) { - throw new InvalidOperationException(Resources.FormatGenericConstraintViolation(serviceType, descriptor.ImplementationType), ex); + throw new InvalidOperationException(SR.Format(SR.GenericConstraintViolation, serviceType, descriptor.ImplementationType)); } return null; From e205b37f9a4bb53331a4158ba05103e65c464d96 Mon Sep 17 00:00:00 2001 From: Jimmy Bogard Date: Wed, 1 Apr 2020 11:06:37 -0500 Subject: [PATCH 3/5] Compile error --- .../src/ServiceLookup/CallSiteFactory.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs index e1b09b20f29c8b..30d515ce8c11a7 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/src/ServiceLookup/CallSiteFactory.cs @@ -247,7 +247,7 @@ private ServiceCallSite TryCreateOpenGeneric(ServiceDescriptor descriptor, Type { if (throwOnConstraintViolation) { - throw new InvalidOperationException(SR.Format(SR.GenericConstraintViolation, serviceType, descriptor.ImplementationType)); + throw new InvalidOperationException(SR.Format(SR.GenericConstraintViolation, serviceType, descriptor.ImplementationType), ex); } return null; From 3f1b86c188d3f131984257dce4779aac0b69b836 Mon Sep 17 00:00:00 2001 From: Jimmy Bogard Date: Wed, 1 Apr 2020 13:05:35 -0500 Subject: [PATCH 4/5] Removing AbstractClass in Fakes in favor of new AbstractClass in the DI specification --- .../tests/Fakes/AbstractClass.cs | 13 ------------- 1 file changed, 13 deletions(-) delete mode 100644 src/libraries/Microsoft.Extensions.DependencyInjection/tests/Fakes/AbstractClass.cs diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/Fakes/AbstractClass.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/Fakes/AbstractClass.cs deleted file mode 100644 index 40c45ab9e1be39..00000000000000 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/Fakes/AbstractClass.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. -// See the LICENSE file in the project root for more information. - -namespace Microsoft.Extensions.DependencyInjection.Tests.Fakes -{ - public abstract class AbstractClass - { - public AbstractClass() - { - } - } -} From aa71caafdfb64449f08ff05f8ea13eb3e99b6788 Mon Sep 17 00:00:00 2001 From: Jimmy Bogard Date: Tue, 23 Jun 2020 16:07:54 -0500 Subject: [PATCH 5/5] Adding external DI tests and fixing solution file --- .../Microsoft.Extensions.DependencyInjection.sln | 11 +++++++++-- .../tests/DI.External.Tests/Autofac.cs | 9 +++++++-- .../tests/DI.External.Tests/StashBox.cs | 11 +++++++++-- 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/Microsoft.Extensions.DependencyInjection.sln b/src/libraries/Microsoft.Extensions.DependencyInjection/Microsoft.Extensions.DependencyInjection.sln index 84191f744afa0a..603cce0f654ec8 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/Microsoft.Extensions.DependencyInjection.sln +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/Microsoft.Extensions.DependencyInjection.sln @@ -13,9 +13,11 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.Depend EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection", "src\Microsoft.Extensions.DependencyInjection.csproj", "{0BD0C911-A456-40DA-B5B7-D51887777BF9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Tests", "tests\Microsoft.Extensions.DependencyInjection.Tests.csproj", "{85D59394-C2FA-42D0-B04A-B1F8E244E619}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.Tests", "tests\DI.Tests\Microsoft.Extensions.DependencyInjection.Tests.csproj", "{85D59394-C2FA-42D0-B04A-B1F8E244E619}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{62FF9AD2-6603-4F9C-AB56-FCACEE3B6723}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TestUtilities", "..\Common\tests\TestUtilities\TestUtilities.csproj", "{62FF9AD2-6603-4F9C-AB56-FCACEE3B6723}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Extensions.DependencyInjection.ExternalContainers.Tests", "tests\DI.External.Tests\Microsoft.Extensions.DependencyInjection.ExternalContainers.Tests.csproj", "{A6803580-7984-42A3-A3F9-61F6DA7DD4A1}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -39,6 +41,10 @@ Global {62FF9AD2-6603-4F9C-AB56-FCACEE3B6723}.Debug|Any CPU.Build.0 = Debug|Any CPU {62FF9AD2-6603-4F9C-AB56-FCACEE3B6723}.Release|Any CPU.ActiveCfg = Release|Any CPU {62FF9AD2-6603-4F9C-AB56-FCACEE3B6723}.Release|Any CPU.Build.0 = Release|Any CPU + {A6803580-7984-42A3-A3F9-61F6DA7DD4A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A6803580-7984-42A3-A3F9-61F6DA7DD4A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A6803580-7984-42A3-A3F9-61F6DA7DD4A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A6803580-7984-42A3-A3F9-61F6DA7DD4A1}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -48,6 +54,7 @@ Global {0BD0C911-A456-40DA-B5B7-D51887777BF9} = {BF719C68-E106-47F7-AD0D-F996739BA266} {85D59394-C2FA-42D0-B04A-B1F8E244E619} = {615C17D7-1F66-4409-A982-73C31C517388} {62FF9AD2-6603-4F9C-AB56-FCACEE3B6723} = {615C17D7-1F66-4409-A982-73C31C517388} + {A6803580-7984-42A3-A3F9-61F6DA7DD4A1} = {615C17D7-1F66-4409-A982-73C31C517388} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {91C254CC-F9EE-4916-8A19-F816C708AE3C} diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Autofac.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Autofac.cs index 68fc0f2eaef3c1..0fe2b905263416 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Autofac.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/Autofac.cs @@ -8,9 +8,14 @@ namespace Microsoft.Extensions.DependencyInjection.Specification { - public class AutofacDependencyInjectionSpecificationTests: DependencyInjectionSpecificationTests + public class AutofacDependencyInjectionSpecificationTests : SkippableDependencyInjectionSpecificationTests { - protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection) + public override string[] SkippedTests => new[] + { + "PublicNoArgCtorConstrainedOpenGenericServicesCanBeResolved" + }; + + protected override IServiceProvider CreateServiceProviderImpl(IServiceCollection serviceCollection) { var builder = new ContainerBuilder(); builder.Populate(serviceCollection); diff --git a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StashBox.cs b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StashBox.cs index b9f469a528e5de..39e247395c1c5c 100644 --- a/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StashBox.cs +++ b/src/libraries/Microsoft.Extensions.DependencyInjection/tests/DI.External.Tests/StashBox.cs @@ -6,9 +6,16 @@ namespace Microsoft.Extensions.DependencyInjection.Specification { - public class StashBoxDependencyInjectionSpecificationTests: DependencyInjectionSpecificationTests + public class StashBoxDependencyInjectionSpecificationTests : SkippableDependencyInjectionSpecificationTests { - protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection) + public override string[] SkippedTests => new[] + { + "PublicNoArgCtorConstrainedOpenGenericServicesCanBeResolved", + "SelfReferencingConstrainedOpenGenericServicesCanBeResolved", + "ClassConstrainedOpenGenericServicesCanBeResolved" + }; + + protected override IServiceProvider CreateServiceProviderImpl(IServiceCollection serviceCollection) { return serviceCollection.UseStashbox(); }