Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 5 Async Context Management

I have a fairly standard MVC 5 application consisting of a repository layer, service layer and a controller layer. To keep each layer decoupled and testable I am using Ninject for dependency injection.

To brush up on new skill I have decided to make use of the new new Task controller actions with async / await on IO bound operations for service and controller methods.

Normally I just make use of the InRequestScope binding like so

kernel.Bind<IDbContext>().To<BlogContext>().InRequestScope();

In general this is working fine now, however if I chose to debug my application, or join multiple tracked entity framework objects together and save, I'm finding the context has been disposed or I'm getting tracking issues. I see why this is happening, it's perfectly logical because the operation is no longer taking place on the IIS thread, so how would Ninject know that it should be using the same context.

To work around this I can pass my context into each repository call from my service layer or even down from the controller layer if need be. However I feel this looks messy and I'd rather Ninject manage the context of this object if possible.

What are the best strategies for handling this in an elegant / minimalistic manner whilst keeping my code similar to the examples given below?

Here is an example of one of my controller methods

    public virtual async Task<ActionResult> Edit(int id)
    {
        var editViewModel = await BuildDefaultCreateEditViewModel();

        var post = await postService.GetNonDeletedPost(id);
        ...
        ...
        return View(MVC.Admin.Post.Views.CreateEdit, editViewModel);
    }

Service method

    public async Task<PostDTO> GetNonDeletedPost(int postId)
    {
        return (await PostRepostiory.GetPost(postId)).ConvertToDTO();
    }

Repository method

    public Task<Post> GetPost(int postId)
    {
        return QueryableExtensions.SingleOrDefaultAsync(
                DbSet.Where(post => post.PostId == postId)
                .Include(post => post.PostVersions)
                .Include(post => post.Categories)
                .Include(post => post.Files));
    }
like image 709
jps Avatar asked May 21 '14 21:05

jps


1 Answers

In your post and in the related comments there are some great examples of how to do this.

However, for some other readers that might stumble here, I want to throw out an honest question: Do you even really need asynchronous controllers?

Asynchronous controller actions free up the number of threads needed to handle requests; however, if you're not running out of threads and not likely to, then you're creating complexity while trying to solve a problem you neither have nor expect to have. Or: "Is Async truly free and always better?"

Effectively, I'm trying to showcase that one very viable solution (and perhaps not the one you are looking for), would be to simply not use asynchronous controllers - if indeed not needed (which would depend on your specific circumstances.)

That good old wacky solution of solving the problem by removing the problem. And particularly, I propose it because removing complexity appears to be one of the goals.

How to Choose Which to use? there is a longer list, but two of the best questions are:

  • Are your items short running?
  • And if not, are your items CPU bound?

http://www.asp.net/mvc/tutorials/mvc-4/using-asynchronous-methods-in-aspnet-mvc-4

From the above link "Using asynchronous action methods on CPU-bound operations provides no benefits and results in more overhead."

like image 165
Greg Avatar answered Oct 19 '22 04:10

Greg