This question already has answers here:
How to consume a Scoped service from a Singleton?
(2 answers)
Closed 4 years ago.
My .net core app needs to crawl data in a specified time interval. I have chosen to implement IHostedService to run it in parallel with API. The hosted service needs some services injected. I register them in startup.cs, but I get an error:
System.InvalidOperationException: 'Cannot consume scoped service 'IXService' from singleton 'Microsoft.AspNetCore.Hosting.Internal.HostedServiceExecutor'.'
My startup.cs:
services.AddScoped<IXService, XService>();
services.AddHostedService<MyHostedService>();
I had a similar problem yet with DbContext, I solved it with https://stackoverflow.com/a/48368934/8475133, but this time I need dependency injection going through deeper layers and dealing with IServiceScopeFactory in each doesn't seem to be an elegant solution.
The reason you're not allowed to do this is because MyHostedService has a singleton lifetime, which is a longer lifetime than scoped. The basic assumption here is that a service that is registered as scoped should not be kept alive indefinitely, this could easily happen if a singleton service keeps a reference to a scoped service.
I think the solution you're looking for is to inject IServiceProvider into MyHostedService, use it to create a new scope and new XService instance whenever you need it.
That is, replace
_xService.Foo();
with
using(var scope = _serviceProvider.CreateScope()) {
var xService = scope.ServiceProvider.GetService<IXService>();
xService.Foo();
}
An alternative, of course, would be to simply register XService as a singleton, by just replacing the call to AddScoped with AddSingleton, but I would not recommend it.
Edit: I must admit to not reading your link before posting my response. However, I still think this is the most elegant solution.
Related
I have multiple hosted services each working with the database. I was getting the DbConcurrencyException, since all the hosted services were using the same instance of the dbContext simultaneously (at the start of the application).
I've resolved this issue by changing the lifetime of the dbContext from scoped to transient. What did I really change by this? Will the application connect and disconnect from the database each time I work with the dbContext? If not, is there any other issue?
IHostedService or BackgroundService are singletons. Also, by default DBContext (when you run AddDbContext) is scoped service. So in each hosted service, you need to open a scope using IServiceScopeFactory. Like this:
using var scope = _scopeFactory.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>;
Regarding your question about what it has changed when you registered it as transient. Each time you inject and call DBContext it will instantiate a new DbContext. When the service is scoped, during one scope if you are going to call DBContext multiple times, you will get the same instance of DBContext, which will improve performance, so maybe this is what you need?
Edit: Due to lots of users mistakenly taking this as a ASP.NET specific question. Please note that my application is not a web application and I'm not using ASP.NET application (I'm using it's funtionality, that is available in .NET Core as well).
Recently, while configuring an Entity Framework DbContext lifetime in a Ninject DI, I have been digging through the .NET Core Dependency Injection, because it already has a functionality for registering DbContext and can be found here. The default context life time is ServiceLifetime.Scoped.
In the code peek, we can read that in the ASP.NET applications, "scoped" means:
scope is created around each server request
namespace Microsoft.Extensions.DependencyInjection
{
//
// Summary:
// Specifies the lifetime of a service in an Microsoft.Extensions.DependencyInjection.IServiceCollection.
public enum ServiceLifetime
{
//
// Summary:
// Specifies that a single instance of the service will be created.
Singleton = 0,
//
// Summary:
// Specifies that a new instance of the service will be created for each scope.
//
// Remarks:
// In ASP.NET Core applications a scope is created around each server request.
Scoped = 1,
//
// Summary:
// Specifies that a new instance of the service will be created every time it is
// requested.
Transient = 2
}
}
I'm trying to achieve a similar functionality in Ninject DI but it's really hard to state what would be the equivalent of scoped life time in Ninject, while speaking about .NET Core application (that isn't a web application!).
Ninject has that InRequestScope method, however it's only available for web applications, so it's really different from the .NET Core DI ServiceLifetime.Scoped setting.
Perhaps I would have to create some sort of a custom scope in Ninject, but still - I'm not really able to state, how to achieve exact the same scoped behaviour as in the .NET Core DI. To do that I need to be aware of how is the scoped life time working in context of a .NET Core application in .NET Core DI. My guess would be that there's one instance of a DbContext being created and is being disposed once the application quits.
Hence my questions:
How is .NET Core DI scope life time setting working and what is it's life cycle?
Is it possible to achieve a similar behaviour in Ninject DI?
How is .NET Core DI scope life time setting working and what is it's
life cycle?
.Net core internally works with class called ServiceScope. When new request is called (e.g. web request) new instance is created, with new service provider included. During request this service provider is used for dependency resolution. After request is finished, scope is disposed and also its service provider with its resolved services.
internal class ServiceScope : IServiceScope, IDisposable
{
private readonly Microsoft.Extensions.DependencyInjection.ServiceProvider _scopedProvider;
public ServiceScope(Microsoft.Extensions.DependencyInjection.ServiceProvider scopedProvider)
{
this._scopedProvider = scopedProvider;
}
public IServiceProvider ServiceProvider
{
get
{
return (IServiceProvider) this._scopedProvider;
}
}
public void Dispose()
{
this._scopedProvider.Dispose();
}
}
Is it possible to achieve a similar behaviour in Ninject DI?
As you have already noticed implementing custom scope is way to go. You can check how to do this in another answer:
Ninject - In what scope DbContext should get binded when RequestScope is meaningless?
EDIT:
Principle of .NET Core DI is the same like any other IOC container. It provides dependencies to your objects (MVC controllers etc.) by DI and controls its lifetime.
If you specify singleton lifetime for your DbContext than only one is
created, provided by DI when requested and hold in memory for
whole application/container lifetime.
If you specify transient you get new
one all the time DbContext is requested.
If you specify scoped,
lifetime of DbContext is bound to some disposable scope, which is created on the beggining of some logical request (http request in case of asp). When DbContext is
requested by DI for the first time, new one is created, hold in memory and you get always the same during
subsequent DI requests until the scope is disposed (with end of http request in case of asp) and DbContext with
it.
You can find similar parallel with TransactionScope. Here all the sqlCommands within the same TransactionScope are enlisted into the same sql transaction util the scope is disposed/committed.
There is extension method called InRequestScope, which is available in Ninject.Web.Common nuget package.
InRequestScope : https://github.com/ninject/Ninject.Web.Common/wiki/InRequestScope
You can correlate .net core and ninject DI methods
from https://github.com/ninject/Ninject/wiki/Object-Scopes
In .Net core 1.0 and 1.1 in my Services Collection I had:
services.AddDbContext<VisualJobsDbContext>();
...
...
services.AddScoped<IRecruiterRepository, RecruiterRepository>();
services.AddSingleton<IAccountRepository, AccountRepository>();
this seemed to work. Now I've gone to .net core 2.0 this has stopped working:
I've changed the above to :
services.AddDbContext<VisualJobsDbContext>(ServiceLifetime.Scoped);
...
...
services.AddScoped<IRecruiterRepository, RecruiterRepository>();
services.AddScoped<IAccountRepository, AccountRepository>();
I still receive the following error using swagger:
An unhandled exception occurred while processing the request.
InvalidOperationException: Cannot consume scoped service
FindaJobRepository.Interfaces.IAccountRepository from singleton
FindaJobServices.Interfaces.IAccountService
Does anybody have any ideas what else I can do?
IAccountService is a added as singleton and can't depend on a service with a shorter lifetime. IAccountRepository is a scoped service.
Probably you have a constructor with something like this:
public IAccountService(IAccountRepository accountRepository) {
}
Not sure how that ever worked, but essentially your issue is that you're trying to inject scoped services into a singleton, which based on the error is your AccountService.
Simply, if you need to use scoped services, the classes you inject them into must also be scoped. If you need the lifetime to be singleton, then your only option is use the service locator anti-pattern, where you instead inject IServiceProvider, and then create a scope around your operations:
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.GetRequiredService<MyDbContext>();
// do something with context
}
Importantly, do not save the instances you retrieve from your scope to ivars, statics, etc. on your singleton class. These will disposed when the scope is disposed.
Is there a possibility to register a service scoped to the Session?
In the documentation I found the possibility to add
Transient: New instance each time the service is requested
Scoped: New instance for each HTTP request
Singleton: One instance as long as the server is up and running
Instance: Basically a singleton but with the instance I create
But I did not find anything to register a service scoped to the Session.
Is there a workaround for this? Or is it just not possible?
It would be possible, of course. But as far as I know it would need some coding to write custom solution. I would start with try to write and integrate custom 'life time manager' (in unity nomenclature), or create custom factory for subject service and register it as singleton.
I have an application in ASP.NET MVC that also have a WCF Service included in the same proyect.
Im using Autofac to manage dependency injection. The problem is that when the application is accessed throught web, I need the dependencies to be instanced per Http request. And when the application is accessed throught WCF, I need the dependencies to be instanced per dependency.
In Castle.Windsor, there is a proyect to manage hybrid lifestyles (in this link).
I need something similar, something like:
builder.Register<UnitOfMeasureService>(x => new UnitOfMeasureService())
.As<IUnitOfMeasureService>().HybridLifetimeInstance();
Are there a workaround to manage the instance lifetime depending on when the application has a HttpContext or not?
Autofac does not have support for custom lifestyle managers.
Autofac lifetimes revolve around scopes, which are nestable and can be optionally tagged with a known ID. That's how instance-per-HTTP-request works: A nested scope "tagged" with a known value ("AutofacWebRequest") is created when a web request comes in. The hierarchy looks like this:
Container (root lifetime scope)
Web Reqeust Scope (tagged "AutofacWebRequest")
Any child scopes you might create in your code
When using InstancePerHttpRequest it's basically the same as InstancePerMatchingLifetimeScope("AutofacWebRequest"). If you resolve the type, it falls back until it finds a scope with that name and then uses the same instance in that tagged scope.
In standard WCF hosting, Autofac resolves everything out of a child scope you can get from the instance context (AutofacInstanceContext.Current.OperationLifetime). You could create a child lifetime scope from that and manually tag it, then resolve your dependencies like this:
var opScope = AutofacInstanceContext.Current.OperationLifetime;
using(var requestScope = opScope.BeginLifetimeScope("AutofacWebRequest"))
{
// Resolve InstancePerHttpRequest items from requestScope
}
However, there's no way to do that automatically and the WCF hosting mechanism isn't currently architected in a way you can "plug in" and do this - if you needed the web request scope automatically created, you'd have to roll your own WCF hosting mechanism based on the Autofac source code.
The other option is to have two different containers - one for your web stuff and one for your WCF stuff - and register the component with a different lifetime in each container.
Beyond that... there's really no way to "switch" lifetimes based on context. A component gets one lifetime declared and has to live with it for that component registry.