How to utilize Scoped Service inside Singleton Service - c#

I have a service name e.g. "Service1.cs" I have registered it as singleton, I have another Service "Service2.cs" and it is registered as Scoped. I want to inject "service2" into "Service1" But As we can't that its giving error, is there any way to consume scoped service into singleton service

As you know when use a scoped service in singleton service, that instance of scoped service behave like a singleton and alive in application and cause to memory leak. I think a good way is to use IServiceScopeFactory and get your scoped service and use it and dispose it like this:
public class SingletonService
{
private readonly IServiceScopeFactory _scopeFactory;
public SingletonService(IServiceScopeFactory scopeFactory)
{
_scopeFactory = scopeFactory;
}
public Task SampleMethod()
{
using var scope = _scopeFactory.CreateScope();
var scopedService = scope.ServiceProvider.GetRequiredService<IScopedService>();
//use scope service
//finally service will disposed because of using statement
}
}

Related

Dependency Injection and Service Lifetimes [duplicate]

In my Asp.Net Core App I need a singleton service that I can reuse for the lifetime of the application. To construct it, I need a DbContext (from the EF Core), but it is a scoped service and not thread safe.
Therefore I am using the following pattern to construct my singleton service. It looks kinda hacky, therefore I was wondering whether this is an acceptable approach and won't lead to any problems?
services.AddScoped<IPersistedConfigurationDbContext, PersistedConfigurationDbContext>();
services.AddSingleton<IPersistedConfigurationService>(s =>
{
ConfigModel currentConfig;
using (var scope = s.CreateScope())
{
var dbContext = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
currentConfig = dbContext.retrieveConfig();
}
return new PersistedConfigurationService(currentConfig);
});
...
public class ConfigModel
{
string configParam { get; set; }
}
What you're doing is not good and can definitely lead to issues. Since this is being done in the service registration, the scoped service is going to be retrieve once when your singleton is first injected. In other words, this code here is only going to run once for the lifetime of the service you're registering, which since it's a singleton, means it's only going to happen once, period. Additionally, the context you're injecting here only exists within the scope you've created, which goes away as soon as the using statement closes. As such, by the time you actually try to use the context in your singleton, it will have been disposed, and you'll get an ObjectDisposedException.
If you need to use a scoped service inside a singleton, then you need to inject IServiceProvider into the singleton. Then, you need to create a scope and pull out your context when you need to use it, and this will need to be done every time you need to use it. For example:
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}
Just to emphasize, again, you will need to do this in each method that needs to utilize the scoped service (your context). You cannot persist this to an ivar or something. If you're put off by the code, you should be, as this is an antipattern. If you must get a scoped service in a singleton, you have no choice, but more often than not, this is a sign of bad design. If a service needs to use scoped services, it should almost invariably be scoped itself, not singleton. There's only a few cases where you truly need a singleton lifetime, and those mostly revolve around dealing with semaphores or other state that needs to be persisted throughout the life of the application. Unless there's a very good reason to make your service a singleton, you should opt for scoped in all cases; scoped should be the default lifetime unless you have a reason to do otherwise.
Although Dependency injection: Service lifetimes documentation in ASP.NET Core says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But in your case this is not the issue. Actually you are not resolving the scoped service from singleton. Its just getting an instance of scoped service from singleton whenever it requires. So your code should work properly without any disposed context error!
But another potential solution can be using IHostedService. Here is the details about it:
Consuming a scoped service in a background task (IHostedService)
Looking at the name of this service - I think what you need is a custom configuration provider that loads configuration from database at startup (once only). Why don't you do something like following instead? It is a better design, more of a framework compliant approach and also something that you can build as a shared library that other people can also benefit from (or you can benefit from in multiple projects).
public class Program
{
public static void Main(string[] args)
{
CreateWebHostBuilder(args).Build().Run();
}
public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
WebHost.CreateDefaultBuilder(args)
.UseStartup<Startup>()
.ConfigureAppConfiguration((context, config) =>
{
var builtConfig = config.Build();
var persistentConfigBuilder = new ConfigurationBuilder();
var connectionString = builtConfig["ConnectionString"];
persistentStorageBuilder.AddPersistentConfig(connectionString);
var persistentConfig = persistentConfigBuilder.Build();
config.AddConfiguration(persistentConfig);
});
}
Here - AddPersistentConfig is an extension method built as a library that looks like this.
public static class ConfigurationBuilderExtensions
{
public static IConfigurationBuilder AddPersistentConfig(this IConfigurationBuilder configurationBuilder, string connectionString)
{
return configurationBuilder.Add(new PersistentConfigurationSource(connectionString));
}
}
class PersistentConfigurationSource : IConfigurationSource
{
public string ConnectionString { get; set; }
public PersistentConfigurationSource(string connectionString)
{
ConnectionString = connectionString;
}
public IConfigurationProvider Build(IConfigurationBuilder builder)
{
return new PersistentConfigurationProvider(new DbContext(ConnectionString));
}
}
class PersistentConfigurationProvider : ConfigurationProvider
{
private readonly DbContext _context;
public PersistentConfigurationProvider(DbContext context)
{
_context = context;
}
public override void Load()
{
// Using _dbContext
// Load Configuration as valuesFromDb
// Set Data
// Data = valuesFromDb.ToDictionary<string, string>...
}
}

