Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -613,14 +613,22 @@ private ConstructorCallSite CreateConstructorCallSite(
{
if (serviceIdentifier.ServiceKey != null && attribute is ServiceKeyAttribute)
{
// Check if the parameter type matches
if (parameterType != serviceIdentifier.ServiceKey.GetType())
// Even though the parameter may be strongly typed, support 'object' if AnyKey is used.

if (serviceIdentifier.ServiceKey == KeyedService.AnyKey)
{
parameterType = typeof(object);
}
else if (parameterType != serviceIdentifier.ServiceKey.GetType()
&& parameterType != typeof(object))
{
throw new InvalidOperationException(SR.InvalidServiceKeyType);
}

callSite = new ConstantCallSite(parameterType, serviceIdentifier.ServiceKey);
break;
}

if (attribute is FromKeyedServicesAttribute keyed)
{
var parameterSvcId = new ServiceIdentifier(keyed.Key, parameterType);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,131 @@ public void GetService_DoesNotThrow_WhenGetServiceForPolymorphicServiceIsCalledO
Assert.IsType<Bar3>(actual);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void BuildServiceProvider_AnyKey_ServiceKeyWithStronglyTypedArgument(bool validateOnBuild)
{
// Arrange
var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedTransient<ServiceKeyWithStronglyTypedArgument>(KeyedService.AnyKey);
using var serviceProvider = serviceCollection.BuildServiceProvider(new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = validateOnBuild
});

// Act
var actual = serviceProvider.GetKeyedService<ServiceKeyWithStronglyTypedArgument>(42);

// Assert
Assert.Equal(42, actual.Key);
Assert.Throws<InvalidOperationException>(() => serviceProvider.GetKeyedService<ServiceKeyWithStronglyTypedArgument>("Hello"));
}

private class ServiceKeyWithStronglyTypedArgument
{
public int Key { get; set; }

public ServiceKeyWithStronglyTypedArgument([ServiceKey] int key)
{
Key = key;
}
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void BuildServiceProvider_AnyKey_ServiceKeyWithObjectTypedArgument(bool validateOnBuild)
{
// Arrange
var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedTransient<ServiceKeyWithObjectTypedArgument>(KeyedService.AnyKey);
using var serviceProvider = serviceCollection.BuildServiceProvider(new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = validateOnBuild
});

// Act
var actualInt = serviceProvider.GetKeyedService<ServiceKeyWithObjectTypedArgument>(42);
var actualString = serviceProvider.GetKeyedService<ServiceKeyWithObjectTypedArgument>("hello");

// Assert
Assert.Equal(42, actualInt.Key);
Assert.Equal("hello", actualString.Key);
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void BuildServiceProvider_ServiceKeyWithObjectTypedArgument(bool validateOnBuild)
{
// Arrange
var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedTransient<ServiceKeyWithObjectTypedArgument>(42);
serviceCollection.AddKeyedTransient<ServiceKeyWithObjectTypedArgument>("hello");
using var serviceProvider = serviceCollection.BuildServiceProvider(new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = validateOnBuild
});

// Act
var actualInt = serviceProvider.GetKeyedService<ServiceKeyWithObjectTypedArgument>(42);
var actualString = serviceProvider.GetKeyedService<ServiceKeyWithObjectTypedArgument>("hello");
var notFound = serviceProvider.GetKeyedService<ServiceKeyWithObjectTypedArgument>(false);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GetKeyedService

what would be the behavior when passing null as a parameter? will GetKeyedService return null? or will return ServiceKeyWithObjectTypedArgument object with Key = null?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Passing null means that this is not a "keyed service" so it just uses the type which is ServiceKeyWithObjectTypedArgument and since that was not registered, it returns null.

These checks are really just verifying that [ServiceKey] object key is called with the correct key and no unexpected exceptions occur.


// Assert
Assert.Equal(42, actualInt.Key);
Assert.Equal("hello", actualString.Key);
Assert.Null(notFound);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Assert.Null(notFound);

Just curious why GetKeyedService return null when using boolean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bool is not registered with AddKeyedTransient(); only int and string are.

}

private class ServiceKeyWithObjectTypedArgument
{
public object Key { get; set; }

public ServiceKeyWithObjectTypedArgument([ServiceKey] object key)
{
Key = key;
}
}

[Theory]
[InlineData(true)]
[InlineData(false)]
public void BuildServiceProvider_AnyKey_ServiceKeyWithObjectAndIntTypedArguments(bool validateOnBuild)
{
// Arrange
var serviceCollection = new ServiceCollection();
serviceCollection.AddKeyedTransient<ServiceKeyWithObjectAndIntTypedArguments>(KeyedService.AnyKey);
using var serviceProvider = serviceCollection.BuildServiceProvider(new ServiceProviderOptions
{
ValidateScopes = true,
ValidateOnBuild = validateOnBuild
});

// Act
var actual = serviceProvider.GetKeyedService<ServiceKeyWithObjectAndIntTypedArguments>(42);

// Assert
Assert.Equal(42, actual.Key);
Assert.Equal(42, actual.IntKey);
}

private class ServiceKeyWithObjectAndIntTypedArguments
{
public object Key { get; set; }
public int IntKey { get; set; }

public ServiceKeyWithObjectAndIntTypedArguments([ServiceKey] object key, [ServiceKey] int intKey)
{
Key = key;
IntKey = intKey;
}
}

[Fact]
public void ScopeValidation_ShouldBeAbleToDistingushGenericCollections_WhenGetServiceIsCalledOnRoot()
{
Expand Down
Loading