Resolve scoped service from singleton service - asp.net-core

Is it somehow possible to resolve a scoped service in a singleton service's method being called by a scoped one?
E.g. i have a singleton service "GlobalService" and a scoped one "UserService".
If the UserService executes a method "Job" in "GlobalService", is it somehow possible to get scoped services in this method by using Assembly.GetCallingAssembly()? Otherwise I need to pass all the required parameters.
Thank you ✌

#DeepkaMishra's answer won't work in all scenarios.
I used it myself in blazor webassembly loggingprovider and httpcontext came as null.
For more details, read this, just adding quoted text here.
Think of HttpContext as a telephone call. If you pick the phone up
when no-one has called then there is no context i.e. it is null. When
someone does call then you have a valid context. This is the same
principal for a web call. The Configure method in Startup is not a web
call and, as such, does not have a HttpContext.
Working solution, I found is provided in this.
public class PersistedConfigurationService : IPersistedConfigurationService
{
private readonly IServiceProvider _serviceProvider;
public PersistedConfigurationService(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task Foo()
{
using (var scope = _serviceProvider.CreateScope())
{
//here you can get the scoped service
var context = scope.ServiceProvider.GetRequiredService<IPersistedConfigurationDbContext>();
// do something with context
}
}
}

Singleton would have one single instance which can be used by your scoped service. Your scoped service method can use singleton service instance.
If you call a singleton service's method, you can get the scoped service object in it. You can use IHttpcontextAccessor to resolve the scoped service instance inside that method.
internal class Singleton
{
private readonly IHttpContextAccessor httpContextAccessor;
public Singleton(IHttpContextAccessor httpContextAccessor)
{
this.httpContextAccessor = httpContextAccessor;
}
public int Job()
{
return httpContextAccessor.HttpContext.RequestServices.GetRequiredService<Scoped>().MyProperty;
}
}
You would need to register these service in Startup's ConfigureServices method:
services.AddHttpContextAccessor();
services.AddScoped<Scoped>();
services.AddSingleton<Singleton>();

Related

Creating Singleton CacheManager in Asp.Net Core

I am trying to create Singleton CacheManager class that has dependency on IMemoryCache.
public class CacheManager:ICacheManager
{
private readonly IMemoryCache _cache;
public CacheManager(IMemoryCache cache)
{
_cache = cache;
}
public void LoadCache(MyData data)
{
// load cache here at startup from DB
}
}
I also have a Scoped service that retrives data from the database
public class LookupService:ILookupService
{
private readonly MyDatabaseContext _dbContext;
public class LookupService(MyDatabaseContext dbContext)
{
_dbContext = dbContext;
}
public void Dispose()
{
//Dispose DBContext here
}
// some async methods that returns lookup collection
}
Register these services in Startup.cs
public void ConfigureServices(IServiceCollection services)
{
// EF
services.AddDbContext<MyDatabaseContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
// domain services
services.AddScoped<ILookupService, LookupService>();
services.AddMemoryCache();
// singleton
services.AddSingleton<CacheManager>(sp=>
{
using(var scope = sp.CreateScope())
{
using (var service = scope.ServiceProvider.GetService<ILookupService>())
{
how do i create cacheManager instance by injecting IMemoryCache and also register callback function
}
}
});
}
ILookupService is registered as Scoped service becuase it has dependency on DBContext which is also (by default) registered with Scoped lifetime. I do not want to change lifetime of these services.
However I want CacheManager to be registered as Singleton, that means I cannot inject ILookupService as dependency into CacheManager.
So here is my possible solution to create & register singleton instance of CacheManager
services.AddSingleton<CacheManager>(sp=>
{
using(var scope = sp.CreateScope())
{
using (var lookupService = scope.ServiceProvider.GetService<ILookupService>())
{
var cache = scope.ServiceProvider.GetService<IMemoryCache>();
var manger = new CacheManager(cache);
manger.LoadCache(lookupService.GetData());
return manger;
}
}
});
Not sure this is the best way to create CacheManager. How do I implement a callback function to re-populate CacheEntry if it becomes null?
I guess I would simply configure services.AddSingleton<CacheManager>();
(CacheManager having a default constructor)
After configuring all of the DI dependencies and having a serviceprovider, get the Cachemanager singleton and initialize it with LoadCache.
(so let DI create "empty" singleton cachemanager, but initialize immediately somewhere in startup of application)
var cachemanager = scope.ServiceProvider.Get<CacheManager>();
var lookupService = scope.ServiceProvider.Get<ILookupService>();
var cache = scope.ServiceProvider.Get<IMemoryCache>();
cachemanager.Cache = cache;
cachemanager.LoadCache(lookupService.GetData());
Looks like the underlying issue is that ILookupService cannot be resolved until runtime and requests start coming in. You need to create CacheManager before this.
DI COMPOSITION
This should be done when the app starts - as in this class of mine. Note the different lifetimes for different types of object but I just focus on creating the objects rather than interactions.
DI RESOLUTION
.Net uses a container per request pattern where scoped objects are stored against the HttpRequest object. So a singleton basically needs to ask for the current ILookupService, which is done by calling:
container.GetService<ILookupService>
So include the DI container as a constructor argument to your CacheManager class and you will be all set up. This is the service locator pattern and is needed to meet your requirement.
An alternative per request resolution mechanism is via the HttpContext object as in this class, where the following code is used:
IAuthorizer authorizer = (IAuthorizer)this.Context.RequestServices.GetService(typeof(IAuthorizer));
SUMMARY
The important thing is to understand the above design pattern, and you can then apply it to any technology.
register Cache service as singleton, try below code
public class CacheService : ICacheService
{
private ObjectCache _memoryCache;
/// <summary>
/// Initializes a new instance of the <see cref="CacheService"/> class.
/// </summary>
public CacheService()
{
this._memoryCache = System.Runtime.Caching.MemoryCache.Default;
}
}

c# asp.net core 3 calling different methods from the controller, depending on the request body

I have a controller with the following content (simplified version):
[HttpPost]
public Task<OkResult> Post([FromBody] commonRequest)
{
parser.DoWork(commonRequest);
return Ok();
}
The commonRequest object is populated from the incoming JSON request.
The parser.DoWork method should invoke the creation of a new instance of the class, depending on requestBody.
Here's what it looks like:
public class CommonParser : ICommonParser
{
private readonly ILogger<CommonParser> logger;
private IServiceProvider serviceProvider;
public CommonParser(ILogger<CommonParser> _logger, IServiceProvider _serviceProvider)
{
this.logger = _logger;
this.serviceProvider = _serviceProvider;
}
public void DoWork(CommonRequest commonRequest)
{
ICommonParser parser = (ICommonParser)Activator.CreateInstance(Type.GetType(commonRequest.instance)
, serviceProvider);
parser.DoWork(commonRequest);
}
}
I have three classes whose names are passed through commonRequest.instance. All of these classes implement the ICommonParser interface. Inside these classes, I pass a serviceProvider so that they can get the ILogger inside themselves and use it.
Here is an example constructor of this class:
private readonly ILogger<Parser1> logger;
public Parser1(IServiceProvider serviceProvider)
{
this.logger = serviceProvider.GetRequiredService<ILoggerFactory>().CreateLogger<Parser1>();
}
As a result, I can send only one message in this way. On the second call, I get a message that serviceProvider.GetRequiredServiceILoggerFactory () has been destroyed.
Please tell me what to do in such cases. I think I'm designing wrong.
From Dependency Injection in ASP.NET Core:
Avoid using the service locator pattern. For example, don't invoke
GetService or GetRequiredService to obtain a service instance when you
can use DI instead.
1) register the logger factory or the logger service, in case of the logger factory
services.AddSingleton<ILoggerFactory, LoggerFactory>();
2) use constructor injection to inject logger factory into the constructor
public Parser1(ILoggerFactory loggerFactory)
{
}
3) you might create a new interface for the parsers (parser1, 2, 3). The parsers implement this interface. Register them as services
public interface IParser
{
void DoWork(CommonRequest commonRequest);
}
services.AddTransient<Parser1>(); // implements IParser
services.AddTransient<Parser2>();
This post gives an answer how to resolve classes implementing the same interface. For getting parser with DI you will actually need IServiceProvider:
_serviceProvider.GetService<Parser1>();