Dependency injection Scoped into Transient and then Transient into Singleton

I have a scoped Context that is being accessed from transient service through method.
This transient service is being injected into singleton service.
Will my scoped Context become singleton or will it stay scoped?
public class Context : IContext
{
public string CorrelationId { get; set; }
public Context(string id)
{
CorrelationId = id;
}
}
Context accessor:
internal class RequestContextRegistrator : IRequestContextRegistrator
{
private IContext context;
public IContext RegisterContext(IContext context)
{
this.context = context;
return context;
}
public IContext Get()
{
return context ?? new Context()
{
CorrelationId = context.CorrelationId
};
}
}
And Singleton object:
public class QueueSender<TCommand>
{
private readonly IRequestContextRegistrator provider;
public QueueSender(IRequestContextRegistrator provider)
{
this.provider = provider;
}
public async Task Send(TCommand command)
{
var context = provider.Get();
var message = PrepareServiceBusMessage(command, userAgent, context?.CorrelationId);
}
}
The whole idea is to be able to pass around context id that is unique to the particular "request". The request is not coming from dotnet controller, it comes from queue receiver class.
Or to paraphrase it, how deep does this conversion to singleton goes for tree of dependency injections.
Do not resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests. It's fine to:
Resolve a singleton service from a scoped or transient service.
Resolve a scoped service from another scoped or transient service.
please see this link Service lifetime
Will my scoped Context become singleton or will it stay scoped?
It will stay scoped.
Your singleton instance will have a RequestContextRegistrator injected which will in turn have a Context injected; this Context instance will exist until your app terminates, as the singleton will preserve its reference, however, any other class that requires an IContext will have a new Context injected.

Create scope using IServiceProvider from singleton instance

