dotnet / runtime

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

Generic TryAddEnumerable method(s) #36403

Open stevejgordon opened 5 years ago

stevejgordon commented 5 years ago

Is your feature request related to a problem? Please describe.

Currently, many generic extension methods exist on the IServiceCollection to support the registration of services. One that seems to be missing is a TryAddEnumerable extension which supports generics. The remarks for TryAddEnumerable recommend it for situations where multiple implementations are being registered. Currently, when using TryAddEnumerable it's one the few cases I've found where the user must also understand how to directly create a ServiceDescriptor.

Describe the solution you'd like

Something along the lines of this would be sufficient as a minimum, although perhaps other overloads would be useful to support the different lifetimes...

public static void TryAddEnumerable<TService, TImplementation>(this IServiceCollection collection, ServiceLifetime serviceLifetime)
    where TService : class
    where TImplementation : class, TService
{
    var serviceDescriptor =
        ServiceDescriptor.Describe(typeof(TService), typeof(TImplementation), serviceLifetime);

    collection.TryAddEnumerable(serviceDescriptor);
}

If this looks reasonable I'd be happy to submit a PR for this and any other extension methods that would be helpful.

Describe alternatives you've considered

N/A

Additional context

N/A

rynowak commented 5 years ago

@stevejgordon do you feel like this is common enough to worth adding? We considered this at a much earlier point and left it out because we felt like only commonly needed to do this... but that might have been wrong. Do you have examples of where you've needed this?

stevejgordon commented 5 years ago

Hi @rynowak. I'd definitely expect that it's less common but we have used the pattern of registering multiple implementations of a service type in at least 3 microservices in the last year or so. The typical case for us is in data processing services where we're applying either rules or a conditional set of data enrichment services over the messages being processed. These are typically .NET Core generic host based queue readers.

For example, we have something like an IDataEnricher interface defining two methods Task Enrich(Message m) and Task<bool> CanEnrich(Message m). We then register in our case around 14 implementations this and have a message enricher accepting the IEnumerable that envokes the Enrich method against the message for all those that can enrich it.

For our first services, I was completely unaware of the TryAddEnumerable method and we just used the regular Add extensions. We switched to TryAddEnumerable after I discovered it and saw it remarks. I've got a small internal package where we have a couple of extensions to make the use by our teams more consistent with the other generic methods.

I'm also now considering using the scanning feature in Scrutor by @khellang.

I don't know if we're a bit of an exception in that use, but it would be great even in our small case to have at least this one generic method.

I'm including a section on this pattern and registering with TryAddEnumerable in my Pluralisght course too. In that demo, I'm showing the concept of having an IRule interface defined for a booking system. There can then be multiple rule implementations which are registered and all of which are evaluated when a booking attempt is made. This is in an ASP.NET Core web app scenario.