Autofac, thread and ISErviceScopeFactory

I have a problem: the service provider is disposed when arrives in the method.
Is this an issue or it is my fault?
My service
public class BomService
{
private readonly IServiceScopeFactory _scope;
public BomService(IServiceScopeFactory scope)
{
_scope = scope;
}
public void ImportAsync(ImportRequestDto importSettings)
{
Task.Run(async () => await ImportFile.ImportAsync<Bom, CatalogContext>(_scope));
}
}
Method
public static async Task ImportAsync<T, TContext>(IServiceScopeFactory parentScope) where T : class where TContext : DbContext
{
using var scope = parentScope.CreateScope();
var repo = scope.ServiceProvider.GetService<IGenericRepository<T, TContext>>();
}
The error:
Instances cannot be resolved and nested lifetimes cannot be created from this LifetimeScope as it has already been disposed
Do not run long running tasks in an HTTP request, these should be done in a separate process while returning a response to the client immediately.
Refer to the answer of this question
As also suggested there you can use something like Hangfire to run background processes.
Update
It is not good practice to inject IServiceScopeFactory. Like that you are implementing the Service Locator anti pattern. Instead inject the repository directly and let the DI figure out the resolution and scope.

Mediatr Scope problems

I am using Mediatr to handle messages from a queue. I can get a simple example to work. However I have run into problems when I try to inject an object into my handler
public class MessageCommandHandler : IRequestHandler<MessageCommand, bool>
{
private IMyDependency myDependency;
public MessageCommandHandler(IMyDependency myDependency)
{
this.myDependency = myDependency;
}
public Task<bool> Handle(MessageCommand request, CancellationToken cancellationToken)
{
return Task.FromResult(true);
}
}
This only works when I register IMyDependency as a transient scope, however when I register it as scoped lifetime it fails with the error
Cannot resolve 'MediatR.IRequestHandler`2[MyNamespace.MessageCommand,System.Boolean]' from root provider because it requires scoped service 'MyNamespace.IMyDependency'
I need to be able to inject dependencies with scoped lifetime. Has anyone got a solution for this.
I am using the .NET Core dependency injection framework. It is setup as follows
services.AddHostedService<QueueConsumer>();
services.AddScoped<IMyDependency, MyDependency>();
services.AddMediatR(new Assembly[] { Assembly.GetExecutingAssembly() });
Any ideas?
Any time you use a dependency with a Scoped lifetime, you will need to use it inside a pre-created scope. In the case of MVC this would happen automatically behind the scenes but if you're using direct from your own code, say via a console application or something, you will need to create the scope yourself.
This can be done by injecting an instance of IServiceScopeFactory and then using this factory to create a scope and then retrieve the dependency from that scope e.g.
public class MessageCommandHandler : IRequestHandler<MessageCommand, bool>
{
private IServiceScopeFactory _serviceScopeFactory;
public MessageCommandHandler(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public Task<bool> Handle(MessageCommand request, CancellationToken cancellationToken)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
var scopedServices = scope.ServiceProvider;
var myDependency = scopedServices.GetRequiredService<IMyDependency>();
return Task.FromResult(true);
}
}
}
However (and note that the code above is untested), in my own systems I would almost always create the scope around whatever is sending the mediator request in which case any Scoped dependencies will still be injected automatically at this scope e.g.
... // some other calling class / Main method etc..
using (var scope = _serviceScopeFactory.CreateScope())
var mediator = scope.ServiceProvider.GetRequiredService<IMediator>();
mediator.Send(new MessageCommand());
}

