Kostadin Pirgov
Kostadin Pirgov

Reputation: 45

Async\Await methods

I am a new in the Async/Await functionality and I tried to use them in my MVC project.

So from the Controller I call the current method to initialize my model:

var model = this.quantService.GetIndexViewModel(companyIds, isMore, currentrole).Result;

In this GetIndexViewModel I use await:

public async Task<CompanyBoardViewModel> GetIndexViewModel(IEnumerable<int> parameter, bool isMore = false, bool currentRole = false)
        {
                return new CompanyBoardViewModel
                {
                    TopJoinAplicant = await this.TopJointApplicant(parameter, isMore),
                    TopPriorityCountry = await this.TopPriorityCountry(parameter),
                    TopPublicationCountries = await this.TopPublicationCountries(parameter),
                    TopGrantedInventors = await this.TopGrantedInventors(parameter),
                    TopIPC = await this.TopIPC(parameter),
                    TopCPC = await this.TopCPC(parameter),
                    TopCitedInventors = await this.TopCitedInventors(parameter),
                    TopCitedPatents = await this.TopCitedPatents(parameter),
                    CGAR = await this.GetCGAR(parameter),
                };

}

For the first method I use these code:

private async Task<QuantTableViewModel<TopFilterViewModel>> TopJointApplicant(IEnumerable<int> ids, bool isMore = false)
        {

            return await Task.Run(() => new QuantTableViewModel<TopFilterViewModel>
            {
                Tableid = "TopJointApplicants",
                Title = "Top Joint Applicants",
                FirstCol = "Position",
                SecondCol = "Joint Applicant",
                ThirdCol = "#",
                IsSeeMore = isMore,
                Data = this.cache.TopJointApplicant(ids).ToList()
            });
}

In this method I call : Data = this.cache.TopJointApplicant(ids).ToList() this method created a procedure and get information from the Database(the method is executed without any problems), but when I try to return the QuantTableViewModel<TopFilterViewModel> I stack(as I go in a death log).

I will be really happy if anyone know why this is happened.

Upvotes: 1

Views: 168

Answers (4)

smoksnes
smoksnes

Reputation: 10851

In this case I would say that's it's enough that your public method is async, since there's not really any asyncronous going on in TopJointApplicant.

    public async Task<CompanyBoardViewModel> GetIndexViewModel(IEnumerable<int> parameter, bool isMore = false, bool currentRole = false)
        {
                return new CompanyBoardViewModel
                {
                    TopJoinAplicant = this.TopJointApplicant(parameter, isMore),
                    TopPriorityCountry = await this.TopPriorityCountry(parameter),
                    TopPublicationCountries = await this.TopPublicationCountries(parameter),
                    TopGrantedInventors = await this.TopGrantedInventors(parameter),
                    TopIPC = await this.TopIPC(parameter),
                    TopCPC = await this.TopCPC(parameter),
                    TopCitedInventors = await this.TopCitedInventors(parameter),
                    TopCitedPatents = await this.TopCitedPatents(parameter),
                    CGAR = await this.GetCGAR(parameter),
                };
    }

    private QuantTableViewModel<TopFilterViewModel> TopJointApplicant(IEnumerable<int> ids, bool isMore = false)
            {
                var Data = this.cache.TopJointApplicant(ids).ToList();
                return new QuantTableViewModel<TopFilterViewModel>
                {
                    Tableid = "TopJointApplicants",
                    Title = "Top Joint Applicants",
                    FirstCol = "Position",
                    SecondCol = "Joint Applicant",
                    ThirdCol = "#",
                    IsSeeMore = isMore,
                    Data = this.cache.TopJointApplicant(ids).ToList()
                });
            }

I recommend you to fully embrace the await/async-pattern if you're going to use it. This means that your controller also should use await/async.

public class YourController : Controller
{
    // Note the Task<ActionResult> and async in your controller.
    public async Task<ActionResult> YourControllerMethod()
    {
        var model = await this.quantService.GetIndexViewModel(companyIds, isMore, currentrole);
        return View(model); // Or something like this.
    }
}

Also, consider your naming convention. For clarity, async methods should end with the suffix Async, such as GetIndexViewModelAsync.

EDIT: Based on the comments I think I should clarify what await/async does. An operation will not execute faster simply because you use the await/async-pattern, rather the opposite. Async/await creates an overhead for the thread management which would likely cause your operation to execute slower.

