dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.27k stars 4.73k forks source link

[API Proposal]: Add `Type` parameter overload to Microsoft.Extensions.DependencyInjection resolver delegate method #102970

Open thomhurst opened 5 months ago

thomhurst commented 5 months ago

Background and motivation

If we register an open generic within the Microsoft Dependency Injection library, we have to also register the implementation as on open-generic type too: e.g.: services.AddSingleton(typeof(ILogger<>), typeof(MyLogger<>));

If we try and new up an object using the Func<> overload, we get an exception. E.g: services.AddSingleton(typeof(ILogger<>), sp => new MyLogger(stringBuilder));

Open generic service type 'Microsoft.Extensions.Logging.ILogger`1[TCategoryName]' requires registering an open generic implementation type. (Parameter 'descriptors')

This means we're limited by how this object is created. We can't pass custom arguments to the constructor.

I propose an overload where the Func<> also passes the Type information to the delegate, therefore we could interrogate it and use that to create objects.

This allows for greater control over objects and more flexibility within the dependency injection setup of an application.

API Proposal

public ServiceDescriptor(
    Type serviceType,
    Func<IServiceProvider, Type, object> factory,
    ServiceLifetime lifetime)
    : this(serviceType, serviceKey: null, lifetime)
{
    // Internal Microsoft DI code
}

public static IServiceCollection AddSingleton(
    this IServiceCollection services,
    Type serviceType,
    Func<IServiceProvider, Type, object> implementationFactory)
{
    // Internal Microsoft DI code
}

public static IServiceCollection AddScoped(
    this IServiceCollection services,
    Type serviceType,
    Func<IServiceProvider, Type, object> implementationFactory)
{
    // Internal Microsoft DI code
}

public static IServiceCollection AddTransient(
    this IServiceCollection services,
    Type serviceType,
    Func<IServiceProvider, Type, object> implementationFactory)
{
    // Internal Microsoft DI code
}

API Usage

var stringBuilder = new StringBuilder();

services.AddSingleton(typeof(ILogger<>), (sp, type) => 
{
    // When requested `ILogger<>` is `ILogger<MyClass>` then `type` will be `typeof(ILogger<MyClass>)`
    var genericLoggerType = typeof(MyLogger<>).MakeGenericType(type.GenericTypeArguments[0]);
    return ActivatorUtilities.CreateInstance(sp, genericLoggerType, stringBuilder)
});

Alternative Designs

No response

Risks

No response

dotnet-policy-service[bot] commented 5 months ago

Tagging subscribers to this area: @dotnet/area-extensions-dependencyinjection See info in area-owners.md if you want to be subscribed.

KalleOlaviNiemitalo commented 4 months ago

https://github.com/dotnet/runtime/issues/41050 is similar.

steveharter commented 3 months ago

Moving to Future since we have reached the v9 "feature complete" milestone; API suggestion also needs to be finalized and marked ready-for-review.

Also verify whether this is a full or partial duplicate of https://github.com/dotnet/runtime/issues/41050.

wvpm commented 3 months ago

It is a duplicate of #41050 Both the problem and the solution are the same.