0

There are three layers in ASP.NET MVC application. From first layer, I am calling a method in second layer and the method calls in third layer where I call web service. Below is the code. Both the layers (2 and 3) are added as Class Library in the solution.

namespace Web.Controllers // Layer 1
{
    using Web.Services;
    // other usings...
    public class Controller1
    {
        [HttpPost]
        public JsonResult Search(SomeObject request)
        {
            Service service = new Service();
            var result = service.Search(request).Result;
        }  
    }
}

namespace Web.Service // Layer 2
{
    using Web.Library;
    // other usings...
    public class Service
    {
        public async Task<SomeType> SearchFlights(SomeObject requestModel)
        {
            SomeObjectReturn result = new SomeObjectReturn();

            Library library = new Library();
            var result = await library.Search(requestModel);
            return result;

        }
    }
}

namespace Web.Library // Layer 3
{
    public class Library
    {
        public async Task<SomeObjectReturn> Search(SomeObject request)
        { 
            // here I call Sabre service to get the result...
            SomeObjectReturn obj = new SomeObjectReturn();
            RestClient restClient = RestClientFactory.Create();
            IActivity activity = new InstaFlightsActivity(restClient, requestModel);
            Sabre.Library.Workflow.Workflow workflow = new Sabre.Library.Workflow.Workflow(activity);
            SharedContext sharedContext = await workflow.RunAsync();
            // map sharedContext to SomeObjectReturn
            return obj;
        }
    }
}

Now I don't know why there is deadlock on await workflow.RunAsync. I have also tried .ConfigureAwait(false) on workflow.RunAsync. But the deadlock is being generated anyway. I don't know what's wrong with the code.

BTW, I have made changes as below in Controller and i got the result.

public async Task<JsonResult> Search(SomeObject request) {...

instead of above.

DhavalR
  • 1,409
  • 3
  • 29
  • 57
  • `.Resul` and `.Wait()` are *blocking* calls. – Panagiotis Kanavos Sep 22 '17 at 10:21
  • _I don't know what's wrong with the code._ by using `.Result` your code goes in the state where thread waiting for completion of hisself. Calling method wait for when task is complete, where completed task waiting for thread to be in idle state to inform about completion. – Fabio Sep 22 '17 at 10:21
  • Two more comments: 1. All `ASP.NET` Controllers must inherit from `Controller` or `ApiController` (if web api), you are not doing this. 2. All Controllers' Actions should be `async Task` – Camilo Terevinto Sep 22 '17 at 10:25
  • `.Result` blocks the synchronization context to which `await` will try to return. Just make the controller action itself asynchronous. In fact, there's *NO* benefit unless you do. Web requests are always processed on separate threads. There's no reason to create *another* background thread while the original background thread is blocked. `async/await` is used to release threads so they can service other requests while waiting. If the action itself isn't asynchronous, you waste that benefit – Panagiotis Kanavos Sep 22 '17 at 10:26
  • This code was copied from Sabre's [SACS-DotNet](https://github.com/SabreDevStudio/SACS-DotNet) sample. Despite the unfortunate name, the [controller actions](https://github.com/SabreDevStudio/SACS-DotNet/blob/master/SACS.ExampleApp/Controllers/SoapController.cs#L32) there are asynchronous, eg `public async Task SoapWorkflow` – Panagiotis Kanavos Sep 22 '17 at 10:48

1 Answers1

1

The call of Result produces the deadlock. Once you have an asynchronous API you should go all the way with async/await, otherwise may deadlocks arise. So if you change your controller code as below, the problem would disappear.

[HttpPost]
public async Task<JsonResult> Search(SomeObject request)
{
    Service service = new Service();
    var result = await service.Search(request);
}  
Boppity Bop
  • 9,613
  • 13
  • 72
  • 151
Christos
  • 53,228
  • 8
  • 76
  • 108
  • Is that same what OP already mentioned in the question – Fabio Sep 22 '17 at 10:24
  • 1
    @Fabio, no, the OP used a *synchronous* method and a `.Result` to *block* the synchronization context. This prevents any call to `await` from returning to that context, hence deadlock. The best solution *is* to make everything asynchronous all the way to the top. Even if one used `ConfigureAwait(false)` this would avoid the deadlock but still waste a thread – Panagiotis Kanavos Sep 22 '17 at 10:34
  • @PanagiotisKanavos, OP's last sentence: _BTW, I have made changes as below in Controller and i got the result._ I think OP expected to get some explanation why – Fabio Sep 22 '17 at 10:35