Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Transient registrations in ServiceCollection are mapped to PerRequestLifetime #13

Closed
sashaozz opened this issue May 10, 2017 · 3 comments

Comments

@sashaozz
Copy link

It seems that transient registrations in ServiceCollection are mapped to PerRequestLifetime. So they will not be resolved without scope. But it gives different semantics to the lifetime of registered service because they are not truly transient anymore. I am building some reusable library for asp.net core applications so i am sticked to rather general approach here - use IServiceCollection extensions of form "services.UseSomeLibrary()". My design requires transient service to be resolved in background, outside of any scope like HttpRequest so they are registered with transient lifetime. When i use this library with LightInject container in the same app then my library is broken because transient background services are not resolved due to absence of scope. Is not it better to map transient lifetime of ServiceCollection to real transient registrations in LightInject?

@seesharper
Copy link
Owner

seesharper commented May 10, 2017

The reason that we map transient to the PerRequestLifetime is solely because of the specification tests that each adapter needs to pass.
https://github.com/aspnet/DependencyInjection/blob/dev/src/Microsoft.Extensions.DependencyInjection.Specification.Tests/DependencyInjectionSpecificationTests.cs

The default implementation (Microsoft.Extensions.DependencyInjection) operates with what they call the "root scope" that basically means that a scope is started when the container is created. Transients requested outside an explicit scope will therefore be released when the container is released. I guess the problem is what to do with disposable transients and when they should be disposed. We could however do a check for this by looking at the service type and the implementing type if available (could be a factory expression where the implementing type is unknown) and only register with the PerRequestLifetime if the service appears to be disposable.

btw, this library you are talking about, is it here on GitHub?

@sashaozz
Copy link
Author

Thank you for explanation, Bernhard it really helped.
Actually my problem was in the little different place. I am using my library and LightInject container in the classic Asp.Net app (not .Net Core) so i used LightInject.Web package which adds PerWebRequest scope manager. So when i was resolving IServiceCollection transient services (which are PerRequestLifetime for LightInject) on background thread PerWebRequestScopeManager was bumped with NullReference exception because HttpContext.Current was null (which is normal, because it is background thread). Still it is related to this issue.
It seems that your adapter package tries to open Scope (see https://github.com/seesharper/LightInject.Microsoft.DependencyInjection/blob/master/src/LightInject.Microsoft.DependencyInjection/LightInject.Microsoft.DependencyInjection.cs#L76) when it creates IServiceProvider to be aligned with what Microsoft.Extensions.DependencyInjection expects. But at this point container will use PerThreadScopeManager (or PerWebRequestScopeManager in my case) so created scope will be bound
to current thread (or current http request in my case). It means that if you will try to resolve transient (in the terms of Microsoft.Extensions.DependencyInjection) on background thread it will fail as no opened scope will exist on this thread. It seems that difference between Microsoft.Extensions.DependencyInjection and your adapter is in that their "root scope" is global but your adapter starts thread bound scope.
If my observations are correct then I believe mapping Transient services to PerRequestLifetime are totally reasonable. The only thing adapter should do is to ensure that "global" scope is opened when IServiceProvider is created. It seems that it possible to do by wrapping current ScopeManagerProvider of container to the new one which will provide wrapped ScopeManager which will ensure global scope exists.
What do you think?

@seesharper
Copy link
Owner

Fixed by #23

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants