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>);
Related
With Autofac it pretty easy to create a child scope of the container and register addtional services. How would I achieve the same with the .net's dependency injection?
The IServiceProvider that I tried to inject to a class that needs to create child containers only offers the CreateScope() method that creates an IServiceScope that agian has only the ServiceProvider property and no way to register additional services. Is there anything else I should inject that would allow me to register more services with the container?
I don't know how Autofac works, but I can explain you how Microsoft DI works.
First of all, the Microsoft DI container is designed so that you have two main abstractions:
IServiceCollection: this is the object you use to register services in your applications. Think of this as the builder object for the actual DI container.
ServiceProvider: this is the actual DI container, that you obtain from the IServiceCollection object by invoking the IServiceCollection.BuildServiceProvider extension method. The behavior of this object is described by the IServiceProvider interface (ServiceProvider class implements the IServiceProvider interface)
So working with the DI container is a two steps operation: first of all you need an IServiceCollection object so that you can register services by specifying the implementing type and the lifetime (transient, scoped or singleton), then you can build a ServiceProvider and use it to resolve services in your application. The concrete type actually used for the IServiceCollection interface is the ServiceCollection class.
When you build the service collection in order to get a ServiceProvider instance, you actually get the root container for your application. It is called root container because you can create a hierarchy of service providers having its root in the root container of the application.
Given the root container for your application, in order to create a child container you need to create a scope, which is basically a scope used to resolve services. Each scope has its own container, which is an object implementing the IServiceProvider interface that you can use to resolve services inside of that scope.
This is the general pattern to do that:
// create the IServiceCollection instance
var services = new ServiceCollection();
// register services on the IServiceCollection instance
services.AddSingleton<IFooService, FooService>();
services.AddScoped<IBarService, BarService>();
services.AddTransient<IBuzzService, BuzzService>();
// create the root container
using var rootContaier = services.BuildServiceProvider();
// create a scope
using var scope = rootContainer.CreateScope();
// gets a reference to the container for the scope
var scopeContainer = scope.ServiceProvider;
// use the scope container to resolve services
var fooService = scopeContainer.GetRequiredService<IFooService>();
var barService = scopeContainer.GetRequiredService<IBarService>();
var buzzService = scopeContainer.GetRequiredService<IBuzzService>();
// do whatever you want with the resolved services
fooService.Foo();
barService.Bar();
buzzService.Buzz();
These dependency resolution scopes are really important because they define the lifetimes of the services resolved by using the scope service provider. These are the rules:
services registered with the singleton lifetime are always resolved by the application root container. Even if you use a child container to resolve a singleton service, the actual service resolution is delegated to the root container. Singleton services implementing the IDisposable interface are disposed when the root container is disposed, which usually happens at the application shutdown. Each singleton service is actually resolved once from the root container and the very same instance is reused for the entire lifetime of the application.
services registered with the transient lifetime are basically the opposite of a singleton service. A brand new instance is created each time the service is resolved. Given a scope, each time the scope service provider is used to resolve a transient service a brand new instance of the implementing type is created and tracked by the scope service provider. When the scope is disposed all the transient services that were resolved from that scope and having a disposable implementing type will be disposed too.
services registered with the scoped lifetime behave like singletons for the scope they were created from. If you have a scope and you use the scope service provider to resolve a scoped service, a brand new instance of the implementing type is created only the first time the service is resolved. All the subsequent requests to resolve the same service inside of the same scope will be fulfilled by reusing the instance of the implementing type created the first time the service was resolved in the scope. Scoped services are tracked by the scope: this is useful because a scoped service whose implementing type is disposable, will be disposed when the scope is disposed (same behavior as the transient services).
From the previous rules you can derive a fourth rule: never use the root container to resolve services, always create a scope and resolve services from that scope (remember to dispose the scope when you are done).
You should do that because scoped and transient services are tracked by the scope and disposed when the scope is disposed. If you try to resolve transient and scoped services from the root container they will be tracked by the root container and they will be disposed only when the root container is disposed: this usually happens at the application shutdown, because the lifetime of the root container is basically the application lifetime. Put in other words, you will create a memory leak if you are not compliant with the fourth rule.
Notice that if you use the overload of IServiceCollection.BuildServiceProvider taking a boolean parameter, you can ask the service collection to build a service provider which actually checks that scoped services are never resolved from the root container. This way you will get a partial check for the rule number four (only scoped services are checked for the dangerous resolution from the root container).
Going back to your question, the service registration phase is all done on the very same IServiceCollection instance and is not aware of the concept of service resolution scope. Scopes are only useful in the later stage of the services resolution, to define the lifetime of the resolved services as explained above.
Once you have built the root container from the service collection instance, the service registration phase is done and based on my knowledge you can only resolve services, no additional registrations will be allowed.
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 ;)
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.
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
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.