TContainerBuilder CreateBuilder(Microsoft.Extensions.DependencyInjection.IServiceCollection services);
System.IServiceProvider CreateServiceProvider(TContainerBuilder containerBuilder);
}
+ public partial interface IServiceProviderIsService
+ {
+ bool IsService(System.Type serviceType);
+ }
public partial interface IServiceScope : System.IDisposable
{
System.IServiceProvider ServiceProvider { get; }
--- /dev/null
+// 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
+{
+ /// <summary>
+ /// Optional service used to determine if the specified type is available from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ public interface IServiceProviderIsService
+ {
+ /// <summary>
+ /// Determines if the specified service type is available from the <see cref="IServiceProvider"/>.
+ /// </summary>
+ /// <param name="serviceType">An object that specifies the type of service object to test.</param>
+ /// <returns>true if the specified service is a available, false if it is not.</returns>
+ bool IsService(Type serviceType);
+ }
+}
--- /dev/null
+// 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 System.Text;
+using Microsoft.Extensions.DependencyInjection.Specification.Fakes;
+using Xunit;
+
+namespace Microsoft.Extensions.DependencyInjection.Specification
+{
+ public abstract partial class DependencyInjectionSpecificationTests
+ {
+ public virtual bool SupportsIServiceProviderIsService => true;
+
+ [Fact]
+ public void ExplictServiceRegisterationWithIsService()
+ {
+ if (!SupportsIServiceProviderIsService)
+ {
+ return;
+ }
+
+ // Arrange
+ var collection = new TestServiceCollection();
+ collection.AddTransient(typeof(IFakeService), typeof(FakeService));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsService(typeof(IFakeService)));
+ Assert.False(serviceProviderIsService.IsService(typeof(FakeService)));
+ }
+
+ [Fact]
+ public void OpenGenericsWithIsService()
+ {
+ if (!SupportsIServiceProviderIsService)
+ {
+ return;
+ }
+
+ // Arrange
+ var collection = new TestServiceCollection();
+ collection.AddTransient(typeof(IFakeOpenGenericService<>), typeof(FakeOpenGenericService<>));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsService(typeof(IFakeOpenGenericService<IFakeService>)));
+ Assert.False(serviceProviderIsService.IsService(typeof(IFakeOpenGenericService<>)));
+ }
+
+ [Fact]
+ public void ClosedGenericsWithIsService()
+ {
+ if (!SupportsIServiceProviderIsService)
+ {
+ return;
+ }
+
+ // Arrange
+ var collection = new TestServiceCollection();
+ collection.AddTransient(typeof(IFakeOpenGenericService<IFakeService>), typeof(FakeOpenGenericService<IFakeService>));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsService(typeof(IFakeOpenGenericService<IFakeService>)));
+ }
+
+ [Fact]
+ public void IEnumerableWithIsServiceAlwaysReturnsTrue()
+ {
+ if (!SupportsIServiceProviderIsService)
+ {
+ return;
+ }
+
+ // Arrange
+ var collection = new TestServiceCollection();
+ collection.AddTransient(typeof(IFakeService), typeof(FakeService));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsService(typeof(IEnumerable<IFakeService>)));
+ Assert.True(serviceProviderIsService.IsService(typeof(IEnumerable<FakeService>)));
+ Assert.False(serviceProviderIsService.IsService(typeof(IEnumerable<>)));
+ }
+
+ [Fact]
+ public void BuiltInServicesWithIsServiceReturnsTrue()
+ {
+ if (!SupportsIServiceProviderIsService)
+ {
+ return;
+ }
+
+ // Arrange
+ var collection = new TestServiceCollection();
+ collection.AddTransient(typeof(IFakeService), typeof(FakeService));
+ var provider = CreateServiceProvider(collection);
+
+ // Act
+ var serviceProviderIsService = provider.GetService<IServiceProviderIsService>();
+
+ // Assert
+ Assert.NotNull(serviceProviderIsService);
+ Assert.True(serviceProviderIsService.IsService(typeof(IServiceProvider)));
+ Assert.True(serviceProviderIsService.IsService(typeof(IServiceScopeFactory)));
+ Assert.True(serviceProviderIsService.IsService(typeof(IServiceProviderIsService)));
+ }
+ }
+}
namespace Microsoft.Extensions.DependencyInjection.ServiceLookup
{
- internal sealed class CallSiteFactory
+ internal sealed class CallSiteFactory : IServiceProviderIsService
{
private const int DefaultSlot = 0;
private readonly ServiceDescriptor[] _descriptors;
_callSiteCache[new ServiceCacheKey(type, DefaultSlot)] = serviceCallSite;
}
+ public bool IsService(Type serviceType)
+ {
+ if (serviceType is null)
+ {
+ throw new ArgumentNullException(nameof(serviceType));
+ }
+
+ // Querying for an open generic should return false (they aren't resolvable)
+ if (serviceType.IsGenericTypeDefinition)
+ {
+ return false;
+ }
+
+ if (_descriptorLookup.ContainsKey(serviceType))
+ {
+ return true;
+ }
+
+ if (serviceType.IsConstructedGenericType && serviceType.GetGenericTypeDefinition() is Type genericDefinition)
+ {
+ // We special case IEnumerable since it isn't explicitly registered in the container
+ // yet we can manifest instances of it when requested.
+ return genericDefinition == typeof(IEnumerable<>) || _descriptorLookup.ContainsKey(genericDefinition);
+ }
+
+ // These are the built in service types that aren't part of the list of service descriptors
+ // If you update these make sure to also update the code in ServiceProvider.ctor
+ return serviceType == typeof(IServiceProvider) ||
+ serviceType == typeof(IServiceScopeFactory) ||
+ serviceType == typeof(IServiceProviderIsService);
+ }
+
private struct ServiceDescriptorCacheItem
{
private ServiceDescriptor _item;
Root = new ServiceProviderEngineScope(this);
CallSiteFactory = new CallSiteFactory(serviceDescriptors);
+ // The list of built in services that aren't part of the list of service descriptors
+ // keep this in sync with CallSiteFactory.IsService
CallSiteFactory.Add(typeof(IServiceProvider), new ServiceProviderCallSite());
CallSiteFactory.Add(typeof(IServiceScopeFactory), new ServiceScopeFactoryCallSite(Root));
+ CallSiteFactory.Add(typeof(IServiceProviderIsService), new ConstantCallSite(typeof(IServiceProviderIsService), CallSiteFactory));
if (options.ValidateScopes)
{
Func<ServiceProviderEngineScope, object> realizedService = _realizedServices.GetOrAdd(serviceType, _createServiceAccessor);
OnResolve(serviceType, serviceProviderEngineScope);
DependencyInjectionEventSource.Log.ServiceResolved(serviceType);
- return realizedService.Invoke(serviceProviderEngineScope);
+ var result = realizedService.Invoke(serviceProviderEngineScope);
+ System.Diagnostics.Debug.Assert(result is null || CallSiteFactory.IsService(serviceType));
+ return result;
}
private void ValidateService(ServiceDescriptor descriptor)
{
public class AutofacDependencyInjectionSpecificationTests : DependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
var builder = new ContainerBuilder();
namespace Microsoft.Extensions.DependencyInjection.Specification
{
- public class DryIocDependencyInjectionSpecificationTests: DependencyInjectionSpecificationTests
+ public class DryIocDependencyInjectionSpecificationTests : DependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
return new Container()
{
public class GraceDependencyInjectionSpecificationTests: SkippableDependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
public override string[] SkippedTests => new[]
{
"ResolvesMixedOpenClosedGenericsAsEnumerable",
{
public class LamarDependencyInjectionSpecificationTests : SkippableDependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
public override string[] SkippedTests => new[]
{
"DisposesInReverseOrderOfCreation",
{
public class LightInjectDependencyInjectionSpecificationTests: DependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
var builder = new ContainerBuilder();
{
public class StashBoxDependencyInjectionSpecificationTests : DependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
protected override IServiceProvider CreateServiceProvider(IServiceCollection serviceCollection)
{
return serviceCollection.UseStashbox();
{
public class StructureMapDependencyInjectionSpecificationTests: SkippableDependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
public override string[] SkippedTests => new[]
{
"DisposesInReverseOrderOfCreation",
{
public class UnityDependencyInjectionSpecificationTests: SkippableDependencyInjectionSpecificationTests
{
+ public override bool SupportsIServiceProviderIsService => false;
+
// See https://github.com/unitycontainer/microsoft-dependency-injection/issues/87
public override bool ExpectStructWithPublicDefaultConstructorInvoked => true;