Skip to content

KeyedService.AnyKey is not usable with ServiceKeyAttribute on object-typed arguments due to type validation #93438

Closed
@DillonN

Description

@DillonN

Description

I'm trying to use the new Keyed services feature, but it looks like the KeyedService.AnyKey piece is not functional when used in conjunction with ServiceKeyAttribute?

E: Issue only occurs when trying to use a base class for the ServiceKeyAttribute argument, e.g. in the case below where object is the argument.

Whenever I try, all combination of types leads to an exception:

System.InvalidOperationException: 'The type of the key used for lookup doesn't match the type in the constructor parameter with the ServiceKey attribute.'

This appears to be due to a check that requires the parameter's type to be the same as the one on the registration:

if (parameterType != serviceIdentifier.ServiceKey.GetType())
{
throw new InvalidOperationException(SR.InvalidServiceKeyType);
}

However, this is impossible because KeyedService.AnyKey uses a dedicated private type and is only exposed as object.

I feel like I'm missing something here, but how does KeyedService.AnyKey work with that strict type check in place?

Reproduction Steps

Create a new console app under .NET 8 RC2, add the Microsoft.Extensions.DependencyInjection package, and paste this into Program.cs:

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddKeyedTransient<Service>(KeyedService.AnyKey);

// This line throws
// System.InvalidOperationException: 'The type of the key used for lookup doesn't match the type in the constructor parameter with the ServiceKey attribute.'
using var provider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });

// If ValidateOnBuild is false, this line also throws
// System.InvalidOperationException: 'The type of the key used for lookup doesn't match the type in the constructor parameter with the ServiceKey attribute.'
var service = provider.GetKeyedService<Service>("test");

public class Service
{
    public Service([ServiceKey] object serviceKey)
    { }
}

Expected behavior

  • When using KeyedService.AnyKey for a service that uses the [ServiceKey] attribute, building the service provider should succeed without validation failures
  • When using KeyedService.AnyKey for a service that uses the [ServiceKey] attribute, resolving the service using any key input should return the registered implementation and fill in the service key value for the marked parameter

Actual behavior

In both of the above points, a System.InvalidOperationException is thrown instead

Regression?

No response

Known Workarounds

I can workaround by using a factory service registration instead:

using Microsoft.Extensions.DependencyInjection;

var services = new ServiceCollection();
services.AddKeyedTransient<Service>(KeyedService.AnyKey, (_, key) => new Service(key));

// Validation succeeds now
using var provider = services.BuildServiceProvider(new ServiceProviderOptions { ValidateOnBuild = true });

// Resolving service also succeeds now
var service = provider.GetKeyedService<Service>("test");

public class Service
{
    public Service(object serviceKey)
    { }
}

Configuration

.NET SDK:
 Version:   8.0.100-rc.2.23502.2
 Commit:    0abacfc2b6

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.23550
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\8.0.100-rc.2.23502.2\

.NET workloads installed:
<removed for brevity>

Host:
  Version:      8.0.0-rc.2.23479.6
  Architecture: x64
  Commit:       0b25e38ad3

.NET SDKs installed:
  8.0.100-rc.2.23502.2 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
<removed for brevity>
  Microsoft.NETCore.App 8.0.0-rc.2.23479.6 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
<removed for brevity>

Other architectures found:
  x86   [C:\Program Files (x86)\dotnet]
    registered at [HKLM\SOFTWARE\dotnet\Setup\InstalledVersions\x86\InstallLocation]

Environment variables:
  Not set

global.json file:
  Not found

Learn more:
  https://aka.ms/dotnet/info

Download .NET:
  https://aka.ms/dotnet/download

Other information

No response

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Relationships

None yet

Development

No branches or pull requests

Issue actions