Let's say I have my own class QueueListener<TService, TPayload> inherited from BackgroundService. It opens TCP connection and listens incoming messages. On each message I would like to initialize service of TService type and pass deserialized from JSON instance of TPayload to it. TService is going to be registered as Transient, so it means to be lightweight and stateless as a handler for payload have to be (in my current task). For this purpose I am going to inject IServiceProvider in constructor of my QueueListener and create a scope on each message it receives. Does it sounds like a plan or am I overengineering? I want to avoid TService is singleton as well.
Documentation says:
It's dangerous to resolve a scoped service from a singleton. It may cause the service to have incorrect state when processing subsequent requests.
But I am not completely sure what does it means. There is no way to inject scoped service in BackgroundService because it has Singleton lifetime. Do they warn me to stop doing things like I do?
UPD #1
I explain why I suppose to create scope on each message. The idea behind that is to prevent listener to be blocked by message processing and to provide other developers possibility to create their own handlers and do some stuff on received message. Other developers can create database connections for instance while processing and I want it to be closed and released when handling is done.
Register TService as scoped and create a new scope per message. Then resolve TService from created scope. Just read Consuming a scoped service in a background task
You can write it like this:
services.AddHostedService<MyBackgroundService>();
services.AddScoped<IScopedServicePerMessage, ScopedServicePerMessage>();
...
public class MyBackgroundService : BackgroundService
{
private readonly IServiceProvider _sp;
public MyBackgroundService(IServiceProvider sp)
{
_sp = sp;
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
DoWork(stoppingToken);
return Task.CompletedTask;
}
private void DoWork(CancellationToken stoppingToken)
{
while(true)
{
var msg = GetNextMessage();
using (var scope = _sp.CreateScope())
{
var servicePerMessage = scope.ServiceProvider.GetRequiredService<IScopedServicePerMessage>();
servicePerMessage.Handle(msg);
}
}
}
...
}
Regarding this:
It's dangerous to resolve a scoped service from a singleton. It may
cause the service to have incorrect state when processing subsequent requests.
It's about the case when you inject scoped service (ef core dbcontext, for instance) directly into singleton. It's not your case.
The documentation is referring to injecting a scoped service into a singleton service. Since the injection happens at the construction of the singleton object, the scoped service would be provided at that time. This will effectively increase the lifetime of the scoped service to that of a singleton service. This is dangerous because a scoped service lifetime is often chosen explicitly to ensure that the object gets disposed quickly again.
The most common example would be a database context which owns a database connection; you want to make sure that you free up this database connection as soon as possible to free up the resources. But if you injected the context into a singleton service, it would never get disposed.
That however does not mean that there is no way to consume scoped services within a singleton service. This is done by having the singleton service create a service scope from which it can then retrieve singleton services. It’s important though that this service scope is supposed to be short-lived. So take the example from ASP.NET Core itself, where a service scope is created for every request, and do something similar. For example in your case, you could do it for every incoming message if that makes sense for your application.
To create a service scope, you should inject an IServiceScopeFactory; you can then create a scope with it like this:
public async Task Process(TPayload payload)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var service = scope.GetService<TService>();
await service.Process(payload);
}
}
This pattern is strictly only necessary if you need to consume scoped services. You could resolve all other services directly without creating a scope. If you can reuse the same service instance to process all payloads, you could also inject the service as a singleton (same as registering it as transient but resolving it only once). If you need a fresh instance for every payload, then consider creating a scope even if it isn’t strictly necessary.
First, transient services are not scoped services. Transient services are usually externally owned by your code and are created each time they are resolved from the container. Container does not cache transient services.
TService is going to be registered as Transient ... For this purpose I am going to inject IServiceProvider in constructor of my QueueListener and create a scope on each message it receives.
You do not need a scope for resolving transient services. Even if you create a scope, the scope still does not manage / own transient services. That, for example, ending the lifetime of the scope does not end lifetime of transient services.
You could simply use the IServiceProvider injected in QueueListener to resolve TService. And each TService resolved should be already like what you want
lightweight and stateless as a handler for payload
With regards to
Documentation says:
What the document says might not be relevant now since you are not using scoped services. But in case you want to know the reason:
It's dangerous to resolve a scoped service from a singleton.
Singleton is a special kind of scope. Singleton services are created and cached within a "root" scope of the container, which is essentially the container itself.
If you resolve scoped service from singleton, the lifetime / scope where the service instance is resolved and cached is likely to be the "root" scope. This leads to a problem where the scoped service instance being cached inside the container, and shared across multiple client requests.
This is dangerous, because scoped services are supposed to be
Scoped lifetime services (AddScoped) are created once per client request (connection).
I don't wanted my singleton class depending on the IServiceProvider directly.
So I've used a custom factory to accomplish this goal.
May this code example help others:
public class Startup
{
// ...
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IScopedBar, ScopedBar>();
services.AddSingleton<IScopedServiceFactory<IScopedBar>, ScopedServiceFactory<IScopedBar>>(
(provider) => {
var scope = provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IScopedBar>();
return new ScopedServiceFactory<IScopedBar>(() => new ScopedService<IScopedBar>(scope, service));
});
services.AddSingleton<ISingletonFoo, SingletonFoo>();
}
// ...
}
public interface ISingletonFoo
{
void DoSomethingUsingScopedServices();
}
public class SingletonFoo : ISingletonFoo
{
private readonly IScopedServiceFactory<IScopedBar> _barFactory;
public SingletonFoo(IScopedServiceFactory<IScopedBar> barFactory)
{
_barFactory = barFactory;
}
public void DoSomethingUsingScopedServices()
{
using var scopedService = _barFactory.CreateService();
scopedService.Service.DoSomething();
}
}
public interface IScopedBar
{
void DoSomething();
}
public class ScopedBar : IScopedBar
{
public void DoSomething()
{
// Do something
}
}
public interface IScopedService<T> : IDisposable
{
T Service { get; }
}
public interface IScopedServiceFactory<T>
{
IScopedService<T> CreateService();
}
public class ScopedService<T> : IScopedService<T>
{
private readonly IDisposable _scope;
public ScopedService(IDisposable scope, T service)
{
_scope = scope;
Service = service;
}
public T Service { get; }
public void Dispose()
{
_scope.Dispose();
}
}
public class ScopedServiceFactory<T> : IScopedServiceFactory<T>
{
private readonly Func<IScopedService<T>> _serviceFactory;
public ScopedServiceFactory(Func<IScopedService<T>> serviceFactory)
{
_serviceFactory = serviceFactory;
}
public IScopedService<T> CreateService()
{
return _serviceFactory();
}
}
Here is my code to consume scoped service:
public interface IScopedResolver<T> where T: class
{
TResult Resolve<TResult>(Func<T, TResult> dataFactory);
Task<TResult> ResolveAsync<TResult>(Func<T, Task<TResult>> dataFactory);
}
Implement class:
public class ScopedResolver<T> : IScopeResolver<T> where T: class
{
private readonly IServiceProvider _provider;
public ScopedResolver(IServiceProvider provider)
{
_provider = provider;
}
public TResult Resolve<TResult>(Func<T, TResult> dataFactory)
{
using IServiceScope scope = _provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<T>();
return dataFactory(service);
}
public async Task<TResult> ResolveAsync<TResult>(Func<T, Task<TResult>> dataFactory)
{
using var scope = _provider.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<T>();
return await dataFactory(service);
}
}
Register at startup:
services.AddSingleton(typeof(IScopedResolver<>), typeof(ScopedResolver<>));
using ScopedResolve:
public class ServiceA
{
private readonly IScopedResolver<DbContext> _context;
public ServiceA(IScopedResolver<DbContext> context)
{
_context = context;
}
public async Task<List<ClassOne>> GetListAsync()
{
return await _context.ResolveAsync(async s => await s.Set<ClassOne>().ToListAsync());
}
}

ASP.NET Core Web App DI Error - Some services are not able to be constructed (Error while validating the service descriptor

I am creating a ASP.NET Core web application. I am using a Repository through a library project. I reference it in the web application project.
The repository interface is as below:
public interface IPushNotificationRepository
{
IQueryable<PushNotification> Notifications
{
get;
}
IQueryable<Client> Clients
{
get;
}
void Add(PushNotification notification);
void Add(Client client);
void AddRange(IList<PushNotification> notifications);
bool AddIfNotAlreadySent(PushNotification notification);
void UpdateDelivery(PushNotification notification);
bool CheckIfClientExists(string client);
Client FindClient(int? id);
void Update(Client client);
void Delete(Client client);
}
Within the repository I inject the db context
public class PushNotificationRepository : IPushNotificationRepository
{
private readonly PushNotificationsContext _context;
public PushNotificationRepository(PushNotificationsContext context)
{
_context = context;
}
}
The configure services of the start up class is as below:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddSingleton<IPushNotificationRepository, PushNotificationRepository>();
services.AddDbContextPool<PushNotificationsContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("PushNotificationsConnection")));
}
In the controller class I consume the repository:
public class ClientsController : Controller
{
//private readonly PushNotificationsContext _context;
private readonly IPushNotificationRepository _pushNotificationRepository;
public ClientsController(IPushNotificationRepository pushNotificationRepository)
{
_pushNotificationRepository = pushNotificationRepository;
}
}
The repository classes are in a separate library project which is referenced by the web application project. The error I receive is:
System.AggregateException: 'Some services are not able to be
constructed (Error while validating the service descriptor
'ServiceType:
Services.Messaging.Data.Abstract.IPushNotificationRepository Lifetime:
Singleton ImplementationType:
Services.Messaging.Data.PushNotificationRepository': Cannot consume
scoped service 'Services.Messaging.Data.PushNotificationsContext' from
singleton
'Services.Messaging.Data.Abstract.IPushNotificationRepository'.)'
Would really appreciate some advise on this
A singleton cannot reference a Scoped instance. The error message is clear.
Cannot consume scoped service
'Services.Messaging.Data.PushNotificationsContext' from singleton
PushNotificationsContext is considered as a scoped service. You should almost never consume scoped service or transient service from a singleton. You should also avoid consuming transient service from a scoped service. Consuming scoped services it's a good practice to inject what you need, it gets cleaned up after the request automatically.
Either
services.AddTransient < IPushNotificationRepository, PushNotificationRepository>();
or
services.AddScoped< IPushNotificationRepository, PushNotificationRepository>();
will work fine, but check your design. Maybe this is not the behaviour you are looking for.
services.AddDbContext<PushNotificationsContext>() registers the PushNotificationsContext as a service with ServiceLifetime.Scoped which means that your PushNotificationsContext is created per web request. It is disposed when request is completed.
You could inject IServiceScopeFactory which is singleton into your repository, then create a new scope using CreateScope() and request the PushNotificationsContext service from that scope
public class PushNotificationRepository : IPushNotificationRepository
{
IServiceScopeFactory _serviceScopeFactory;
public PushNotificationRepository(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public void Add(PushNotification notification);
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var context = scope.ServiceProvider.GetRequiredService<PushNotificationsContext>();
//other logic
}
}
}
Refer to c# - DataContext disposed in ASP.NET Core scheduler

