I'm using .net core 3.1 with this configuration :
public interface IFoo
{
public void Work();
}
public class Foo : IFoo
{
readonly string MyGuid;
public Foo()
{
MyGuid = Guid.NewGuid().ToString();
}
public void Work() { Console.WriteLine(MyGuid); }
}
And this is the configuration :
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IFoo, Foo>();
...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IFoo foo, IServiceProvider myServiceProvider)
{
Console.WriteLine("Via ApplicationServices");
app.ApplicationServices.GetService<IFoo>().Work();
Console.WriteLine("Via IServiceProvider");
myServiceProvider.GetService<IFoo>().Work();
Console.WriteLine("Via IFoo injection");
foo.Work();
}
Result are :
Via ApplicationServices 27e61428-2adf-4ffa-b27a-485b9c45471d <----different
Via IServiceProvider c9e86865-2eeb-44db-b625-312f92533beb
Via IFoo injection c9e86865-2eeb-44db-b625-312f92533beb
More , in the controller action , If I use IserviceProvider :
[HttpGet]
public IActionResult Get([FromServices] IServiceProvider serviceProvider)
{
serviceProvider.GetService<IFoo>().Work();
}
I see another different Guid : 45d95a9d-4169-40a0-9eae-e29e85a3cc19.
Question:
Why does the IServiceProvider and Injection of IFoo in the Configure method yield the same Guid , while the controller action and app.ApplicationServices.GetService yield different ones?
There are 4 different guids in this example
It's a scoped service. It supposed to be the same Guid.
TL;DR;
IFoo foo is resolved using myServiceProvider.GetService<IFoo>() before both get passed into Configure method. So you're resolving same IFoo instance from the same myServiceProvider instance for the 2nd time.
app.ApplicationServices is special root service provider of the application. Parent of myServiceProvider. Thus it resolves different IFoo.
**Default behavior of app.ApplicationServices should be to throw exception when you try to resolve scoped service.
LONGER EXPLANATION
If you have three dummy classes:
class Singleton { }
class Scoped { }
class Transient { }
And you register them as such in IServiceConfiguration:
public static void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<Singleton>();
services.AddScoped<Scoped>();
services.AddTransient<Transient>();
}
Now, you create your "root" IServiceProvider from IServiceCollection - in command-line app, it would look like this:
ServiceCollection sc = new ServiceCollection();
ConfigureServices(sc);
ServiceProvider root = sc.BuildServiceProvider();
ROOT SERVICE PROVIDER BEHAVIOR (equivalent to app.ApplicationServices):
If you now test root:
root.GetService<Singleton>(); - every time it's called returns same object instance.
root.GetService<Scoped>(); - every time it's called returns same object instance.
root.GetService<Transient>(); every time it's called returns new object instance.
CHILD-SCOPE SERVICE PROVIDER BEHAVIOR (eg: IServiceProvider in Configure method):
If you now create child scope and use it's own IServiceProvider:
IServiceScope scope1 = root.CreateScope();
IServiceProvider sp1 = scope1.ServiceProvider;
sp1.GetService<Singleton>(); - every time it's called returns same object instance whichroot.GetService<Singleton>(); returns. Singleton is the same instance no matter from which scope you call it. It is resolved climbing the hierarchy of scopes back to the root service provider (scopeless one).
sp1.GetService<Scoped>(); - every time it's called returns same object instance, but not the same instance that root returns. Object instance is cached on the current scope. Every scope creates/caches it's own scoped instance.
sp1.GetService<Transient>(); every time it's called returns new object instance, same behavior like for the root.
root scope is "special" only because it has no parent scope, so resolving scoped or singleton service from the root technically does the same thing - object instance returned is cached in the root itself.
This also explains why you cannot resolve service from IServiceCollection directly. IServiceCollection does not have hierarchy of scopes and caching infrastructure which IServiceProvider has. It just contains list of ServiceDescriptor. In addition, it would be unclear in which scope service instance should be cached.
ASP.NET Core
For ASP.NET Core root IServiceProvider is app.ApplicationServices. Configure method receives first child-scope created from the root - application-scope. For every HTTP request application-scope creates child-scope which is used to resolve all services and is itself injected in controllers and views of that HTTP request. It is also used to inject all other types in a controller constructor or a view.
IFoo resolution
So, your foo from Configure method is resolved using myServiceProvider, then they both get used as input parameters for Configure. Framework does something like this:
ServiceProvider root = sc.BuildServiceProvider(validateScopes: true);
var appScope = root.CreateScope();
IFoo foo = appScope.ServiceProvider.GetService<IFoo>();
ConfigureServices(foo, appScope.ServiceProvider);
When you call sp.GetService<IFoo>() inside of Configure method it is identical to appScope.ServiceProvider.GetService<IFoo>(); that was already called from the outside. root.GetService<IFoo>() creates different IFoo instance, as it should.
More ASP.NET Core:
To prevent developers making mistake trying to resolve scoped service from the app.ApplicationServices and not realizing that it is application scoped (global), instead of being scoped to HTTP request, by default, ASP.NET Core creates root ServiceProvider using BuildServiceProvider overload:
ServiceProvider root = sc.BuildServiceProvider(validateScopes: true);
validateScopes: true to perform check verifying that scoped services never gets resolved from root provider; otherwise false.
However, this might depend on compatibility mode you're using. I'm guessing that's the reason why it allows you to resolve scoped service via app.ApplicationServices.
Update answer:
For being able to provide you with the same instance, the DI engine will need to know the current scope.
The Configure method in the Startup class is called initially by the ASP.NET Core engine, not by a request from the user which is how a scoped service is meant to be used.
Now if you try this example instead:
IServiceScope scope = app.ApplicationServices.CreateScope();
scope.ServiceProvider.GetService<IFoo>().Work();
As long as you use the same ServiceProvider from the same IServiceScope instance, you will have access to the same IFoo object and will get the same GUID.
Old answer:
A scoped service is meant to be living for a short period (scope) and get removed when they are needed anymore.
From Dependency injection in .NET
a scoped lifetime indicates that services are created once per client request (connection)
furthermore,
Do not resolve a scoped service from a singleton and be careful not to do so indirectly, for example, through a transient service.
app.ApplicationServices.GetService will try to provide you a Singleton which is not what the service is registered as.
What you see is the expected behavior. If you need the service to have the same instance for the whole period of your application's lifetime, you will have to register it as a Singleton.
Read more about the singleton lifetime and change your code from services.AddScoped<IFoo, Foo>() to services.AddSingleton<IFoo, Foo>() if you need the same value everywhere.
The IApplicationBuilder "app" is created and passed to the Configure method.
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/startup?view=aspnetcore-5.0#the-configure-method
IApplicationBuilder is available to the Configure method, but it isn't registered in the service container. Hosting creates an IApplicationBuilder and passes it directly to Configure.
On the other hand the IServiceProvider "myServiceProvider" is actually activated/created and injected:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-5.0#services-injected-into-startup
Services can be injected into the Startup constructor and the Startup.Configure method.
Any service registered with the DI container can be injected into the Startup.Configure method
So we are in two different scopes, so the guid, as expected, is different.
Via ApplicationServices 27e61428-2adf-4ffa-b27a-485b9c45471d <---- created in scope "A"
Via IServiceProvider c9e86865-2eeb-44db-b625-312f92533beb <-- created in scope "B"
Via IFoo injection c9e86865-2eeb-44db-b625-312f92533beb < -- created in scope "B"
When you are in your controller each request will have it's brand new scope, a different scope than the ones discussed above:
https://learn.microsoft.com/en-us/dotnet/core/extensions/dependency-injection#scoped
It seems to me that you expected that also the guid in the controller to be the same, you should be aware that for each request an instance of your controller is created and a DI chain starts in a fresh scope for each request.
Related
In an ASP.NET Core 6 app, adding IServiceProvider to the service collection in this way allows injection of a scoped provider:
services.AddScoped(sp => sp);
But is it the scoped provider for the current request? In other words, is it guaranteed to provide the same instances of scoped dependencies that may have been injected elsewhere in the request?
XY: Method boundary callbacks are added to the service collection at startup. I'm injecting a service provider rather than the callbacks for two reasons: to break potential circular dependencies; and to avoid instantiating callbacks for all methods when only one method is being called.
First of all you don't need to register service provider explicitly, it should be available without any extra registrations.
Second of all resolving IServiceProvider from the scope, at least ATM (.NET 6) not only returns the same scoped provider, but the same instance:
var services = new ServiceCollection();
services.AddScoped<MyScopedDep>();
var serviceProvider = services.BuildServiceProvider();
using var serviceScope = serviceProvider.CreateScope();
var providerResolvedFromScope = serviceScope.ServiceProvider.GetRequiredService<IServiceProvider>();
Console.WriteLine(object.ReferenceEquals(serviceScope.ServiceProvider, providerResolvedFromScope)); // prints "True"
Console.WriteLine(object.ReferenceEquals(serviceScope.ServiceProvider.GetRequiredService<MyScopedDep>(), providerResolvedFromScope.GetRequiredService<MyScopedDep>())); // prints "True"
And from the docs:
For example, if you resolve services from a scope, and any of those services take an IServiceProvider, it'll be a scoped instance.
I have a scoped service (lets it be UserContext, that contain user IP). It service I try to inject in another services (let's call them ProfileManager and LogerProvider).
In my controller at startup, I added them like so:
service
.AddTransient(ILogerProvider, LogerProvider)()
.AddSingleton<IProfileManager, ProfileManager)()
.AddScoped<IUserContext, UserContext>()
Class LogerProvider contain UserContext inject:
class LogerProvider: ILogerProvider
{
private readonly IUserContext _userContext;
public LogerProvider(IUserContext userContext)
{
_userContext = userContext;
}
}
Class ProfileManager contain LogerProvider inject:
class ProfileManager: IProfileManager
{
private readonly ILogerProvider _logerProvider;
public ProfileManager(ILogerProvider logerProvider)
{
_logerProvider = logerProvider;
}
}
And when i try to run my program i got error:
(Inner Exception #1) System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: IProfileManager Lifetime: Singleton ImplementationType: IProfileManager': Cannot consume scoped service 'IUserContext' from singleton 'IProfileManager'.
I found that i can just change lifetime of ProfileManager just make it transient. But i need to use this service like singleton. So a question: how i can realize dependency injection saved lifetime of services how i typed at the beginning of the text?
You need to manually create a scope and consume it, to do so you'll need to inject the IServiceProvider into your singleton service and then call IServiceCollection.CreateScope
public class ProfileManager : IProfileManager
{
private readonly IServiceProvider _services;
public ProfileManager(IServiceProvider services)
{
_services = services;
}
public void DoSomething()
{
using (var scope = _services.CreateScope())
{
var logger = scope.ServiceProvider.GetRequiredService<ILogerProvider>();
}
}
}
Sidenote: You may want to rethink the lifetimes and scopes of your services if you find yourself repeating this pattern again and again
I would strongly advise against storing any sort of user context information in a singleton class, for security reasons. If you do it wrong, users will be able to see each others' data.
If your logger needs user context information, have the caller pass it in and store it only as a local variable for the duration of the method call. Never store it as a member variable.
class LogerProvider: ILogerProvider
{
public void LogMessage(IUserContext userContext, string message)
{
//Write to logs, including user context information
}
}
This will also prevent anyone from trying to log anything when there is no user context, as it will be obvious to them that they don't have one of the necessary arguments. If your logger has a dependency on user context then its methods should not be called when there isn't one.
If you don't want to burden the caller with the extra argument, consider using the ThreadPrincipal to store your user context.
To be able to use scoped services within a singleton, you must create a scope manually. A new scope can be created by injecting an IServiceScopeFactory into your singleton service (the IServiceScopeFactory is itself a singleton, which is why this works). The IServiceScopeFactory has a CreateScope method, which is used for creating new scope instances.
public class MySingletonService
{
private readonly IServiceScopeFactory _serviceScopeFactory;
public MySingletonService(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Execute()
{
using var scope = _serviceScopeFactory.CreateScope()
var myScopedService = scope.ServiceProvider.GetService<IMyScopedService>();
myScopedService.DoSomething();
}
}
The created scope has its own IServiceProvider, which you can access to resolve your scoped services.
It is important to make sure that the scope only exists for as long as is necessary, and that it is properly disposed of once you have finished with it. This is to avoid any issues of captive dependencies. Therefore, I would recommend:
Only define the scope within the method that you intend to use it. It might be tempting to assign it to a field for reuse elsewhere in the singleton service, but again this will lead to captive dependencies.
Wrap the scope in a using statement. This will ensure that the scope is properly disposed of once you have finished with it.
In short, IServiceProvider.CreateScope() and IServiceScopeFactory.CreateScope() are identical (in non-scoped context even instances of IServiceProvider and IServiceScopeFactory are identical).
But here is a little difference between these abstractions
IServiceProvider's lifetime can be Scoped. But IServiceScopeFactory's lifetime is always Singleton.
I am using Azure Functions version 2.x. It has built-in support for dependency injection.
So I can register my service IMyService for DI at singleton scope using:
public class Startup : FunctionsStartup
{
public override void Configure(IFunctionsHostBuilder builder)
{
builder.Services.AddSingleton<IOther, DefaultOther>();
builder.Services.AddSingleton<IMyService, DefaultMyService>(); // IMyService depends on IOther.
}
}
An instance of DefaultMyService is created the first time the function gets executed. This means the first request is slower because it does heavy initialization that happens inside DefaultMyService (it populates cache, etc.).
Question: Is there a way to have DefaultMyService created earlier than the first request?
A similar question was asked for asp.net core, and the answers there suggests a few solutions, but none of them works in the context of a function app:
Option 1: Create an instance of my service (initialization happens here), and then register the instance (instead of registering the type)
var foo = new Foo();
services.AddSingleton<IFoo>(foo);
This doesn't work because in my case IMyService depends on other services, which are not instantiated at the time when I am registering IMyService in the Configure method. It fails with an error that's described here.
Option 2: Other suggestion is to use overloaded Configure method:
public void Configure(IApplicationBuilder app, IFoo foo)
{
...
}
This also doesn't work because in case of function app, the only configure method that gets executed is Configure(IFunctionsHostBuilder builder), and other overloads are not called.
because it does heavy initialization that happens inside DefaultMyService
This is where the core of the problem lies. As Mark Seemann explained here, Injection constructors should do nothing more checking for null and storing incoming dependencies. Any time you do any I/O or invoke the class's dependencies inside the constructor, you'll get in trouble.
Your question seems similar to this q/a, and my advise would be the same: extract the initialization logic out of the constructor and either do the following:
Do the initialization before wiring the object graph and supply the DI configuration with an initialized object, or
Resolve and invoke an object graph directly after the registration phase, and before the first request, in such way that the data can be initialized.
I'm registering a service as a singleton in .NET Core. Yet I'm seeing the constructor for the singleton called multiple times.
services.AddSingleton<DbAuthorizationOptions, ContextAuthorizationOptions>();
My context authorization options is just Dictionary of Entity Types to IValidators, The context authorization options are passed into the DBContext, to automatically run validations.
During the registration of my services, I also register dynamic Validators with my container registered in DI.
var useDynamicValidator = serviceOption.ValidatorOptions != null;
if(useDynamicValidator)
{
//TODO: Extract this to before the register service no sense in building the provider each time
//TODO: Make this cleaner don't be dependent on Authorization options
var provider = services.BuildServiceProvider();
var authOptions = provider.GetService<DbAuthorizationOptions>();
var validator = BuildDynamicValidatorFactory(serviceOption).Invoke(provider, null);
authOptions.ValidatorOptions.AddValidatorForSet(validator);
}
I notice that when I call GetService on the provider I receive a new singleton instead of the existing one. Does building the provider create a new container so all of the services get re-registered?
If so, How can I call a method to register my dynamic validators in the singleton container with the existing IServiceProvider, is there a way to invoke some registration once after the service container is built?
Does building the provider create a new container so all of the services get reregistered?
Yes. See the source code.
If so, How can I call a method to register my dynamic validators in the singleton container with the existing IServiceProvider, is there a way to invoke some registration once after the servicecontainer is built?
I'm not really understanding why this is a problem. You should be registering all of your services one time at application startup in the Composition Root.
The DI container is then responsible for resolving the object graphs of the application. The application itself shouldn't have a dependency on it, nor be required to update it.
You should be injecting DbAuthorizationOptions in the place where you need to use it.
public class Foo : IFoo
{
private readonly DbAuthorizationOptions authOptions;
public Foo(DbAuthorizationOptions authOptions) // <-- Inject parameters
{
this.authOptions = authOptions ??
throw new ArgumentNullException(nameof(authOptions));
}
public void DoSomething()
{
// TODO: Inject the type that has the BuildDynamicValidatorFactory
// method and the serviceOption (whatever type that is) here
// either as a method parameter of this method, or a constructor
// parameter of this class.
var validator = BuildDynamicValidatorFactory(serviceOption).Invoke(provider, null);
// Now we have an instance of authOptions that can be used
authOptions.ValidatorOptions.AddValidatorForSet(validator);
}
}
Note that the DI container automatically provides the DbAuthorizationOptions if injected into another type that is also resolved through DI (such as a controller or filter).
NOTE: It isn't very clear from your question where you need to do this. You mention that you want it to happen once, which usually means to put it at application startup. But users cannot interact with code that runs at startup. So, maybe you could use a filter. It really all depends on where in the lifecycle of the application it has to happen.
You can declare a dependency on IServiceProvider -- don't build it, inject it.
public class SomeController
{
DbAuthorizationOptions authOptions;
public SomeController(IServiceProvider provider)
{
authOptions = provider.GetSerivce<DbAuthorizationOptions>();
}
}
But this is the service locator anti-pattern. As I commented on NightOwl888's post after you gave more details, a factory is probably a better approach.
We have several applications hosted in Windows services that self host a Nancy endpoint in order to expose instrumentation about the operation of the applications.
We use Autofac as our IOC. Several repositories are registered into the root container in a core DLL shared by all applications; this container is then passed to Nancy as its container using a bootstrapper derived from the Nancy.Autofac.Bootstrapper.
What we found was that when a web request is received by Nancy it resolves a request for a repository from the root container and this led to memory being consumed by non-garbage collected IDisposables as the root container does not go out of scope (it has the lifetime of the windows service). This led to the services "leaking" memory.
We then switched to a model where we added registrations for the repositories using InstancePerRequest in the overridden ConfigureRequestContainer() method in our Nancy bootstrapper:
protected override void ConfigureRequestContainer(ILifetimeScope container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
PerRequestContainerBuilder().Update(container.ComponentRegistry);
}
private static ContainerBuilder PerRequestContainerBuilder()
{
var builder = new ContainerBuilder();
// Dependency for repository
builder.RegisterType<SystemDateTimeProvider>().InstancePerRequest().As<IDateTimeProvider>();
// Repository
builder.RegisterType<BookmarkRepository>().InstancePerRequest().As<IBookmarkRepository>();
return builder;
}
We also override the CreateRequestContainer() method to create the request container with the tag MatchingScopeLifetimeTags.RequestLifetimeScopeTag.
protected override ILifetimeScope CreateRequestContainer(NancyContext context)
{
return ApplicationContainer.BeginLifetimeScope(MatchingScopeLifetimeTags.RequestLifetimeScopeTag);
}
This appears to have solved the problem of IDisposables not being disposed - the child request container is disposed at the end of the web request pipeline and objects resolved by it are also disposed and eventually garbage collected.
Our problem is that this seems to be leaking the implementation details of the repositories into the services as we have to not only register the repository in ConfigureRequestContainer() but also any other objects required by the repository, i.e. if we want to change the implementation of a repository we have to "walk the dependency chain" to register required objects in each service using it - this seems wrong.
Is there a way we can get Autofac to resolve supporting objects for the repositories out of the root container but keep the registration information within the scope of the web request container? Or is there a way to automatically copy existing registrations from the root container into the child container when it is created?
Autofac should automatically resolve instances from "parent" lifetimes. If you configure your registrations using InstancePerRequest, Autofac will register these services with a special lifetime tag, MatchingScopeLifetimeTags.RequestLifetimeScopeTag, so it can be resolved in the correct scope later.
This means that there's no need to use the Nancy bootstrapper's ConfigureRequestContainer method to do request-scoped registrations. You've already done it! As long as Nancy creates the request lifetime using the same tag used in InstancePerRequest (this is done by default as of Nancy 1.1), the services should be resolved correctly.
Example:
public class Startup
{
public void Configuration(IAppBuilder app)
{
var builder = new ContainerBuilder();
// Do request-scoped registrations using InstancePerRequest...
var container = builder.Build();
// Pass the pre-built container to the bootstrapper
var bootstrapper = new MyAwesomeNancyBootstrapper(container);
app.UseNancy(options => options.Bootstrapper = bootstrapper);
}
}
public class MyAwesomeNancyBootstrapper : AutofacNancyBootstrapper
{
private readonly ILifetimeScope _lifetimeScope;
public MyAwesomeNancyBootstrapper(ILifetimeScope lifetimeScope)
{
_lifetimeScope = lifetimeScope;
}
protected override ILifetimeScope GetApplicationContainer()
{
return _lifetimeScope; // Tell Nancy you've got a container ready to go ;)
}
}
This setup should be enough (As of Nancy 1.1. In earlier versions you have to also override the CreateRequestContainer method and pass the request lifetime tag when creating the request lifetime scope).
EDIT: I put together an example for you at https://github.com/khellang/Nancy.AutofacExample