Asp.Net Core Middleware service dependent on current User

I would like to either change a request scoped service or set one in a custom middleware layer.
Specifically, I want to be able to do something like the below contrived example in Startup.cs:
public void ConfigureServices(IServiceCollection service)
{
service.AddScoped<IMyUserDependentService>((provider) => {
return new MyService());
});
}
public void Configure(...) {
//other config removed
app.Use(async (context, next) => {
var myService = context.ApplicationServices.GetService<IMyUserDependentService>();
myService.SetUser(context.User.Identity.Name);//Name is Fred
next.Invoke();
});
}
Then in the controller do this:
public class HomeController: Controller
{
public HomeController(IMyUserDependentService myService)
{
//myService.UserName should equal Fred
}
}
The problem is, that this doesn't work. myService.UserName isn't Fred in controller, it's null. I think that the IOC container is creating a new instance in the controller, and not using the one set in the middleware.
If I change the scope of the service to Transient, Fred is remembered, but that doesn't help because the service is dependent on who the current user is.
To recap, what I need is to create/or edit a service that requires the current user (or other current request variables), but am unable to work this out.
Thanks in advance!
Have you tried using context.RequestServices?
I just ran into a similar issue, I got an error like
InvalidOperationException: Cannot resolve scoped service 'IScopedService' from root provider., the exception thrown was very not well documented.
Here is how I solved it:
[Startup.cs]
services.AddScoped<IAnyScopedService, AnyScopedService>();
services.AddSingleton<ISomeOtherSingletonService, SomeOtherSingletonService>();
[MyMiddleware.cs]
public sealed class MyMiddleware
{
private readonly RequestDelegate _next;
private readonly ISomeOtherSingletonService _Svc;
public MyMiddleware(
RequestDelegate next,
ISomeOtherSingletonService svc)
{
_next = next;
_Svc = svc;
}
public async Task Invoke(HttpContext context,
IAnyScopedService scopedService)
{
// Some work with scoped service
}
}
Indeed the Middleware is instanciated only once, but called many times.
The constructor takes therefore singleton instances, where the invoke method can get scoped injected parameters.
More details on Mark Vincze post