I am using ASP.Net Core 3.1 Web API. I have a need to perform an additional activity in the background, when I get a call to a controller action method, without adding too much delay in the processing of current action. Additional activity that I am performing is prepping some data into Cache so its available before user performs next step. So I want to simply trigger that activity, without waiting for it to complete. But at the same time, I don't want task to be disposed without completing, just because the action method completed it processing, returned response back to caller and the transient controller was disposed.
So I plan to use a Singleton service injected into controller to perform the task. But the task itself may involve needing to use a transient service. So instead of directly injecting transient service into Singleton Service, I am thinking injecting transient service into Controller. And then in the action method, I will pass the transient service as parameter to an async method on Singleton service and then within that method, call the required service.
public IActionResult GetSomething(string input1)
{
var resp = await _transientService1.GetSomethingElse(input1);
// I am not awaiting this task
var backgroundTask = _singletonService1.DoSomethingAsync(_transientService2, resp.someattr);
return Ok(resp);
}
Now within the singleton service, I will get the required data and write it into cache.
public async Task DoSomethingAsync(ITransientService2 myTransientService2, string someParam)
{
var temp1 = await myTransientService2.GetSomethingNew(someParam);
var temp2 = await _SingletonService2.WriteCache(temp1);
}
So I wanted to know first of all, if this approach will work. If it works, what are the pitfalls or gotchas, that I need to be aware of.
Currently, this is all conceptual. Else I would have tried it out directly:) Hence the questions.
That can work as long as you're happy with passing the dependency as an argument.
If you don't want to pass the transient dependency in as an argument, another option is to inject the IServiceProvider into the singleton service, and instantiate the transient service when it's needed.
class MySingleton
{
private readonly IServiceProvider _serviceProvider;
public MySingleton(IServiceProvider serviceProvider)
{
_serviceProvider = serviceProvider;
}
public async Task ExecuteAsync()
{
// The scope informs the service provider when you're
// done with the transient service so it can be disposed
using (var scope = _serviceProvider.CreateScope())
{
var transientService = scope.ServiceProvider.GetRequiredService<MyTransientService>();
await transientService.DoSomethingAsync();
}
}
}
Related
I have following registration of the service:
For<Internal.FullPdfExporter>().Use<Internal.FullPdfExporter>();
I have a exporter factory to create different exporters. First of all injected StructureMap container to this factory:
private readonly IContainer _container;
public ExporterFactory(IContainer container)
{
_container = container;
}
Then i use following code to get a instance of FullPdfExporter:
private IExporterBase CreateInstance(Type exporterType)
{
return _container.GetInstance(exporterType) as IExporterBase ;
}
But this code doesn't return control at all. What's the problem is here?
I tried to check all registered instances in debug, and notices that container has registration for "Internal.FullPdfExporter".
Note: Internal is a alias for namespace;
How i get exporter from factory (code of business service which is called from Http Controller):
var task = Task.Run(async () =>
{
foreach (var item in exportData.OrderBy(x => x.ExportUid))
{
var exporter = _exporterFactory.GetBookExporter(param1, param2);
await exporter.StartExport(item.ExportUid);
if (!exporter.IsCompletedSuccessfully)
{
break;
}
}
});
return response;
Note: If to place _exporterFactory.GetBookExporter call before the task.Run, everything works good. Container is able to resolve the dependency.
Code of the GetBookExporter method:
public IExporterBase GetBookExporter(BookSchema bookSchema, PublishProfileType publishProfileType)
{
return CreateInstance(typeof(Internal.FullPdfExporter));
}
Your general logic is flawed. You expect things to run, even after your http call is already over.
Once you return a response, things happen in the ASP.NET framework.
Dependency injection containers finish their scope, objects get disposed.
Keeping those references that are probably scoped or transient in nature until after the call has completed is not going to work. Granted, sometimes it might work, when you have classes that do not implement IDisposable or you just get lucky with a microsocond timing, but generally speaking, this is not anticipated and not meant to work.
If you want to do work after sending out the reply, you have to hand this work over to another party. Maybe you save it into a database and another service picks it up and works on it. Or maybe you have another program or thread running and you try some IPC. But you should not expect threads or tasks referencing things from the scope of your http call to work after it's done.
So right now I get a
Error: System.InvalidOperationException: A second operation was started on this context before a previous operation completed.
Because blazor doesn't seem to respect the current request.
What I am doing is something like this:
FirstComponent.razor
#inject IService _service; // abstracted service that calls EF
<layout> // layout stuff here
<SecondComponent /> // second blazor component
</layout>
#code {
protected override async Task OnInitializeAsync()
{
var getSomething = await _service.OnInitializedAsync();
}
}
SecondComponent.razor
#inject IService _service; // abstracted service that calls EF
#code {
protected override async Task OnInitializedAsync()
{
var getSomething = await _service.GetSomething();
}
}
So I split my entity in to multiple sub-components for editing it. Now I have one "parent" component that calls all these sub-components.
Edit 1
My IService would look like this.
public interface IService
{
public Task<Something> GetSomething();
}
internal class Service : IService
{
private readonly SomethingRepository _repo;
public Service(SomethingRepository repo)
{
_repo = repo;
}
public async Task<Something> GetSomething() => _repo.GetAsync();
}
internal SomethingRepository
{
private readonly AppDbContext _context;
public SomethingRepository(AppDbContext context)
{
_context = context;
}
public async Task<Something> GetAsync() => ...;//implementation of whatever
}
I am adding my AppDbContext to the service collection with AddDbContext and my services and repositories with AddScoped
For Blazor Server apps you should not use the existing DI lifetimes for your DbContext. Instead create a new one for each request, or scope it to a component. per:
EF Core provides the AddDbContext extension for ASP.NET Core apps that
registers the context as a scoped service by default. In Blazor Server
apps, scoped service registrations can be problematic because the
instance is shared across components within the user's circuit.
DbContext isn't thread safe and isn't designed for concurrent use. The
existing lifetimes are inappropriate for these reasons:
Singleton shares state across all users of the app and leads to
inappropriate concurrent use.
Scoped (the default) poses a similar issue between components for the same user.
Transient results in a new instance per request; but as components can be long-lived, this results in a longer-lived context than may be
intended.
The following recommendations are designed to provide a consistent
approach to using EF Core in Blazor Server apps.
By default, consider using one context per operation. . . .
Use a flag to prevent multiple concurrent operations: . . .
For longer-lived operations that take advantage of EF Core's change tracking or concurrency control, scope the context to the lifetime of
the component.
ASP.NET Core Blazor Server with Entity Framework Core (EFCore)
Typically this happens when you use async/await in calling the service but do not use flags to stop the rendering of the child components. This should fix most of your "A second operation started..." issues.
<layout>
#if(_loading)
{
<span> Loading .... </span>
}
else{
<ChildComponent/>
}
</layout>
#code
{
bool _loading;
protected override async Task OnInitializedAsync(){
_loading = true;
var data = _service.getSomething();
_loading = false;
}
}
Add this to your connection string "MultipleActiveResultSets=true"
So using the factory pattern provided by Henk Holterman is not a solution for me. See: https://learn.microsoft.com/en-us/ef/core/dbcontext-configuration/#using-a-dbcontext-factory-eg-for-blazor
That did work is changing everythings lifetime from Scoped to Transient. Including the db context.
services.AddDbContext<AppDbContext>(..options.., ServiceLifetime.Transient);
services.AddTransient<IService, Service>();
services.AddTransient<SomethingRepository>();
Controller
public class LocationsController : ApiController
{
private readonly IMediator _mediator;
public LocationsController(IMediator mediator)
{
_mediator = mediator;
}
public IEnumerable<Place> Get()
{
return _mediator.Send(new GetLatestMapData<Place>());
}
}
On first request of Get() action, the Handler is instantiated by SimpleInjector and executed correctly.
On the second request (F5 in browser for e.g.), it fails with :
Handler was not found for request of type ....
Container or service locator not configured properly or handlers not registered with your container.
and inner exception of:
Cannot access a disposed object.
Object name: 'The ThreadLocal object has been disposed.'
OWIN Startup
public class Startup
{
public void Configuration(IAppBuilder app)
{
// SimpleInjector
var container = CompositionRoot.CreateContainer();
var config = GlobalConfiguration.Configuration;
config.DependencyResolver = new SimpleInjectorWebApiDependencyResolver(container);
// Routing
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute("DefaultApi", "api/{controller}/{id}",
new { id = RouteParameter.Optional });
config.EnsureInitialized();
app.UseWebApi(config);
}
}
SimpleInjector IPackage for WebAPI project
public class Installer : IPackage
{
public void RegisterServices(Container c)
{
c.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
c.RegisterWebApiControllers(GlobalConfiguration.Configuration);
}
}
I think what's happening is the Handler is correctly created, and then disposed after the first request.
Now, I don't know why, but on subsequent requests, the Handler isn't re-created. I know this because if I change the WebApiRequestLifestyle to 'not dispose when scope ends', it works for every request:
c.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle(false
/*disposeInstanceWhenScopeEnds*/);
Questions
Should I keep the disposeInstanceWhenScopeEnds parameter set to false?
If not, what is the correct solution?
I see this has been solved before by creating a LifetimeScopeDecorator... however, surely this functionality is already provided by the SimpleInjector WebApi integration library? What am I missing?
(And thank you for reading)
This link provides good guidance on dependency resolution and using the IDependencyResolver / IDependencyScope Interfaces.
Immediately you will see that they touch on life spans which tend to get a little tricky.
This section in particular is interesting:
Dependenecy Scope and Controller Lifetime
Controllers are created per request. To manage object lifetimes,
IDependencyResolver uses the concept of a scope.
The dependency resolver attached to the HttpConfiguration object has
global scope. When Web API creates a controller, it calls BeginScope.
This method returns an IDependencyScope that represents a child scope.
Web API then calls GetService on the child scope to create the
controller. When request is complete, Web API calls Dispose on the
child scope. Use the Dispose method to dispose of the controller’s
dependencies.
Conventionally bootstrapping a service would occur once during the app start-up and as you know resolve any dependencies at that time. Only when the worker process was shutting down (no activity, for example) would this then invoke dispose.
Ordinarily I think it is quite normal for resolved instances to remain for the life cycle unless it is imperative that they are destroyed after use. But the example given explains that we must correctly dispose once the request is complete. So I would recommend that you dispose of your instances correctly using the examples provided as guidance.
This helped me when working with IoC and WebApi. I hope this helps!
You need to arrange your Lifetime scoped
Code:
container.Options.DefaultScopedLifestyle = new WebApiRequestLifestyle();
container.Options.LifestyleSelectionBehavior = new WebApiInjectionLifestyle();
internal class WebApiInjectionLifestyle : ILifestyleSelectionBehavior
{
public Lifestyle SelectLifestyle(Type serviceType, Type implementationType)
{
return Lifestyle.Scoped;
}
}
More Detail
https://simpleinjector.readthedocs.io/en/latest/lifetimes.html
I'm currently using WebApiRequestLifestyle has the default scoped lifestyle. I want to inject a service in the OWIN Middleware and in one of the API controllers, and the service's scope should be still WebAPI i.e., for the entire request, there should be only one instance of the service.
public class TestMiddleware : OwinMiddleware
{
private readonly ITestService _testService;
public TestMiddleware(OwinMiddleware next, ITestService testService) : base(next)
{
_testService = testService;
}
public override async Task Invoke(IOwinContext context)
{
var test = _testService.DoSomething();
await Next.Invoke(context);
}
}
public class ValuesController : ApiController
{
private readonly ITestService _testService;
public ValuesController(ITestService testService)
{
_testService = testService;
}
}
ITestService instance should be same for the entire request. How should I register the middleware?
This is how I'm doing it now:
using (container.BeginExecutionContextScope())
{
var testService = container.GetInstance<ITestService>();
app.Use<TestMiddleware>(testService);
}
The problem with this approach is - one instance of ITestService is getting created for the middleware during registration and stays forever (like a singleton), and for every webapi request, a new instance gets created and shared across the controllers (webapi scope)
Please don't point me to these questions - WebApi + Simple Injector + OWIN
Injecting a dependency into OWIN Middleware and per web-request with Simple Injector
OWIN's Use<T> method registers the supplied T as singleton in the OWIN pipeline, no matter what lifetime you configured that type with in your container. So while you resolve the middleware in an active scope, you (implicitly) tell OWIN to cache this instance for ever.
You have two choices here:
Make sure that the middleware component can be used as singleton in the OWIN pipeline, or
Resolve the middleware component on each request.
Making sure the middleware component can be used as singleton is easy. Just register it as singleton in Simple Injector, and when you call Verify(), Simple Injector will detect whether or not this component can be used as singleton, or whether it has dependencies with a shorter lifestyle. This does mean however that all dependencies should be singletons and runtime data (like DbContext's and other data objects) should be passed through method calls after the object graph is built. I consider this good practice, but this might be quite a change in your application and probably quite a mind shift. Because of this I consider this to be out of scope for this question, so you should go for option 2.
In case your middleware component has dependencies with a shorter lifestyle, you are should resolve that middleware per request request. This means you should not use OWIN's Use<T>(middleware) method, because that would make it a singleton.
This is how to do it:
app.Use(async (context, next) =>
{
var middleware = container.GetInstance<TestMiddleware>();
await middleware.Invoke(context, next);
});
Note that the TestMiddleware is resolved on each request. This gives Simple Injector full control over the built object graph. This means however that you need to make a small adjustment to your TestMiddleware class. This is how it should look:
public sealed class TestMiddleware
{
private readonly ITestService _testService;
public TestMiddleware(ITestService testService)
{
_testService = testService;
}
public async Task Invoke(IOwinContext context, Func<Task> next)
{
var test = _testService.DoSomething();
await next();
}
}
Note that the OwinMiddleware parameter removed from the constructor, and replaced by a Func<Task> parameter in the Invoke method. This allows Simple Injector to construct the type, because its constructor won't contain any runtime parameters anymore. Remember: compile time dependencies through the constructor, runtime data through method calls.
Also note that the middleware doesn't inherit from OwinMiddleware anymore. Since the middleware didn't inject a wrapped middleware, inheriting from it became useless.
After reading Steven's answer, this is what I did:
Registered the middleware like this:
using (AsyncScopedLifestyle.BeginScope(ServiceLocator.Container))
{
app.Use<TestMiddleware>();
}
Inside the middleware, I used ServiceLocator to resolve the dependency as Steven suggested (I know ServiceLocator is an anti-pattern, but we are already using it in a few unavoidable places in the app)
public override async Task Invoke(IOwinContext context)
{
var testService = ServiceLocator.Current.GetInstance<ITestService>();
testService.DoSomething();
await Next.Invoke(context);
}
Note: I assume Simple Injector (or any DI library) uses CallContext to maintain the scoped instances; if so, just wanted to share that the CallContext doesn't flow properly after some middlewares. Here is my another question that I posted a while ago reporting the same - OWIN Middleware & CallContext
I'm using autofac in an owin-driven web api project. I've come to a place where I need to "Fire and Forget" some processing of data.
This normally isn't an issue. But since the dependencies are controlled by the per-request lifetime scope managed by the autofac middleware. I cannot use un-managed resources (which is what i need to do) in my non-awaited async method.
//for example...
public async Task<IEnumerable<MyThings>> GetThings(){
var things = await _thingRepo.GetInterestingThingsAsync();
//fire and forget. -- not awaiting
InjectedObject.DoThingsAsync(things);
return things;
}
The problem here is that I need to use other injected resources (un-managed resources) inside DoThingsAsync. Currently this breaks because they're disposed of when the Owin Request ends.
I could create a child lifetime scope for each invocation but I'm hoping their might be a cleaner way to do such things..
Update:
With the dawn of .Net Framework 4.5.2 I can now use
HostingEnvironment.QueueBackgroundWorkItem Which is preferred Because it will keep IIS from recycling before the work item completes.
I think I found a solution for my use case..
I just Inject this class where I need it:
public class AsyncTask : IAsyncTask
{
private ILifetimeScope _lifetimeScope;
public AsyncTask(ILifetimeScope lifeTimeScope)
{
//disposed of by owning scope...
_lifetimeScope = lifeTimeScope;
}
public async Task Fire<T>(Func<T,Task> asyncAction)
{
using (var scope = _lifetimeScope.BeginLifetimeScope())
{
await asyncAction(scope.Resolve<T>());
}
}
}
public interface IAsyncTask
{
Task Fire<T>(Func<T, Task> action);
}
Then..
_asyncTask.Fire<IEmailService>(svc => svc.SendMillionsOfEmailsAsync(content));