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.
Related
I am calling a purchased package, which I do not have source code for. It is expecting a service pre-configured through DI. I can't change this.
I don't know all of the parameters for the service until the user logs on. User 'A' might have a different configuration than User 'B'. If no user is logged in, then the service needs to be a different way.
I can't inject it during 'Startup', as it will be configured differently for 'A' and 'B' (and 'no user')
The best place (I think) to do it is during Middleware 'Invoke', however, I can't seem to get access to IServicesCollection to perform '.AddTransient<>'
Is this even possible?
Or is there a better way to create an '.AddTransient' service dynamically?
If intending to access the service within the middleware invoke then there is no need to try and access ServiceCollection outside of Startup
register desired transient service using deferred factory delegate
//...
services.AddTransient<IService>(sp => {
var ctx = sp.GetService<IHttpContextAccessor>().HttpContext;
var user = //get user however you intended to get user
//create instance of service
return ActivatorUtilities.CreateInstance<Service>(sp, user);
});
//...
In the middleware the service can be explicitly injected via method injection
//...
public async Task InvokeAsync(HttpContext context, IService service) {
//...
}
//...
Additional parameters for the middleware's InvokeAsync, after HttpContext are populated by dependency injection (DI).
This will in turn resolve your transient service when injecting into the member
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.
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.
I am playing around with ASP.NET Core on my own hobby project, I want to create a framework that will be consumed by a developer, and I want to allow optional service and use defaults if they are not registered.
I am getting the Unable to resolve service for type 'XXX' error, but I would prefer the DI to return null rather then throw an exception.
I want to allow for optional services, so if a service is found, use that in the constructor, if not found, pass null into the constructor.
In my implementation I have:
public IServiceManager(IService service, ...)
{
_service = service ?? new DefaultService();
...
}
So as you can see, if the service cannot be found (null) use the default.
Perhaps I am misunderstanding how DI works. Perhaps I could use a factory to do this instead?
However, in my system I using default services when non is provided will be a common occurrence, so I need a solution that doesn't require the consumer of the API to register a service.
Is there a way to configure ASP.NET Core DI to return null rather then throw an exception?
Add default value to that parameter in the constructor.
public IServiceManager(IService service = null, ...)
{
_service = service ?? new DefaultService();
...
}
By their very nature, constructor injection is always considered as mandatory.
The very first versions of the Microsoft DI (I don't like using the term ASP.NET Core DI, because it does not depend on ASP.NET Core and can be used outside of it) only supported the constructor with the most parameters.
I think this has been changed since then to allow multiple constructors and the IoC container will choose a fitting one. That being said, you'd likely need to define multiple constructors.
public IServiceManager(IService service, IOtherService otherService)
{
}
public IServiceManager(IOtherService otherService)
{
}
Then the second constructor should be called, if IService isn't registered with the IoC container.
But it's still quite a questionable practice at best and makes your code harder to maintain and hold its invariant/loose coupling.
You should never have to instantiate your types inside your services, not even for optional services.
Instead, you should provide registrations which allow a user to override them with their own implementations.
public static IServiceCollection AddMyLibrary(this IServiceCollection services)
{
services.TryAddTransient<IService, Service>();
services.TryAddTransient<IOtherService, OtherService>();
}
Then the user override it.
services.AddTransient<IService, CustomService>();
services.AddMyLibrary();
Now CustomService will be injected where IService is requested.
Easiest would be to register the DefaultService component itself for the IService service within your IoC container - I'm using the terminology of Castle Windsor. Most of the containers allow to register multiple components for a service. In case you do not register a custom component for the service (another implementation of IService), DefaultService will be resolved and injected; otherwise your custom component will be resolved for the service, just register the components in proper order (in Castle Windsor, the component registered first will be considered: multiple components for a service)
WindsorContainer container = new WindsorContainer();
container.Register(Component.For<IServiceManager>().ImplementedBy<ServiceManager>());
container.Register(Component.For<IService>().ImplementedBy<CustomService>());
container.Register(Component.For<IService>().ImplementedBy<DefaultService>());
IServiceManager serviceManager = container.Resolve<IServiceManager>();
IService service = ((ServiceManager)serviceManager).Service; // service is of type CustomService
Regarding the comment below from #Tseng:
This beats the idea of having Dependency Injection / IoC container in the firstplace, when you instantiate it inside the constructor
It is not always the case... If you have an optional dependency, first, define it as a property with a public setter, so component can be injected if registered. In case there is no component registered (thus property is not set by the container), I think it can be acceptable to instantiate the default component via the "dangerous" new keyword. Everything is context-dependent - to be clear, I wouldn't instantiate a service manually, but there are always exceptions.
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