Dependency Injection ASP.NET Core Singleton - c#

May I know what is wrong with following line of code:-
Why I can't use singleton on it?
services.AddSingleton<IProductRepository, ProductRepository>();
I have been getting 500 internal server error on the above code, however it is working fine with Transient and Scoped.

I assume based on the provided pattern that the repository depends on a DbContext?
public class ProductRepository : IProductRepository {
public ProductRepository (MyDbContext db) {
//...
}
}
Which tends to be registered as scoped.
If you try to register a singleton with scoped dependencies, it may cause the service to have incorrect state when processing subsequent requests.
Reference Dependency injection in ASP.NET Core : Service Lifetimes

According to Microsoft Documentiaon:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
Now look at the following code of your ConfigureServices method in Startup class:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
There is a second parameter in the UseSqlServer method for Service Lifetime whose default value is
ServiceLifetime.Scoped. May be you didn't specify the second parameter so it is taking the default value ServiceLifetime.Scoped. That means your DbContext has been registered as Scoped service. Now if you use your DbContext in ProductRepository then your ProductRepository has to be resisted as Scoped Service too otherwise ASP.NET Core DI provider cannot resolve it.
Now If you really want to register your ProductRepository as Singleton Service then make your DbContext also Singleton as follows:
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")),ServiceLifetime.Singleton);
Now it should work!
Note: I assumed that your ProductRepository is dependent on DbContext. If not then your ProductRepository repository must be dependent on a service which is registered as a Scoped Service and that's why currently you are not able to use ProductRepository as Singleton.

Related

Some services are not able to be constructed (Error while validating the service descriptor) C#

I am making asp .NET core web API and I made some services and I injected it and it is working fine.
I made a new web app in the same solution and I want to use the same services
and I just got
Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable serviceDescriptors, IServiceProviderEngine engine, ServiceProviderOptions options)
AggregateException: Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: E_CommerceApi.Services.IAttributeService Lifetime: Scoped ImplementationType: E_CommerceApi.Services.AttributeService': Unable to resolve service for type 'E_CommerceApi.Authentication.ApplicationDbContext' while attempting to activate 'E_CommerceApi.Services.AttributeService'.)
Service and IService Code
public interface IAttributeService
{
Task<AllAttributes> GetAll();
}
public class AttributeService : IAttributeService
{
private readonly ApplicationDbContext _db;
public AttributeService(ApplicationDbContext db)
{
_db = db;
}
}
Injecting the service in the same project
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ConnStr")));
// register services
services.AddScoped<IAttributeService, AttributeService>();
}
Injecting service in another web project which is not working
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddScoped<IAttributeService, AttributeService>();
}
As you are not registering the DBContext in your second project,
services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ConnStr")));
the attempt to resolve the IAttributeService fails because its implementation (AttributeService) expects it to be injected into its constructor.
However, if I understand what you're trying to do, and I'm not sure I do, it seems you want to be able to access the services in your second project without having to do this.
Is your second project a completely separate web application?
If so then you need to register all of the components it needs. So add the DBContext.
If not, ie. if it is a referenced project with controllers and views that are to be included in the main project via its reference. Then you need to consider this as a module of your main web hosted application. You would not then have another Startup class and you would not need to register any services. Instead you would be able to access any services via the first container and their interface.
If the second project has services that need to be registered into the container then you would need some way of doing this, such as an extension method on the IServiceCollection so it can be called in the main ConfigureServices method.
public static void RegisterSecondProjectModule(this IServiceCollection services)
{
// register your services here
services.AddScoped...
}
// then in your main ConfigureServices method:
services.RegisterSecondProjectModule();
Note: For any services you want to be available across both / all projects in your solution, it would make sense to move the interface into a separate project that both can reference. I typically create a project with a name suffix of .Abstractions and change the default namespace to exclude it.

How to avoid reload resource in ASP.NET core controller

I am new to ASP.net core. Having a web API connect to database using EntityFramework core. The controller take request, do some analysis, and send the response as below
public class CentoDataController : ControllerBase
{
private readonly CentoWebDBContext _context;
private HPOSubSimHolder _hpoSubSimHolder;
public CentoDataController(CentoWebDBContext context)
{
_context = context;
_hpoSubSimHolder = new HPOSubSimHolder(hpofile);
}
[HttpGet("{id}")]
public ActionResult<CentoData> GetCentoData(string id)
{
IQueryable<CentoData> r = AnalysisMethod(id, _hpoSubSimHolder);
return r;
}
The code works, but _hpoSubSimHolder will be reloaded once a new request comes in. I guess I shouldn't share controller between requests. But how can I avoid reloading _hpoSubSimHolder ?
I can see that you're using .net core dependency injection
If you want that service to be shared across requests, think of making it a Singleton.
You can choose between AddScoped, AddTransient and AddSingleton when registering dependencies.
In your startup.cs class:
public void ConfigureServices(IServiceCollection services)
{
// some code
services.AddSingleton<HPOSubSimHolder>(new HPOSubSimHolder());
}
Singleton means only a single instance will ever be created. That instance is shared between all components that require it. The same instance is thus used always.
Scoped means an instance is created once per scope. A scope is created on every request to the application, thus any components registered as Scoped will be created once per request.
Transient The services created using transient lifetime will be created each time they are requested. This lifetime works best for lightweight services.
(Source)
Controllers are always instantiated per request. To control lifetime of any resources or dependencies the controller should use, you can use the build in Dependency Injection (DI).
Most examples setup DI in your startup.cs ConfigureServices method.
The DI container allows 3 different lifetime states, in your case I guess you can try to add the HPOSubSimHolder as singleton.
I have no idea what HPOSubSimHolder is and what the implementation details are, hence its hard to tell if that'll work for you.
But it would be the "normal" way of setting this up ;)

