dotnet / runtime

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

[API Proposal]: Source generator to replace some use cases of ActivatorUtilities.CreateFactory #85361

Open TechnoBerry opened 1 year ago

TechnoBerry commented 1 year ago

Background and motivation

In most of the cases in which I had to use ActivatorUtilities.CreateFactory, all the passed types were known. It seems to me that the method generation using expression trees used under the hood of the ActivatorUtilities.CreateFactory method is redundant in my case and can be replaced with a method created using source generator. I also saw that the AddHttpClient method from Microsoft.Extensions.Http has an overload, in combination with which the source generator will look appropriate.

API Proposal

I suggest adding an attribute (e.g. CreateInstanceAttribute) to mark methods for which source should be generated and a source generator that will generate an implementation of the factory method using the passed parameters and dependencies obtained from IServiceProvider

namespace Microsoft.Extensions.DependencyInjection;

[AttributeUsage(AttributeTargets.Method)]
public class CreateInstanceAttribute : Attribute
{
}

API Usage

Below is an example of using with AddHttpClient.

public interface IMyClient {}

public class MyClient : IMyClient
{
    private readonly IMemoryCache _memoryCache;
    private readonly HttpClient _httpClient;
    private readonly ILogger<MyClient> _logger;

    public MyClient(IMemoryCache memoryCache, HttpClient httpClient, ILogger<MyClient> logger)
    {
        _memoryCache = memoryCache;
        _httpClient = httpClient;
        _logger = logger;
    }
}

public static partial class MyClientFactory
{
    // The generated implementation of this method should get IMemoryCache and ILogger<MyClient> from serviceProvider
    // and pass them along with given httpClient as constructor parameters
    [CreateInstance]
    public static partial MyClient Create(HttpClient httpClient, IServiceProvider serviceProvider);

    public static void AddMyClient(this IServiceCollection services)
    {
        services.AddHttpClient<IMyClient, MyClient>(Create); // Uses generated method
    }
}

Alternative Designs

No response

Risks

No response

ghost commented 1 year ago

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

Issue Details
### Background and motivation In most of the cases in which I had to use `ActivatorUtilities.CreateFactory`, all the passed types were known. It seems to me that the method generation using expression trees used under the hood of the `ActivatorUtilities.CreateFactory` method is redundant in my case and can be replaced with a method created using source generator. I also saw that the `AddHttpClient` method from `Microsoft.Extensions.Http` [has an overload](https://github.com/dotnet/runtime/blob/v7.0.5/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientFactoryServiceCollectionExtensions.cs#L716-L729), in combination with which the source generator will look appropriate. ### API Proposal I suggest adding an attribute (e.g. `CreateInstanceAttribute`) to mark methods for which source should be generatoed and a source generator that will generate an implementation of the factory method using the passed parameters and dependencies obtained from IServiceProvider ```csharp namespace Microsoft.Extensions.DependencyInjection; [AttributeUsage(AttributeTargets.Method)] public class CreateInstanceAttribute : Attribute { } ``` ### API Usage Below is an example of using with AddHttpClient. ```csharp public interface IMyClient {} public class MyClient : IMyClient { private readonly IMemoryCache _memoryCache; private readonly HttpClient _httpClient; private readonly ILogger _logger; public MyClient(IMemoryCache memoryCache, HttpClient httpClient, ILogger logger) { _memoryCache = memoryCache; _httpClient = httpClient; _logger = logger; } } public static partial class MyClientFactory { // The generated implementation of this method should get IMemoryCache and ILogger from serviceProvider // and pass them along with given httpClient as constructor parameters [CreateInstance] public static partial MyClient Create(HttpClient httpClient, IServiceProvider serviceProvider); public static void AddMyClient(this IServiceCollection services) { services.AddHttpClient(Create); // Uses generated method } } ``` ### Alternative Designs _No response_ ### Risks _No response_
Author: AMalininHere
Assignees: -
Labels: `api-suggestion`, `area-Extensions-HttpClientFactory`
Milestone: -
ghost commented 1 year ago

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

Issue Details
### Background and motivation In most of the cases in which I had to use `ActivatorUtilities.CreateFactory`, all the passed types were known. It seems to me that the method generation using expression trees used under the hood of the `ActivatorUtilities.CreateFactory` method is redundant in my case and can be replaced with a method created using source generator. I also saw that the `AddHttpClient` method from `Microsoft.Extensions.Http` [has an overload](https://github.com/dotnet/runtime/blob/v7.0.5/src/libraries/Microsoft.Extensions.Http/src/DependencyInjection/HttpClientFactoryServiceCollectionExtensions.cs#L716-L729), in combination with which the source generator will look appropriate. ### API Proposal I suggest adding an attribute (e.g. `CreateInstanceAttribute`) to mark methods for which source should be generated and a source generator that will generate an implementation of the factory method using the passed parameters and dependencies obtained from IServiceProvider ```csharp namespace Microsoft.Extensions.DependencyInjection; [AttributeUsage(AttributeTargets.Method)] public class CreateInstanceAttribute : Attribute { } ``` ### API Usage Below is an example of using with AddHttpClient. ```csharp public interface IMyClient {} public class MyClient : IMyClient { private readonly IMemoryCache _memoryCache; private readonly HttpClient _httpClient; private readonly ILogger _logger; public MyClient(IMemoryCache memoryCache, HttpClient httpClient, ILogger logger) { _memoryCache = memoryCache; _httpClient = httpClient; _logger = logger; } } public static partial class MyClientFactory { // The generated implementation of this method should get IMemoryCache and ILogger from serviceProvider // and pass them along with given httpClient as constructor parameters [CreateInstance] public static partial MyClient Create(HttpClient httpClient, IServiceProvider serviceProvider); public static void AddMyClient(this IServiceCollection services) { services.AddHttpClient(Create); // Uses generated method } } ``` ### Alternative Designs _No response_ ### Risks _No response_
Author: AMalininHere
Assignees: -
Labels: `api-suggestion`, `untriaged`, `area-Extensions-DependencyInjection`
Milestone: -
steveharter commented 1 year ago

See also https://github.com/dotnet/runtime/issues/44432 https://github.com/dotnet/runtime/issues/82679