How to manage lifetime scopes inside WCF services without using ILifetimeScope directly?

Here's my simple WCF service which was integrated with Autofac.Wcf:
[ServiceContract]
public class MyService
{
private readonly IResponseBuilder _responseBuilder;
private readonly IEnumerable<IOperation> _operations;
public MyService(IResponseBuilder responseBuilder, IEnumerable<IOperation> operations)
{
_responseBuilder = responseBuilder;
_operations = operations;
}
[OperationContract]
public object Execute()
{
foreach (var operation in _operations)
{
operation.Execute();
}
return _responseBuilder.Build();
}
}
I want all operations to use the same instance of IResponseBuilder to build the result. Example of an operation:
public class AlwaysFailOperation : IOperation
{
private readonly IOperationResultBuilder _resultBuilder;
public AlwaysFailOperation(IOperationResultBuilder resultBuilder)
{
_resultBuilder = resultBuilder;
}
public void Execute()
{
_resultBuilder.Fail();
}
}
I know that it can be achieved with ILifetimeScope or IContainer inside MyService, but I want to keep all container work in composition root.
Please help me with this requirements. Here's my current registrations:
public static void AppInitialize()
{
var builder = new ContainerBuilder();
builder.RegisterType<MyService>();
builder.RegisterType<ResponseBuilder>()
.AsImplementedInterfaces();
builder.RegisterType<AlwaysFailOperation>()
.AsImplementedInterfaces();
AutofacHostFactory.Container = builder.Build();
}
InstancePerLifetimeScope should work, but you'll have to test it in your particular scenario.
Due to restrictions with the internals of WCF there is no per request lifetime in WCF. See the bold comment at top of docs. WCF is very limiting with respect to its DI integration points.
Autofac creates an instance context for each instance of the service. That context contains a lifetime scope from which the service instance is resolved. The scope is not named because for singleton services the instance context lives for the life of the service - that's controlled by WCF, not Autofac.
So, hypothetically, if you made your service instance per request in WCF and registered the dependency as instance per lifetime scope you should get the result you want. But, as always, test, test, test. Sometimes getting the right WCF setup with the right Autofac lifetime registrations can be tricky, especially in a complex system.

Categories

Resources