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));
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.
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();
}
}
}
I am working with async actions and use the HttpContext.Current.User like this
public class UserService : IUserService
{
public ILocPrincipal Current
{
get { return HttpContext.Current.User as ILocPrincipal; }
}
}
public class ChannelService : IDisposable
{
// In the service layer
public ChannelService()
: this(new Entities.LocDbContext(), new UserService())
{
}
public ChannelService(Entities.LocDbContext locDbContext, IUserService userService)
{
this.LocDbContext = locDbContext;
this.UserService = userService;
}
public async Task<ViewModels.DisplayChannel> FindOrDefaultAsync(long id)
{
var currentMemberId = this.UserService.Current.Id;
// do some async EF request …
}
}
// In the controller
[Authorize]
[RoutePrefix("channel")]
public class ChannelController : BaseController
{
public ChannelController()
: this(new ChannelService()
{
}
public ChannelController(ChannelService channelService)
{
this.ChannelService = channelService;
}
// …
[HttpGet, Route("~/api/channels/{id}/messages")]
public async Task<ActionResult> GetMessages(long id)
{
var channel = await this.ChannelService
.FindOrDefaultAsync(id);
return PartialView("_Messages", channel);
}
// …
}
I have the code recently refactored, previously I had to give the user on each call to the service.
Now I read this article https://www.trycatchfail.com/2014/04/25/using-httpcontext-safely-after-async-in-asp-net-mvc-applications/ and I’m not sure if my code still works.
Has anyone a better approach to handle this? I don’t want to give the user on every request to the service.
As long as your web.config settings are correct, async/await works perfectly well with HttpContext.Current. I recommend setting httpRuntime targetFramework to 4.5 to remove all "quirks mode" behavior.
Once that is done, plain async/await will work perfectly well. You'll only run into problems if you're doing work on another thread or if your await code is incorrect.
First, the "other thread" problem; this is the second problem in the blog post you linked to. Code like this will of course not work correctly:
async Task FakeAsyncMethod()
{
await Task.Run(() =>
{
var user = _userService.Current;
...
});
}
This problem actually has nothing to do with asynchronous code; it has to do with retrieving a context variable from a (non-request) thread pool thread. The exact same problem would occur if you try to do it synchronously.
The core problem is that the asynchronous version is using fake asynchrony. This inappropriate, especially on ASP.NET. The solution is to simply remove the fake-asynchronous code and make it synchronous (or truly asynchronous, if it actually has real asynchronous work to do):
void Method()
{
var user = _userService.Current;
...
}
The technique recommended in the linked blog (wrapping the HttpContext and providing it to the worker thread) is extremely dangerous. HttpContext is designed to be accessed only from one thread at a time and AFAIK is not threadsafe at all. So sharing it among different threads is asking for a world of hurt.
If the await code is incorrect, then it causes a similar problem. ConfigureAwait(false) is a technique commonly used in library code to notify the runtime that it doesn't need to return to a specific context. Consider this code:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var context = HttpContext.Current;
// Note: "context" is not correct here.
// It could be null; it could be the correct context;
// it could be a context for a different request.
}
In this case, the problem is obvious. ConfigureAwait(false) is telling ASP.NET that the rest of the current method does not need the context, and then it immediately accesses that context. When you start using context values in your interface implementations, though, the problem is not as obvious:
async Task MyMethodAsync()
{
await Task.Delay(1000).ConfigureAwait(false);
var user = _userService.Current;
}
This code is just as wrong but not as obviously wrong, since the context is hidden behind an interface.
So, the general guideline is: use ConfigureAwait(false) if you know that the method does not depend on its context (directly or indirectly); otherwise, do not use ConfigureAwait. If it's acceptable in your design to have interface implementations use the context in their implementation, then any method that calls an interface method should not use ConfigureAwait(false):
async Task MyMethodAsync()
{
await Task.Delay(1000);
var user = _userService.Current; // works fine
}
As long as you follow that guideline, async/await will work perfectly with HttpContext.Current.
Async is fine. The problem is when you post the work to a different thread. If your application is setup as 4.5+, the asynchronous callback will be posted in the original context, so you'll also have the proper HttpContext etc.
You don't want to access shared state in a different thread anyway, and with Tasks, you rarely need to handle that explicitly - just make sure you put all your inputs as arguments, and only return a response, rather than reading or writing to a shared state (e.g. HttpContext, static fields etc.)
There is no problem, if your ViewModels.DisplayChannel is a simple object without additional logic.
A problem may occur, if the result of your Task references to "some context objects", f.e. to HttpContext.Current. Such objects are often attached to the thread, but entire code after await may be executed in another thread.
Keep in mind, that UseTaskFriendlySynchronizationContext doesn't solve all your problems. If we are talking about ASP.NET MVC, this setting ensures that Controller.HttpContext contains correct value as before await as after. But it doesn't ensure that HttpContext.Current contains correct value, and after await it still can be null.
Let's say that I have a class with members which require asynchronous actions to initialize (such as file i/o or web requests). I only need to initialize once, and I don't want to reinitialize.
Are Tasks and Async-Await a good fit to accomplish this?
Here's an example of what I'm currently doing:
private Task _initializeTask;
public Task InitializeAsync()
{
return _initializeTask ?? (_initializeTask = Task.Run(
async () =>
{
// Do an action requiring await here
await _storageField.LoadAsync();
}));
}
Does this do what I think it does? Are there better ways to do it?
Is it thread safe? Not a requirement but should be considered.
Edits:
What I think it does? I believe that if _initializeTask hasn't been assigned then it will be assigned a new task that will kick off and then await the async lambda contained within. Any subsequent calls to the method will await the already running (or completed) task that was assigned to _initializedTask.
When do I want it to construct? Typically I'd use this sort of method on a service that I resolve with an IoC container. Multiple dependent classes can be constructed with a reference to the class. Then, before use, each of them awaits InitializeAsync(). If theres multiple dependent classes then I don't want to double up on initializing it.
Factory Method? There usually won't be multiple instances constructed that need to be initialized, so Factory method doesn't seem like a good solution. I've used something like a "static CreateAsync()" method for things like folder wrapper classes, but that didn't let me inject initialized folders into constructors. Async Factory methods don't gain anything when they can't be used with IoC constructor injection.
Your code will work but it is not thread safe, _initializeTask can be changed after checking it for null and before initializing it. This will result in two initializations. I would consider using AsyncLazy<T>, which is inherits from Lazy<T> which is thread safe.
Then assuming LoadAsync returns Task rather than Task<T>, your code becomes (untested):
private AsyncLazy<object> initializeTask = new AsyncLazy<object>(async () =>
{
// Do an action requiring await here
await _storageField.LoadAsync();
return null;
});
public Task InitializeAsync()
{
return _initializeTask.Value;
}
You can also define a non-generic version of `AsyncLazy, so you don't have to return a value from the initialization routine.
public class AsyncLazy : Lazy<Task>
{
public AsyncLazy(Func<Task> taskFactory) :
base(() => Task.Run(taskFactory)) { }
}
Then you can initialize it using an initialization method, however that method is required to be static by the compiler:
private AsyncLazy _initializeTask = new AsyncLazy(LoadStorageAsync);
private static async Task LoadStorageAsync()
{
// Do an action requiring await here
await _storageField.LoadAsync();
}
public Task InitializeAsync()
{
return _initializeTask.Value;
}
Run the asynchronous task from a regular initialization function. In the regular function, check if your app already has a loaded data set that matches your expectations. If the data is not present THEN call the async function.
...
If (BooleanFunctionToDetermineIfDataIsNotPresent){FunctionToLoadFreshData}
...
Private Async Void FunctionToLoadFreshData{...}
The function to load the data must not return a value lest it become a task itself.
If you work in a WebHost (ASP.NETCore app) or generic Host environment you can use simply way to do that with nuget HostInitActions
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IService, MyService>();
services.AddAsyncServiceInitialization()
.AddInitAction<IService>(async (service) =>
{
await service.InitAsync();
});
}
This nuget ensures that your initialization action will be performed asynchronously before the application starts.
Another advantage of this approach is that this initialization action can be defined from any place where services are installed into the IServiceCollection (For example, in an extension method in another project that installs internal implementations of public interfaces). This means that the ASP.NET Core project does not need to know what service and how it should be initialized, and it will still be done.
My project is structured by services and repositories (all repositories share the db context). In one of my service layers, I have an asynchronous method that writes to the database using a repository. The web request will finish and dispose of the context before this method can get to use it. I tried to understand NamedScopes as stated in this answer. I still can't seem to understand how to implement it. I'll show how my project is structured and hope someone can help me at the code level.
Bindings
private static void RegisterServices(IKernel kernel)
{
//dbcontext
kernel.Bind<EntityDatabaseContext>().ToMethod(context => new EntityDatabaseContext()).InRequestScope();
//unit of work
kernel.Bind<IUnitOfWork>().To<UnitOfWork>().InRequestScope();
//repositories
kernel.Bind<IRepository<Account>>().To<Repository<Account>>().InRequestScope();
//services
kernel.Bind<IAuthenticationService>().To<AuthenticationService>().InRequestScope();
}
AuthenticationService uses constructor injection
public AuthenticationService(UnitOfWork unitOfWork, IRepository<Account> accountRepository){}
A method inside my AuthenticationService
//this is a background process
public Task SomeMethodAsync(string text)
{
//spin it off into a new task
return Task.Factory.StartNew(() => SomeMethod(text));
}
SomeMethod makes use of accountRepository. Please tell me if anymore information is needed. Please help me with the threading issue, if NamedScopes is the solution, how do I implement it in my case?
Basically, a background process is being executed and it is using a context that is being disposed of by ninject due to the request scope.
You should be aware that running background threads can cause you many problems. IIS can decide to recycle the app pool at any time which will terminate your thread immediately (or it doesn't execute in some cases at all) leaving your application in an inconsistent state.
http://haacked.com/archive/2011/10/16/the-dangers-of-implementing-recurring-background-tasks-in-asp-net.aspx
The easiest and least error prone wayto run asyncronous operations is to implement a windows service and delegate those async operations to the windows service e.g. using MSMQ.
If you still want to go the hard way then read about HostingEnvironment.RegisterObject and IRegisteredObject to prevent these inconsistent situations.
The Ninject part is quite easy. Just create some job processor class e.g. MyJobProcessor taking all required dependencies to execute the task. It should implement INotifyWhenDisposed. The easiest way to do so is to derive from DisposeNotifyingObject.
public class MyJobProcessor : DisposeNotifyingObject, IRegisteredObject
{
public void Execute() { ... }
public void Stop(bool immediate) { ... }
}
Inject this processor to the controller and let the Task start it and dispose it when it has finished its work.
Task.Factory.StartNew(() =>
{
try
{
processor.Execute();
}
finally
{
processor.Dispose);
}
});
Specify that it is the scope for its dependencies.
Bind<MyJobProcessor>().ToSelf().Named("MyJobProcessor").DefinesNamedScope("MyJobProcessorScope");
Bind<IUnitOfWork>().To<UnitOfWork>().WhenAnyAnchestorNamed("MyJobProcessor").InNamedScope("MyJobProcessorScope");