I'm having a huge problem with the configuration/dependency injection of an application.
I have a singleton class added through DI with AddSingleton, that has in its constructor a IRequestClient, that is scoped because
busConfigurator.AddRequestClient()
which among other things, has the same effect as AddScoped.
When I start the app, it says
"Cannot consume scoped service 'MassTransit.IRequestClient`1[...]' from singleton '...'.)"
Which absolutely makes sense.
The weirdest thing is that I have another app set up the exact same way, but it just works and I would really like for that class to remain singleton.
My colleague and I spent an entire day trying to find the differences between the two applications, but they are virtually the same in their configurations, so we are having trouble in understanding why one works while the other doesn't.
I'm not entirely sure on what details could be important to better define the problem, so feel free to ask.
We've looked all around the internet trying to find a solution, but it was always "Change singleton to transient", but that's not an option, first because it HAS to be a singleton, otherwise it wouldn't make sense in our app, as that thing is what caches lots of date from our db so we can't just go around keeping on collecting heaps of data, second because the first app works with singleton, not with transient and we'd like to keep it that way
// This method is called in Main()
private static void ConfigureMassTransit(IServiceCollection services)
{
services.AddMassTransit(busConfigurators =>
{
busConfigurators.AddRequestClient<ICacheRepository>();
busConfigurators.AddConsumers(typeof(Program).GetTypeInfo().Assembly);
busConfigurators.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri($"rabbitmq://{Config.Settings.RabbitMq_Host}"), hostConfigurator =>
{
hostConfigurator.Username(Config.Settings.RabbitMq_User);
hostConfigurator.Password(Config.Settings.RabbitMq_Password);
});
cfg.ReceiveEndpoint("myApp", e =>
{
e.ConfigureConsumers(context);
});
});
});
// CacheRepository
public class CacheRepository : ICacheRepository
{
private readonly IClient Client;
public CacheRepository(ICacheRepository client, ILogger<CacheRepository> logger)
{
this.client = client;
this.logger = logger;
}
}
When a dependency is scoped, the implication is that a new instance is needed for each scope (which is usually an incoming HTTP request or message.) It implies that the instance should not be re-used for multiple requests.
If you have a singleton that depends on that scoped dependency, that singleton will be created using an instance of that dependency (the request client.) Because that singleton "lives forever," so does the instance of the request client it contains.
The result is that the request client is not supposed to be re-used across different scopes, but now it is. One instance is used forever.
A likely solution is to modify the class that depends on that client so that it doesn't need to be a singleton. You mentioned that it has to be a singleton because it caches data.
How does it cache data? Does it do so by storing data in a private field? If so, perhaps you could make that field static. Now the class instance isn't re-used, but those fields are shared between instances. (Verify that interaction with those fields is thread safe if they may be accessed concurrently.)
Or if there's some other cache mechanism, you could move that into its own dependency and make that a singleton.
Then your class can be scoped. It will depend on the singleton cache, always using the same instance. It will also depend on the scoped request client, using a new instance for each scope.
You could inject IServiceProvider instead, and create a scope when the singleton needs to perform a request. That way, you're sticking to the expected use of the request client.
await using var scope = provider.CreateAsyncScope();
var client = scope.ServiceProvider.GetRequiredService<IRequestClient<T>>();
await client.GetResponse(...);
Related
I recently came across AddPooledDbContextFactory concept as part of my NET 5 self-education piece and am keen to implement it properly. However, I am not sure how to use it with generics that I generally use.
Example of my current setup:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<TestDbContext>(
(s, o) => o.UseNpgsql(Configuration.GetConnectionString("DatabaseConnection"))
.UseLoggerFactory(s.GetRequiredService<ILoggerFactory>()));
// other code //
}
my repository generic:
public class Repository<T> : IRepository<T> where T
{
private readonly TestDbContext _dbContext;
public Repository(TestDbContext dbContext)
{
_dbContext = dbContext;
}
public async Task Create(T entity)
{
await _dbContext.Set<T>().AddAsync(entity);
await _dbContext.SaveChangesAsync();
}
// other methods //
}
this is invoked in following manner as example:
public class WeatherForecastController : ControllerBase
{
private readonly IRepository<Test> testRepo;
public WeatherForecastController(IRepository<Test> testRepo)
{
this.testRepo= testRepo;
}
[HttpGet]
public async Task<IEnumerable<WeatherForecast>> GetAsync()
{
await testRepo.Create(new Test { Name = "Superman" });
// other code
}
}
I would like to convert this to use the new AddPooledDbContextFactory concept but cannot find enough documentation to figure out how to do this.
Atm only thing that comes to mind is using statements at each method but that doesn't make sense.
Any advice on this?
Documentation is not yet complete and is in progress, you track this issue
https://github.com/dotnet/EntityFramework.Docs/issues/2523
You can also a look at the tests for AddPooledDbContextFactory to see how to register DbContext with
https://github.com/dotnet/efcore/search?q=AddPooledDbContextFactory
for example to register DbContext:
services.AddPooledDbContextFactory<TContext>(ob =>
ob.UseSqlServer("ConnectionString").EnableServiceProviderCaching(false), poolSize: 32)
Then in your class, inject an IDbContextFactory<TContext> and use it like this:
using(var context = _factory.CreateDbContext())
{
var orders = await context.Orders.Where(o => o.Id > 3).ToListAsync();
}
According to this post:
Note that the DbContext instances created in this way are not managed
by the application's service provider and therefore must be disposed
by the application
You can also check out this post to see how to use IDbContextFactory:
https://learn.microsoft.com/en-us/aspnet/core/blazor/blazor-server-ef-core?view=aspnetcore-5.0
#Aeseir your code looks good to me. You are following best practices and you don't need to change it.
You are using the Repository Pattern, so your Repository class has all of your query logic which helps create loosely coupled and maintainable code.
In your ConfigureServices, calling: services.AddDbContext<TestDbContext>() registers TestDbContext with Scoped service lifetime. This is the way that DbContext is designed to work, and it will also work well with ASP.NET controllers, since they have a Scoped lifetime as well.
You did not show your code for registering IRepository, but that service lifetime should be Scoped as well. Btw, you can tell BuildServiceProvider() to validate scope registrations:
builder.Services.BuildServiceProvider(validateScopes: true);
Since DbContext is designed to have a Scoped service lifetime, and since your IRepository and Controller services are Scoped as well, every request gets brand new:
Controller
IRepository
DbContext
Those services are used for the request and then Diposed. This is how ASP.NET is intended to work.
Apparently at some point, DbContext pooling has been introduced to improve performance. In this case, EF Core manages a pool of context instances for you and resets them after each request. This can improve performance, although in some situations, the benefit might be small. See MSDN documentation for more details.
I think for use with ASP.NET controllers (i.e. the code you posted above) all you need to do to take advantage of EF Core context pooling is call AddDbContextPool():
builder.Services.AddDbContextPool<ApplicationDbContext>(/* ... */);
However, if you needed to use DbContext in services registered with Singleton lifetime, then the pattern above would not work well. Because when a Scoped service gets used in a Singleton service, the Scoped service is essentially a Singleton. Each request would not get a new DbContext, nor a reset one from the pool. (See QUESTION below.)
In that case, you might want to use the DbContext factory pattern instead:
builder.Services.AddDbContextFactory<ApplicationDbContext>(/* ... */);
Or, if you want to use context pooling with a factory pattern:
builder.Services.AddPooledDbContextFactory<ApplicationDbContext>(/* ... */);
The DbContextFactory can then be used in other services through constructor injection. For example:
private readonly IDbContextFactory<ApplicationDbContext> _contextFactory;
public MyController(IDbContextFactory<ApplicationDbContext> contextFactory)
{
_contextFactory = contextFactory;
}
The injected factory can then be used to construct DbContext instances in the controller code. For example:
public void DoSomething()
{
using (var context = _contextFactory.CreateDbContext())
{
// ...
}
}
Keep in mind that when you call CreateDbContext(), context instances are not managed by the service provider and therefore must be disposed by the application. Hence you need to Dispose of them yourself, such as in the example above which does so with the using statement.
QUESTION
I am doing my best to understand this stuff and explain it, but I might be wrong, so please call out an inaccuracies in my post.
When using AddDbContextPool(), does the DbContext get registered as a Singleton or Scoped?
I found in MSDN documentation that it's effectively registered as a Singleton:
Context pooling works by reusing the same context instance across requests; this means that it's effectively registered as a Singleton, and the same instance is reused across multiple requests (or DI scopes). This means that special care must be taken when the context involves any state that may change between requests.
However, I have found that if AddDbContextPool() is used along with true for validatedScopes:
builder.Services.BuildServiceProvider(validateScopes: true)
When DbContext is consumed from another service which is registered as a Singleton, the following exception is thrown:
System.InvalidOperationException: 'Cannot consume scoped service 'ApplicationDbContext' from singleton 'IRepository'.'
Hence why I stated above that DbContext still gets Scoped service lifetime.
I'm using a web app with Autofac injecting services into controllers. Those services are sometimes injected with other services, and repositories. Repositories are injected with DbContexts. These 3 layers (service, repository, context) are all registered with Autofac. My default lifetime for these is InstancePerLifetimeScope.
Unfortunately, I have some code in a specific controller that I want to execute in parallel threads. Since DbContext is not thread-safe, this means I need to give a factory method to each thread to resolve a Service in a per dependency lifetime scope, which in turn will need to resolve per dependency repositories and db contexts.
The options I am considering are to create a new lifetime scope per thread, or to use a separate registration using a named or keyed registration to resolve the per-dependency services.
The challenge with creating a new lifetime scope per thread is that I need access to some per-scope objects. Some objects would need to be inherited and to not have a new instance created in the new scope, but other objects (the non-thread-safe DbContexts) need to have new instances generated in the new scope. I have no idea how to control this behavior implicitly when creating my new lifetime scope.
The other method would be to use a registration key so that when I execute the factory method to resolve a service on each thread, it would resolve one in the per-dependency scope. This would work if the service had no dependencies, but since it depends on a bunch of repositories or services for which the default lifetime scope is set to InstancePerLifetimeScope, I have to write something like this:
builder.RegisterType<MyService>()
.As<IMyService>()
.Named<IMyService>(RegistrationKeys.PerDependency)
.WithParameter(new ResolvedParameter(
(pi, ctx) => pi.ParameterType == typeof(IMyRepository),
(pi, ctx) => ctx.ResolveNamed<IMyRepository>(RegistrationKeys.PerDependency))
).InstancePerDependency();
Since the repositories depend on the DbContext, each repository has to be registered separately using this registration name. And it needs to be configured to resolve the DbContext using the registration name. And the DbContext needs to be registered using the registration name.
With 10 services each using about 4-5 repositories, I wager the amount of boilerplate registration code I will have to write will be around 10-20 full pages. It's not going to be maintainable.
So my question is, is there a way to create a specific type of lifetime scope that will allow me to easily control which objects will have a new instance or which will be inherited from the parent lifetime scope that won't break the asp.net per-request lifetime scope?
Or is there a way I can register or resolve a service to explicitly resolve all of its dependencies in the same scope without relying on their default registrations and without having to hard code an entire second set of registrations for everything?
The challenge with creating a new lifetime scope per thread is that I need access to some per-scope objects. Some objects would need to be inherited and to not have a new instance created in the new scope, but other objects (the non-thread-safe DbContexts) need to have new instances generated in the new scope. I have no idea how to control this behavior implicitly when creating my new lifetime scope.
This is the challenge InstancePerRequest solve. You can create child scope and object scoped to Request will be shared amongst child scope. To do this, tagged lifetimescope and InstancePerMatchingLifetimeScope is used.
You can see InstancePerRequest and Tagging a lifetime scope in the official documentation.
Example :
builder.RegisterType<Service>().As<IService>().InstancePerMatchingLifetimeScope("KEY");
builder.RegisterType<DbContext>().InstancePerLifetimeScope();
// ...
using (ILifetimeScope scope = container.BeginLifetimeScope("KEY"))
{
scope.Resolve<IService>(); // Instance #1
using (ILifetimeScope childScope = scope.BeginLifetimeScope())
{
childScope.Resolve<DbContext>();
childScope.Resolve<IService>(); // shared instance (#1)
}
}
but that's mean you have to change all your InstancePerLifetimeScope to InstancePerMatchingLifetimeScope and can control the creation of the unit of work lifetime scope which can be quite difficult.
Another way of doing this is by using Owned<T> with Func<T>. You can get more information here : Owned instance
builder.RegisterType<Service>().As<IService>().InstancePerLifetimeScope();
builder.RegisterType<DbContext>().InstancePerLifetimeScope();
builder.RegisterType<Operation>().As<IOperation>().InstancePerLifetimeScope();
public class Operation : IOperation
{
public Operation(Func<Owned<DbContext>> contextFactory, IService service)
{
this._contextFactory = contextFactory;
this._service = service;
}
private readonly Func<Owned<DbContext>> _contextFactory;
private readonly IService _service;
public void Do()
{
using Owned<DbContext> context = this._contextFactory();
context.Value // => new instance
this._service // shared instance (#1)
}
}
using (ILifetimeScope scope = container.BeginLifetimeScope())
{
scope.Resolve<IService>(); // Instance #1
IEnumerable<IOperation> operations = scope.Resolve<IEnumerable<IOperation>>();
operations.AsParallel()
.ForAll(operation => operation.Do());
}
The only downside of this solution is that your service will have dependency on Autofac but if you don't want it, it is quite easy to create your own abstraction over Owned
If you don't want to use Owned<T> or your own abstraction instead of trying to make DbContext a special case you can reverse the problem and manually share some dependency between your custom scope.
Something like :
using ILifetimeScope childScope = scope.BeginLifetimeScope(b => {
b.Register<XContext>(c => scope.Resolve<XContext>()).ExternallyOwned();
});
var operation = childScope.Resolve<IOperation>();
operation.Do();
This way IOperation would be resolved in a new scope but XContext will be from parent scope
I am trying to understand the difference between AddSingleton, AddScoped and AddTransient. There are lots of explanations but I can't understand them because I don't understand when an HTTP request is within the same scope
So, there's always a "root container" that's shared by all consumers in the AppDomain or running process. Child containers are then created for each HTTP request (in ASP.NET Core, for each HttpContext which encompasses HttpRequest and HttpResponse). (Note that child containers can be created for other reasons too, but that's outside this answer's concern).
Singleton services are only constructed once, usually only by the root container. They're like the Singleton-pattern in OOP (where a class can only be instantiated once), except in this case you can still manually create multiple instances, but the DI container will only ever create 1 instance itself.
You can use OOP Singletons with DI containers by returning the OOP singleton instance from a service factory method.
Transient services are always created when they're requested - they're meant to be short-lived services. Some containers will call IDisposable.Dispose on all transient services it creates, others will not (as they expect the consumer to dispose of them, check with your container's policies).
Request-scoped services can be implemented differently by different container systems - but a common approach I see is that at the start of each HTTP request (when a new HttpContext is created) a child-container is created (a child-container inherits the registrations of its parent) and then all of the objects it creates (often as singletons, but only in that child container) are then disposed (if applicable) when the HTTP request ends (when the HttpContext is destroyed, after the HTTP response has been sent to the client and the response ended).
Disregarding ASP.NET entirely - let's pretend we have our own HTTP server program with its own DI container:
public class HttpServer
{
private readonly IContainer rootContainer;
public HttpServer()
{
this.rootContainer = RegisterServices( new ContainerBuilder() ).Build();
}
private static IContainerBuilder RegisterServices( IContainerBuilder services )
{
return services
.RegisterSingleton<ISystemClock,BiosClock>()
.RegisterSingleton<MySingleton>( factory: () => MySingleton.Instance )
.RegisterTransient<IDbConnection>( factory: () => new SqlConnection() )
.RegisterRequest<RequestTracingService>();
}
public void OnHttpRequest( Socket socket )
{
HttpContext context = new HttpContext();
context.RequestContainer = this.rootContainer.CreateChildContainer();
try
{
// hand-off the `context` object to code that reads the request, does processing, and then writes the response
}
finally
{
context.RequestContainer.Dispose(); // <-- this disposes of any objects created by RequestContainer during the processing of the request, without touching any objects created by `rootContainer`.
}
}
}
I need to keep an object in memory for the lifetime of the ASP.NET Core application in which I serialize this object and store it in durable storage as a snapshot. Later on in the life-cycle of the object I need to restore the state of this object by de-serializing it and replacing the singleton. It doesn't have to be a singleton but I need to guarantee that there's only a single instance of the object.
I tried the following but it seems it only replaces it for the current request and nothing after that. I assume the DI container makes it's own copy of the object but I'm not sure
internal static SingleObject SingleObject { get; set; } = new SingleObject();
services.AddSingleton<OrderBook>(SingleObject)
Is there a way to replace the singleton instance or will I have to roll my own singleton class that handles this scenario?
I would invite you to read How to configure services based on request in ASP.NET Core.
After that (container being built), the registrations can't be changed
anymore. #Tseng
You can only register scopes once during startup, so you could register functions related to what you want to update/refresh later on. I would recommend if you have something that needs to change implementing a cache or in memory storage that you trigger a refresh when desired.
I had a singleton of CachedData until realized needed to fast reset it at some point. So just implemented a wrapper (see below). Now the wrapper ResettableCachedData is singleton, and CachedData is transient. At any time a can do a quick Reset() to recreate CachedData, while accessing it's data via ResettableCachedData.Data.
public class ResettableCachedData
{
private readonly IServiceProvider _services;
public ResettableCachedData(IServiceProvider services)
{
_services = services;
Reset();
}
public CachedData Data { get; protected set; }
public void Reset()
{
Data = _services.GetRequiredService<CachedData>();
}
}
From https://learn.microsoft.com/en-us/aspnet/core/fundamentals/dependency-injection?view=aspnetcore-2.2:
Singleton
Singleton lifetime services are created the first time they're requested (or when ConfigureServices is run and an instance is specified with the service registration). Every subsequent request uses the same instance. If the app requires singleton behavior, allowing the service container to manage the service's lifetime is recommended. Don't implement the singleton design pattern and provide user code to manage the object's lifetime in the class.
So, you don't need to implement the Singleton pattern your self. The DI takes care of that.
The only thing you should do now is injecting it.
Register as follow:
services.AddSingleton<SingleObject>(new SingleObject())
On the logging samples in the documentation, there is an example how to inject a logger into a controller:
public class TodoController : Controller
{
private readonly ITodoRepository _todoRepository;
private readonly ILogger _logger;
public TodoController(ITodoRepository todoRepository,
ILogger<TodoController> logger)
{
_todoRepository = todoRepository;
_logger = logger;
}
}
Does the DI framework create a new logger each time I inject a logger into something like here? Is there a better way?
This is easily answered by a look into the source. When you do services.AddLogging(), the default behavior is that ILogger<T> is registered as a singleton:
public static IServiceCollection AddLogging(this IServiceCollection services, Action<ILoggingBuilder> configure)
{
// …
services.TryAdd(ServiceDescriptor.Singleton<ILoggerFactory, LoggerFactory>());
services.TryAdd(ServiceDescriptor.Singleton(typeof(ILogger<>), typeof(Logger<>)));
// …
}
So no, ILogger<T> instances for a certain type T are kept around for as long as the application is running. So when injecting an ILogger<TodoController> into your controller, the same logger instance will be passed to it every time.
Of course this only applies to the logger, but not your controller itself. By default, controllers are activated outside of DI but effectively live with a scoped lifetime. So on every request, there will be a new controller instance; but that one will then get the logger instance from before.
To answer your last question, is there a better way? No. Apart from the fact that this behavior is already a good one (since there’s no need for new logger instances), the proper way to use logging is indeed to inject ILogger<T> into types T, so you get a properly categorized logger instance. There’s really no need to worry about the very thin logger here when there are so many way more expensive things going on in the background that you will likely never see ;)
Since the ILogger<T> is a singleton, its instance will be reused all throughout the application. Note that this will not have an effect on logging scopes. The ILogger<T> implementation that you use within your application is actually just a thin wrapper that forwards logging calls to the internal loggers (which are also effectively singletons). So the lifetime of ILogger<T> is actually not relevant since they do not keep any state at all.
The logging scopes themselves are persisted using an AsyncLocal which is a mechanism to keep state throughout the asynchronous call flow. That means that logging scopes will just “magically” work and not leak outside of the call flow just because some instances happen to be shared between multiple threads (or asynchronous flows).