Can't acces method in startup from scoped service - c#

I have a basic interface and class that I've registered as a scoped service in the "ConfigureServices".
Example:
services.AddScoped<ICachingDataManager, CachingDataManager>();
In this class CachingDataManager I inject a DbContext from EF. This context is then registered in the "ConfigureServices". Nothing fancy.
services.AddDbContext<PhoneContext>();
Now I want to run a method from CachingDataManager on startup (it initializes the cache to give some context). So in the "Configure" part I run:
app.ApplicationServices.GetRequiredService<ICachingDataManager>().Initialize();
Here it keeps failing because it's a scoped service, if I execute a method from a singleton this executes without any problem.
Any insights?

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.

ASP.NET Core transient DbContext

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?

InvalidOperationException: Cannot consume scoped service 'Microsoft.JSInterop.IJSRuntime' from singleton '...IAuthentication' in Blazor

I'm creating Blazor project that at first everything works fine until I need to inject IJSRuntime into cs file.
Microsoft.JSInterop;
...
...
public BaseService(IJSRuntime jSRuntime)
{
}
BaseService is inherited in another service named AuthenticationServices which is also uses an Interface called IAuthentication. Thus
using Microsoft.JSInterop;
public class AuthenticationServices : BaseService, IAuthentication
{
public AuthenticationServices(IJSRuntime jSRuntime) : base(jSRuntime)
{
}
}
My issue is in Startup.csfile which has this code
services.AddSingleton<IAuthentication, AuthenticationServices>();
If I run the app it says,
InvalidOperationException: Cannot consume scoped service 'Microsoft.JSInterop.IJSRuntime' from singleton '...IAuthentication'
What does it mean? Am I doing it correctly that I only need something to add?
Dependency injection in the Blazor has 3 different lifetime policies.
Singleton
Scope
Transient
Singleton
This means that any service of that type will have only one instance.
Scope
This lifetime mean that for set of object created scope and within that scope will be just one instance. Usually in most scenarios, scope is created for processing User Session (Client-side Blazor) or User connection (Server-side Blazor). You can compare with scope per single HTTP request (ASP.NET).
Transient
Object with this lifetime created each time they are requested. It's same to regular new.
Lifetime consumption rules
Given the nature of these object lifetime policies, following rules for consuming services applies.
Transient service can consume Transient, ScopedandSingleton` services.
Scoped service can consume Scoped and Singleton services. But cannot consume Transient services.
Singleton services can consume only Singleton services. But cannot consume Transient and Scoped services.
Service IJSRuntime registered inside Blazor as Scoped service, as such it can be used only by Scoped and Transient services.
So you either have to make AuthenticationServices the Scoped service, or get rid of IJSRuntime as constructor parameter.

Configuring Entity Framework In ASP.NET 5 using a DAL

I'm trying to completely separate the repository / data access later whilst still using configuration in the main project (through appsettings.json)
My current solution is either to hard code the connection string in the OnConfiguring method in the DbContext which is not ideal. Or perhaps create a config file just for the repository layer and read from that.
I would like the main project (web service in this case) to be completely independent of the data access layer (as I will be communicating to it using a service layer anyway), but be able to configure it at start-up using the default appsettings.json
Is there a good approach of doing this? Or must the main project have a reference to the repository layer.
Project layout:
Project.WebService
- Startup.cs
- EmployeeController.cs
- appsettings.json
Project.Service
- EmployeeService.cs
- EmployeeDTO.cs
Project.DAL
- DbContext.cs
- EmployeeRepository.cs
Project.Entities
- Employee.cs
The WebService references the Service, the Service references the DAL and the DAL references the Entities / POCOs
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
string connString = Configuration["AppSettings:ConnectionString"];
services.AddEntityFramework()
.AddSqlServer()
.AddDbContext<MyDbContext>(options => options.UseSqlServer(connString));
}
The above doesn't work anyway with the DbContext being in another class library. The following exception gets thrown.
An exception of type 'System.InvalidOperationException' occurred in
EntityFramework.Core.dll but was not handled in user code
Additional information: No database providers are configured.
Configure a database provider by overriding OnConfiguring in your
DbContext class or in the AddDbContext method when setting up
services.
Which is one reason why I hard-coded the connection string into the OnConfiguring method.
Does anyone have any good approaches / solutions for this or am I missing something? The web service doesn't need to know what database it is talking to, or have any reference to it at all. It simply calls a service, a receives a DTO or, sends a DTO.
Hook up all dependencies:
If you really insist on your webservice not having a reference to your DAL, you could always try an 'IT ops' layer.
Some infrastructure layer (which you can call for instance "InfratructureService", which has a reference to all your projects (webservice, Service, DAL, Entities). In this infrastructure layer you create the container which every project is going to use.
Then all your projects should have a class which is called "ConfigureMe" or something, with 1 method called "Configure" for instance. That method takes as parameter an instance of the container which was build in InfratructureService. At startup your Webservice is going to give "InfratructureService" a sign to build the container and call every "Configure" method of every "ConfigureMe" class of every project it references.
In those "Configure" methods each project can ofcourse register their own depedencies in the container. And voila, all dependencies are known in the container without making unnecessary references and ready to be resolved via constructor injection.
The connectionstring problem:
Now as for your hardcoded connectionstring, you know about configuration in asp.net core right? Once you've set up your configuration as described here you can insert via dependency injection the IOptions anywhere you want. That means also in your DAL.
Let me know if anything is unclear or you can't figure it out.

Dependency Injection in the ASP.NET 5 and Object Dispose

Can anybody help me to understand following in context to Dependency Injection in Asp.Net 5 and object dispose.
I need to understand if my Service implements IDispose interface ,who will call dispose method.
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<IService, Service>();
services.AddScoped<IService, Service>();
services.AddSingleton<IService, Service>();
services.AddInstance<IService, Service>();
}
IServiceCollection contains the set of services available in your application. You defines the services you want to use and their lifetime, and the application will instantiate and dispose them for you.
There are 4 different lifetimes :
Transient
Transient lifetime services are created each time they are requested. This lifetime works best for lightweight, stateless service.
Scoped
Scoped lifetime services are created once per request.
Singleton
Singleton lifetime services are created the first time they are requested, and then every subsequent request will use the same instance. If your application requires singleton behavior, allowing the services container to manage the service’s lifetime is recommended instead of implementing the singleton design pattern and managing your object’s lifetime in the class yourself.
Instance
You can choose to add an instance directly to the services container. If you do so, this instance will be used for all subsequent requests (this technique will create a Singleton-scoped instance). One key difference between Instance services and Singleton services is that the Instance service is created in ConfigureServices, while the Singleton service is lazy-loaded the first time it is requested.
The asp.net 5 official documentation is great, take time to read it : http://docs.asp.net/en/latest/fundamentals/dependency-injection.html
The documentation doesn't mention how exactly the dependencies lifetimes are handled by the dependency injection service but if you search in the code, you will find the ServiceProvider class, who manages the lifetimes : ServiceManager class
To be a little more specific, when a scope is created, the service scope factory returns a new service scope, who is instanciated with a service provider. When the dependency injection service have to dispose a service, he calls the service scope's dispose method, who calls the service provider's dispose method.
How the service provider works ? He has all the service scopes in a property, named _resolvedServices, and all the transiant disposables in a property named _transientDisposables. When the dispose() method of the service provider is called, he loops on all the items he has in this two properties, and for each object calls his dispose method.
You have all the source code here : Dependency Injection source code

Categories

Resources