1

I am developing an ASP.NET MVC 5 Application. I have been reading some questions here that suggest not to use the type of behavior I want.

High Level of what I want to achieve -

MVC Controller - calls method at Service Layer and then continues to next line of code in controller as does not need to return anything.

Method in Service Layer - I want to to call some external Web Service that has Async methods so I want these to run off on and then write data to the DB when they return.

Is something like this achievable or not the Best Approach?

To Dummy this out and Test the principle I have the following on controller:

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult TestAsync()
    {
        _myService.TestAsync();

        return RedirectToAction("Summary");
    }

At _myService the method looks as follows:

    public async Task TestAsync()
    {
        await Task.Delay(10000);

        var test = "Testing";

       _testRepository.Add(test);
    }

I was hoping that this TestAsync Method would wait 10 seconds and then I could check the DB and the value Testing would be added. However this is not working - On The UI after hitting the button the page goes to the Summary View and the await Task.Delay breakpoint is hit but the next lines never execute. Is there something incorrect with the approach? Should the TestAsync method just look something like:

    public void TestAsync()
    {
       //Call another private async method here in the class that does the task delay
       //which would be similar to calling the actual Async External Web Service methods
        var test = "Testing";

       _testRepository.Add(test);
    }
Community
  • 1
  • 1
Ctrl_Alt_Defeat
  • 3,933
  • 12
  • 66
  • 116

1 Answers1

3

I have been reading some questions here that suggest not to use the type of behavior I want.

That's because it's dangerous. ASP.NET decides it's safe to unload your AppDomain when there are no active requests, so if you return from your controller action while you still have work to do, ASP.NET will not be aware of that work.

The best solution is to set up a reliable queue (such as an Azure Queue) with a separate backend (such as an Azure WebJob) that processes that queue in a reliable way. This is the correct solution, but lots of people just won't do it because it's complex.

If you have a SQL Server or Redis database in your solution, consider Hangfire. Hangfire uses the database as a reliable queue, and executes its backend alongside your ASP.NET application.

If you want to live dangerously, at the very least you should use HostingEnvironment.QueueBackgroundWorkItem, which will inform ASP.NET of the work your application is doing outside of a request.

Is there something incorrect with the approach?

Yes; _myService.TestAsync is attempting to resume on the request context for a request that has already completed.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • had a brief look at Hangfire - I am using MongoDB for BackEnd DB though so not sure if it will work with it. – Ctrl_Alt_Defeat May 16 '14 at 13:05
  • Without adding the complexity of a queue and not being on .NET 4.5.2 - app is 4.5 - is there a way I can call off to this Service Layer and create a new thread to do the work - as don't need to return something to user - i just want that new thread to do the work and persist the data from web service to DB – Ctrl_Alt_Defeat May 16 '14 at 13:11
  • You can use my [AspNetBackgroundTasks library](https://github.com/StephenCleary/AspNetBackgroundTasks) if you don't want to upgrade to 4.5.2. – Stephen Cleary May 16 '14 at 13:16
  • Thanks Stephen - will take a look into using the library - would using Web API controller make any difference here or does the cruz of the problem still exist? – Ctrl_Alt_Defeat May 16 '14 at 13:24