How to inject an IServiceScope into a class in .NET Core

I have a problem with injecting Microsoft.Extensions.DependencyInjection.IServiceScope into my class.
My service implementation:
public class AccountService : IAccountService
{
private readonly IConfiguration _configuration;
private readonly IServiceScope _services;
public AccountService(
IConfiguration configuration,
IServiceScope services) // <-- I can't inject this
{
_configuration = configuration;
_services = services;
}
public async Task CreateAccount(ExternalAccount externalAccount)
{
(some code...)
}
}
In startup.cs:
services.AddTransient<IAccountService, AccountService>();
The problem is that project crashes after above line with following error:
Some services are not able to be constructed (Error while validating the service descriptor 'ServiceType: IDS.Quickstart.Account.IAccountService Lifetime: Transient ImplementationType: IDS.Quickstart.Account.AccountService': Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.IServiceScope' while attempting to activate 'IDS.Quickstart.Account.AccountService'.)
---> System.InvalidOperationException: Error while validating the service descriptor 'ServiceType: IDS.Quickstart.Account.IAccountService Lifetime: Transient ImplementationType: IDS.Quickstart.Account.AccountService': Unable to resolve service for type 'Microsoft.Extensions.DependencyInjection.IServiceScope' while attempting to activate 'IDS.Quickstart.Account.AccountService'.
What is the reason my code refuses to work?
Instead of injecting an IServiceScope, with MS.DI you inject an IServiceProvider. MS.DI will automatically inject a version of the IServiceProvider that is scoped to the current scope. This means that your class can call IServiceProvider.GetService which will lead to the same results as what would happen when you call IServiceScope.ServiceProvider.GetService.
Please be careful with the injection of container-specific abstractions such as IServiceProvider, IServiceScopeFactory, or IServiceScope. If injected into classes that live outside your Composition Root, it leads to the Service Locator anti-pattern. Service Locator comes with quite a few downsides. The name of your class, AccountService, makes me believe that this class lives outside your Composition Root.
Classes that live inside the Composition Root typically consist of solely infrastructure logic (no business logic). Within the Composition Root it is fine to have a dependency on the DI Container (or its abstractions) because this part of the already has a very strong dependency on that particular DI Container.
So if you can, extract the logic that requires to resolve instances from the AccountService class, and move that into a class that consists of solely infrastructure logic, and place it inside the Composition Root.

How to use DbContextPool with Singleton?

In my application I'm doing integrations using NMS and ActiveMQ.
I have some listeners that are singletons listening to some queues for messages.
Upon receiving a message, the listener should process it and log it on the database.
My DbContext is configured using the DbContextPool option:
services.AddEntityFrameworkSqlServer();
services.AddDbContextPool<MyContext>((serviceProvider, options) =>
{
options.UseSqlServer(connectionString);
options.UseInternalServiceProvider(serviceProvider);
});
So, when I try to inject the DbContext into my ActiveMqListener class, I get the error:
InvalidOperationException: Cannot consume scoped service 'MyApp.Data.MyContext' from singleton 'MyApp.Integrations.ActiveMqListener'.
How can I get one of the Contexts in the pool and free it once my work is done processing one message? Is there any other recommend way of doing this?
Thanks in advance.
According to the ASP.NET Core DI Service lifetimes documentation:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
By default AddDbContext or AddDbContextPool register the DbContext as Scoped service. You are consuming your DbContext in ActiveMqListener class which has been registered as Singleton service. That's the problem!
Solution is: register your ActiveMqListener to ASP.NET Core DI as ScopedService in Startup.ConfigureServices method.
Note: If you are obliged to use ActiveMqListener as Singleton then register your DbConext as Singleton too as follows:
services.AddDbContext<MyContext>((serviceProvider, options) =>
{
options.UseSqlServer(connectionString);
options.UseInternalServiceProvider(serviceProvider);
}, ServiceLifetime.Singleton); // <-- Here it is

Lifestyle Mismatches while injecting ISession with SimpleInjector

I have a domain service that has ISession as a dependency in the Ctor.
public JobCreator(IMapper mapper, ISession session)
{
_mapper = mapper;
_session = session;
}
The service is registered as Singleton
container.Register<IKindergardenCreator, KindergardenCreator>(Lifestyle.Singleton);
The ISession however is registered as scoped, since the session should be reopened more than once.
container.Register<ISession>(() => container.GetInstance<ISessionFactory>().OpenSession(),
Lifestyle.Scoped);
When I run the app I get a "Lifestyle mismatch" because of this configuration. What is the right way to configure a singleton service with non singleton service?
What is the right way to configure a singleton service with non singleton service?
The exception information refers to the following documentation
The documentation about lifestyle mismatches explains how to fix violations:
Change the lifestyle of the component to a lifestyle that is as short or shorter than that of the dependency.
Change the lifestyle of the dependency to a lifestyle as long or longer than that of the component.
Instead of injecting the dependency, inject a factory for the creation of that dependency and call that factory every time an instance is required.
Since you can't increase the lifestyle of the ISession dependency, you should either lower the lifestyle of your consuming component (the KindergardenCreator) or inject a factory for ISession instead.
You can lower the lifestyle of your component as follows:
container.Register<IKindergardenCreator, KindergardenCreator>(Lifestyle.Scoped);
You can also change the ISession dependency to Func<ISession> and register this as a factory as follows:
container.RegisterSingleton<Func<ISession>>(container.GetInstance<ISession>);

Categories