.net core 2.0 scoping issue with dbcontext - c#

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.

Related

Dependency Injection lifetimes for a Factory

I have a .Net Core 3.1 web API, and I have the followings dependencies:
As Scoped SomeDbContext (using AddDbContextPool).
As Transient SomeDbRepository, it recive the above SomeDbContext.
As Transient SomeService, it recive the above SomeDbRepository.
As Singleton a factory for SomeService.
Here the code for injecting the dependencies in Startup.cs
services.AddDbContextPool<SomeDbContext>(options => options.UseSqlServer("connectionsString"));
services.AddTransient<SomeDbRepository>();// recive SomeDbContext
services.AddTransient<SomeService>();// recive SomeDbRepository
//Factory for SomeService
services.AddSingleton<Func<SomeService>>(provider => () =>
{
return provider.GetService<SomeService>();
});
API Controller
//API Controller
public SomeController(Func<SomeService> factory)
{
_someService = factory.Invoke();
}
When I call my API in a multi thread enviroment I get the error:
Error: A second operation started on this context before a previous operation
completed. This is usually caused by different threads using the same
instance of DbContext
Why is my DbContext used by different threads? It suposse the Factory returns a new scoped DbContext per request. I use (Invoke) the Factory only one time por request.
If I change the lifetime of the Factory, from Sinlgeton to Scoped, it works ok. But I dont know Why? Why the lifetime of the Factory affects? Could someone explain me why it works that way. Thank a lot guys!!
The IServiceProvider parameter of the delegate of a Singleton registration provides a reference to the root scope. Scoped (and IDisposable Transient) services are tracked in the scope they are resolved from and since the root provider has its own scope, resolving from the root IServiceProvider causes Scoped (and IDisposable Transient) services to be cached for the lifetime of that root provider. This typically means: they live for as long as the application runs.
What this means is that, in the context of the MS.DI container, it is typically a bad idea to register delegates as Singleton that resolve from the IServiceProvider parameter, because it causes resolved Scoped depedendencies to accidentally become Singletons (they become accidental Captive Dependencies). With IDisposable Transients it becomes even more implicit, because resolving them from the root provider could even cause memory leaks, because they will only get released when the application shuts down.
Although it is fine to register a delegate as Singleton when it only resolves other Singletons (or non-disposable Transients), this is typically quite fragile, because it's easy to change this in the future, in a way that accidentally causes trouble.
Instead, with MS.DI, it's best to register your factory delegates as Scoped:
services.AddScoped<Func<SomeService>>(provider => () =>
{
return provider.GetService<SomeService>();
});
Note that this advise is specific to MS.DI and might not be appliable to other DI Containers.

What is the proper way to reuse DbContext across different projects in a single solution?

I have a .Net Core solution which consists of 3 projects like below:
Common : (All EF Data, migrations, Model, DbContext)
WebApi : (Rest api consuming DbContext from Common)
Worker : (Background services that aims to consume DbContext from Common)
I wanted to place all my EF logic and DbContext in Common and consume it from my other two projects.
WebApi is working fine,
but I couldn't use it from my hosted services found in Worker project.
There are 4 background workers and all of them require access to database so I wanted to get access to my DbContext inside them.
So, what is the proper way to reuse a DbContext across multiple projects.
It can be considered that all services need access some common tables. So isolating tables via different Dbcontexts is not an option for me.
This is my Startup.cs in WebApi:
services.AddDbContext<DataContext>(options => options
.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
This is my Program.cs in Worker:
services.AddDbContext<DataContext>(options => options
.UseNpgsql(Configuration.GetConnectionString("DefaultConnection")));
It throws an error telling me that I can not consume a scoped service from a singleton one.
If you just want to reference a DbContext in your Web API + Background Workers, then I don't understand what the issue is: Just reference the Common project from your worker projects. From your post the issue looks to be that the service injection you are using successfully in the Web API doesn't work with the background worker services. (Assuming Windows Services?)
Start instead with a simpler scenario. Initiate the DbContext inside the service when used rather than inject it.
I.e.
using(var context = new DataContext("DefaultConnection"))
{
// ...
}
Your connection string configuration should be identifying NPGSQL as the provider so as long as that config is all set up in your Services config then the DBContext should be able to configure by connection string name. If that works then there will probably be a different mechanism for injection. From what I could quickly find it seems examples used a service locator pattern to resolve dependencies, I don't know if there are better options these days for Windows Services.
If injection isn't really an option and you have to resort to service locator-like implementations then I would probably consider something like a Lazy Property injection pattern I've used in the past:
public class WorkerService
{
private readonly IContainer _container = null;
public WorkerService(IContainer container)
{
_container = container ?? throw new ArgumentNullException("container");
}
private IDataContextFactory _contextFactory = null;
public IDataContextFactory ContextFactory
{
get { return _contextFactory ?? (_contextFactory = _container.Resolve<IDataContextFactory>()); }
set { _contextFactory = value; }
}
public void Execute()
{
using(var context = ContextFactory.Create()) // returns a DataContext.
{
// do stuff.
}
}
}
Where IContainer represents a contract interface for your given DI framework. (Unity, Autofac, etc.)
Alternatively, a unit of work for scoping a DbContext. Given a Service instance will be long running we don't want to inject or resolve a DbContext, but rather a Context Factory which we can use to receive an initialized DbContext which can be used in a dispose. Normally with web requests the instance is scoped to the request and disposed by the container at the end of the request. With a service we want to ensure the DbContext is disposed regularly. A DI can be set up so that transient instances of the context are returned, but those instances need to be disposed meaning it's not suited for constructor injection, but rather via a service locator. If a single DbContext instance was used and injected in the constructor of a service, it would live until the service stopped which would see that DbContext get slower and slower as time went on due to tracked entities.
I think you're a bit confused here. The thing you want to reuse is the DbContext code + all EF logic. You don't want to (can't) reuse same DbContext instance across projects (apps).
So to reuse the code, you just need to put all of your Model + DBContext in a project. Then in other projects, you can add reference to it. And start using it.
Remove all AddDbContext from Startup.cs/Program.cs.
Put DbContext connectionstring in Common project instead.
Create some CRUDs in Common project. Then all other projects can use Common's CRUD which is connected to same DbContext.

Integrate Autofac with mediatr ASP.NET Web API2

I develop an app on asp.net api2 with autofac and mediatR, currently facing some issue with the dependency injection.
// This is registered in the global.asax file and working properly in the controller level
//i'm trying to register the entity framework context as instance per request
builder.RegisterType<EFContext>().InstancePerRequest();
However when sending the command throught MediatR pipeline, i get an exception because MetdiatR service provider cannot read the http request scope.
Below code is also located in the global asax file.
builder.Register<ServiceFactory>(context =>
{
var componentContext = context.Resolve<IComponentContext>();
return t => { object o;
return componentContext.TryResolve(t, out o) ? o : null; };
});
as the delegate function for service locator is called it throw an error saying that No scope with a Tag matching
"AutofacWebRequest" is visible from the scope in....
is there any work around to make mediatr ServiceFactory aware of the autofact InstancePerRequest scope ?
Use InstancePerLifetimeScope instead for resolving entity framework context.
I haven't used mediatR but seems like it doesn't follow the request response pattern, due to which Autofac can't associate any Request Lifetime with it.
Note that the difference between Request Scope and Lifetime Scope in Autofac is only that Autofac will treat a request as a lifetime.
You can read further about scopes from here,
https://autofaccn.readthedocs.io/en/latest/lifetime/instance-scope.html

.net core dependency injection to hosted service [duplicate]

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.

.NET Core DI scope lifetime in Ninject

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

Categories

Resources