aspnet / DependencyInjection

[Archived] Contains common DI abstractions that ASP.NET Core and Entity Framework Core use. Project moved to https://github.com/aspnet/Extensions

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Dirty/Duplicate registration of Microsoft.AspNetCore.DataProtection.RC1ForwardingActivator

opened this issue · comments

Hi

The service type Microsoft.AspNetCore.DataProtection.Internal.IActivator gets registered 3 times with Microsoft.AspNetCore.DataProtection.RC1ForwardingActivator when implementing a IServiceProviderFactory. Is this what you meant? I don't think registrations should be over subscribed. This is inefficient and over allocates resources for service types in a container. If you need a working example please see this implementation

Thanks

Gav

@pranavkm - I had a quick look and left a comment. I could not see any assertions guarding against duplicate registrations using TryAddSingleton for ServiceCollections. I also question why we are trying to register things, this tells me something is not right somewhere.

Trying to register stuff, it a code smell. You either register or not. If it is already registered, then surely an exception should be raised. This is not very efficient and it batters this container with rubbish. Fault tolerant registration is an absolute no no. You are dealing with types and you need to understand how they are being registered and consumed. I will be honest I really dont like this approach.

@fir3pho3nixx each framework component in AspNetCore registers all it's dependencies as part of registering it's components. This could result in overlap and duplicate registrations which is what TryAdd attempts to solve. The TryAdd pattern is primarily meant for AspNetCore's service registration and we don't anticipate application developers to use this for their services.

@pranavkm I am appealing to your common sense here. Each project that owns the service should be responsible for it's own registration.

Project Common -> IServiceCommon. <- The registration should be here

Project A -> ServiceA : IServiceCommon (Transient) <- Not here

Project B -> ServiceB : IServiceCommon (Singleton) <- Not here

Project C -> ServiceC : IServiceCommon (Scoped) <- And not here either.

You end up with clashing lifecycles and try to register logic which is inefficient. This is incredibly problematic. For example, Project A registers a transient but the consumer of IServiceCommon is a singleton. Your transient all of of the sudden becomes long lived. Is this what you intended? Perhaps not. What if your transient implements IDisposable? Does it get disposed inside the singleton?

This is simply wrong and it opens the gates of hell when it comes to managing resources and mitigating memory leaks. You should always be mindful that the thing that consumes your instance knows best how to use it and dispose of it. The reason I say this, is because you simply lose control beyond a certain point. There are design conundrums which would invalidate your lifecycle intent in certain scenarios.

Castle windsor solves this through IWindsorInstaller and registration by convention. In short the project that owns the interface can say find me all the implementations of IServiceCommon and I would like them to be transient, or singleton or whatever lifecycle I choose as Project Common. The use of factories/decorators and scopes for service location is just this projects way of dealing with this(which I think is very juvenile and hopefully will mature later on).

This does throw you firmly into a place where you have to think about how you manage your state. That unfortunately requires a conversation with the owner of Project Common.

Hope this makes sense. What is happening right here can be improved.

@davidfowl - I think I have summed up my problem with what is going on here and why I find scoping so peculiar. I thank you for your input earlier and hope my frustration did not offend you too much. My hope is that you find this insightful and it helps anyone else in the future.