Autofac, thread and ISErviceScopeFactory - asp.net-core

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.

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;
}
}

Implementing service worker in existing ASP.NET Core MVC application

I'm developing an ASP.NET Core MVC web application where I have these two tasks that should be running as background services:
Set the user status as "Expired" if EndOfSubscription date is == DateTime.Now
Before 1 month of EndOfSubscription date send a reminder e-mail to this user
After searching, I found that I can use service worker to implement this. But I'm totally confused how to use this service worker in existing ASP.NET Core MVC web application where I need to access my models and database.
Should I isolate these tasks in a separate service worker project? But in this case should I share the same database for both projects?
Can someone guide me with main steps in this kind of situations?
Thank you in advance.
Service worker or Worker service?
A Service Worker is a way to run background tasks in a browser and definitely unsuitable if you want to execute something on the server.
A Worker service is essentially a template with the (few) calls needed to run a BackgroundService/IHostedService in a console application and (optionally, through extensions) as a Linux daemon or Windows service. You don't need that template to create and run a BackgroundService.
The tutorial Background tasks with hosted services in ASP.NET Core shows how to create and use a BackgroundService but is a bit ... overengineered. The article tries to show too many things at the same time and ends up missing some essential things.
A better introduction is Steve Gordon's What are Worker Services?.
The background service
All that's needed to create a background service, is a class that implements the IHostedService interface. Instead of implementing all the interface methods, it's easier to inherit from the BackgroundService base class and override just the ExecuteAsync method.
The article's example shows this method doesn't need to be anything fancy:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
await Task.Delay(1000, stoppingToken);
}
}
}
That's just a loop with a delay. This will run until the web app terminates and signals the stoppingToken. This service will be created by the DI container, so it can have service dependencies like ILogger or any other singleton service.
Registering the service
The background service needs to be registered as a service in ConfigureServices, the same way any other service is registered. If you have a console application, you configure it in the host's ConfigureServices call. If you have a web application, you need to register it in Startup.ConfigureServices:
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<OrdersContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
...
//Register the service
services.AddHostedService<Worker>();
services.AddRazorPages();
}
This registers Worker as a service that can be constructed by the DI container and adds it to the list of hosted services that will start once .Run() is called in the web app's Main :
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
Using DbContext and other scoped services
Adding a DbContext as a dependency is trickier, since DbContext is a scoped service. We can't just inject a DbContext instance and store it in a field - a DbContext is meant to be used as a Unit-of-Work, something that collects all changes made for a single scenario and either commit all of them to the database or discard them. It's meant to be used inside a using block. If we dispose the single DbContext instance we injected though, where do we get a new one?
To solve this, we have to inject the DI service, IServiceProvider, create a scope explicitly and get our DbContext from this scope:
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IServiceProvider _services;
//Inject IServiceProvider
public Worker(IServiceProvider services, ILogger<Worker> logger)
{
_logger = logger;
_services=services;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
//Create the scope
using (var scope = _services.CreateScope())
{
//Create OrdersContext in the scope
var ctx = scope.ServiceProvider.GetRequiredService<OrdersContext>();
var latestOrders = await ctx.Orders
.Where(o=>o.Created>=DateTime.Today)
.ToListAsync();
//Make some changes
if (allOK)
{
await ctx.SaveChangesAsync();
}
}
//OrdersContext will be disposed when exiting the scope
...
}
}
}
The OrdersContext will be disposed when the scope exits and any unsaved changes will be discarded.
Nothing says the entire code needs to be inside ExecuteAsync. Once the code starts getting too long, we can easily extract the important code into a separate method :
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
_logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
using (var scope = _services.CreateScope())
{
var ctx = scope.ServiceProvider.GetRequiredService<OrdersContext>();
await DoWorkAsync(ctx,stoppingToken);
}
await Task.Delay(1000, stoppingToken);
}
}
private async Task DoWorkAsync(OrdersContext ctx,CancellationToken stoppingToken)
{
var latestOrders = await ctx.Orders
.Where(o=>o.Created>=DateTime.Today)
.ToListAsync();
//Make some changes
if (allOK)
{
await ctx.SaveChangesAsync();
}
}

Resolve scoped service from singleton service

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>();

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());
}

How does Nservicebus handle nested containers on structuremap?

TL;DR; some property is not being injected into a service when it should. How does Nservicebus handle nested containers on structuremap?
Hey guys new here. I'm currently upgrading our project to NSB6. I have a couple of question on dependency injection with Structuremap.
We have some services that use to use ISendOnlyBus, which we call from inside a handler. For test purposes(I just want to get the handler working) I changed those to IMessageHandlerContext, and at the beginning of the handler I'm injecting the context to the container. It works as expected, IMessageHandlerContext is resolved on these services and I'm able to use it.
public class MyMessageHandler : IHandleMessage<IMyMessage>
{
private IContainer _container;
private ISomeService _someService;
public MyMessageHandler (IContainer container, ISomeService someService)
{
_container = container;
_someService = someService;
}
public async Task Handle(IMyMessage message, IMessageHandlerContext context)
{
_container.Inject(typeof(IMessageHandlerContext), context);
_someService.DoSomething();
}
}
public class SomeService : ISomeService
{
private IMessageHandlerContext _context;
public SomeService(IMessageHandlerContext context)
{
_context = context;
}
public void DoSomething()
{
_context.Send<ISomeMessage>(x => {
//... init message
});
}
}
Now the issue is we have an Nhibernate custom IPostUpdateEventListener over an entity that sends nservicebus messages. On version 5 it had an ISendOnlyEndpoint that it used for sending, same as the other services, but this time the context is not being resolved by Structuremap. I'm wondering how's and if nservicebus manages nested containers inside message handlers. I cannot seem to find any documentation for it.
Child containers within StructureMap do not support some of the features required by NServiceBus containers. Policies are used to configure the injection, and policies are not supported in child containers.
Policies are only applied to root container in StructureMap
Here is an issue in the NServiceBus.StructureMap repo to track it.
I'm guessing the suggested refactor of this would be
public class MyMessageHandler : IHandleMessage<IMyMessage>
{
public async Task Handle(IMyMessage message, IMessageHandlerContext context)
{
context.Resolve<ISomeService>().DoSomething(context);
}
}
public class SomeService : ISomeService
{
public void DoSomething(IMessageHandlerContext context)
{
context.Send<ISomeMessage>(x => {
//... init message
});
}
}
context.Resolve would be be an extension you write to access the container instance in the context instance.
However if you wanted to just send messages from outside a message handler you can inject IEndpointInstance or IMessageSession which is returned when you start the bus.
I think they suggest to only send messages from message handlers using the context - but sometimes that just doesn't work.