Instead, there are 2 main advantages:

  1. When using async/await you will not block the thread. This means that while you're application is waiting for something else (such as IO, DB or webservice call) the thread can be used for something else, such as executing another we request. This doesn't mean that an DB-call will be executed faster. But it will let the thread do something else while waiting. If you're using IIS the number of threads are limited. So instead of locking them with expensive IO, they can serve another request while waiting.

  2. You may do more things at the same time. For example, you could send a request to you DB, while executing a slow webservice call at the same time. This may cause the total execution time to be faster, since you're doing more things at the same time. However, there are limitations. For instance, if you're using Entity Framework, only one thread may access the context at the time. But while waiting for the DB, you can do something else. For example:

    public class MyThreadingClass {
    private Task ExecuteWebServiceCallAsync() { return await _myService.DoSomething(); }

    private Task ExecuteDbQueryAsync()
    {
        return await _context.Customer.FirstOrDefaultAsync();
    }
    
    public void DoThingsWithWaitAll()
    {
        var tasks = new Task[2];
        // Fire up first task.
        tasks[0] = ExecuteWebServiceCallAsync();
        // Fire up next task.
        tasks[1] = ExecuteDbQueryAsync();
        // Wait for all tasks.
        Task.WaitAll(tasks);
    }
    
    public Task DoThingsWithWithAwaitAsync()
    {
        // Fire up first task.
        var webServiceTask = ExecuteWebServiceCallAsync();
        // Fire up next task.
        var dbTask = ExecuteDbQueryAsync();
        // Wait for all tasks.
        await webServiceTask;
        await dbTask;
    }
    

    }

So, to sum up. The reason why you should use await/async is when you can do it ALL THE WAY down to the execution of the slow operation (such as DB or webservice). Or if you wish to do several things at once.

In your particular case you can do something like this:

public async Task<CompanyBoardViewModel> GetIndexViewModel(IEnumerable<int> parameter, bool isMore = false, bool currentRole = false)
{
    // Let the threads start processing.
    var topApplicantTask = this.TopJointApplicant(parameter, isMore);
    var topPriorityCountryTask = this.TopPriorityCountry(parameter);
    var topPublicationContriesTask = this.TopPublicationCountries(parameter);
    var topIPCTask = this.TopIPC(parameter);
    var topCPCTask = this.TopCPC(parameter);
    var topCitedInventorsTask = this.TopCitedInventors(parameter);
    var topCitetPatentsTask = this.TopCitedPatents(parameter);
    var getCGARTask = this.GetCGAR(parameter);

    // Await them later.
    return new CompanyBoardViewModel
    {
        TopJoinAplicant = await topApplicantTask,
        TopPriorityCountry = await topPriorityCountryTask,
        TopPublicationCountries = await topPublicationContriesTask,
        TopGrantedInventors = await this.TopGrantedInventors(parameter),
        TopIPC = await topIPCTask,
        TopCPC = await topCPCTask,
        TopCitedInventors = await topCitedInventorsTask,
        TopCitedPatents = await topCitetPatentsTask,
        CGAR = await getCGARTask,
     };
}

But try to avoid Task.Run since it's considered an anti-pattern. Instead, try to use await/async all the way from the controller to the actual operation (DB, IO, webservice). Also, in the example above there's a lot of threading going on. It should likely be cleaned up a bit, but you can see it as a proof-of-concept more than the suggested solution.

Upvotes: 0

Stephen Cleary
Stephen Cleary

Reputation: 457157

I explain the deadlock you're seeing on my blog. In short, don't block on async code; instead, use async all the way.

But there are other problems with your approach. As others have noted, await Task.Run is an antipattern on ASP.NET. You may want to read my article on async ASP.NET.

Finally, one other tip: you're approaching the problem from the wrong direction. Instead of just choosing a method to "make async", you should first think about what your application is doing, and start converting I/O calls to async at the lowest level. Convert them to use async APIs instead of blocking APIs (i.e., no Task.Run). Then change their callers to async, and their callers to async, eventually changing your controller method(s) to async.

Upvotes: 2

jegtugado
jegtugado

Reputation: 5141

You can actually have an async controller so you don't need to call .Result which renders async operation to run synchronously.

something like:

public Task<ActionResult> Index(object parameter)
{
    var model = await this.quantService.GetIndexViewModel(companyIds, isMore, currentRole);

    return View(model);
}

Upvotes: 0

John Mc
John Mc

Reputation: 2933

You don't need to use Task.Run when using the async/await pattern.

Upvotes: 0

Related Questions