dazinator / Dazinator.Extensions

Useful additions to Microsoft.Extensions.DependencyInjection such as Named Services.
Other
38 stars 1 forks source link

Child containers - Non inherited services #25

Closed dazinator closed 10 months ago

dazinator commented 3 years ago

When deriving a child container, The parent registrations are able to be resolved from the child container. Another way to phrase this is that: the child container inherits parent services.

However, suppose the parent container has IHostedServices registered. These are typically run as background services by the IHost. Suppose we then derive a child container for another IHost and start that running also. We end up with the same IHostedServices also being started by the new IHost so they are now running twice - once at the parent level and again at the child level.

It would be good to allow certain services to be optionally "not inherited" by the child container.

I believe this could be done by filtering out the service type from the parent service collection that is used to build the child container.

BuildChildServices(parentServices.Exclude<IHostedService>());

Also (todo: move to seperate issue) when thinking about this scenario of essentially creating another IHost from an existing child container / IServiceProvider - how would that work in practice? Typically new hosts use startup classes - but I could supply an extension method to configure one from an existing or newly derived child container?

dazinator commented 10 months ago

This issue raises its head with AddLogging()

When AddLogging() is called on a parent:

 services.AddLogging(builder =>
            {
                builder.AddProvider(loggerProvider);
                builder.AddXUnit(OutputHelper, (options) =>
                {
                    options.IncludeScopes = true;
                });
            });

AddLogging calls TryAdd<ILoggerFactory, LoggerFactory> - so an ILoggerFactory singleton exists at parent level.

Later when configuring the child container, if you call AddLogging() again, it won't add a duplicate registration because it has inherited the above service description from the parent.

I've made it so that when configuring the child container services, you can modify a copy of the parent services it will inherit, but even with this enable AddLogging() has an issue:

               childServices.AddLogging(a =>
                {                
                    a.ClearProviders(); // removes all ILoggerProvider but does not remove the ILoggerFactory asusmable this is overriding that reg

                    a.AddProvider(loggerProvider); // this provider is singleton instance we want to inherit from parent.

                    a.AddXUnit(OutputHelper, (options) =>
                    {
                        options.IncludeScopes = true;
                    });
                    a.SetMinimumLevel(LogLevel.Warning);

                });

When an ILogger<T> is resolved from the child container (using singleton delegation), it seems to be delegated to the parent container, rather than coming from the child container.

dazinator commented 10 months ago

Closing