2
votes

I have the following async long running method inside my asp.net mvc-5 web application :-

 public async Task<ScanResult> ScanAsync(string FQDN)
 {
    // sample of the operation i am doing 
    var c = await context.SingleOrDefaultAsync(a=>a.id == 1);
    var list = await context.Employees.ToListAsync();
    await context.SaveChangesAsync();
    //etc..
}

and i am using Hangfire tool which support running background jobs to call this async method on timely basis, but un-fortuntly the hangefire tool does not support calling async methods directly . so to overcome this problem i created a sync version of the above method , as follow:-

public void Scan()
{
    ScanAsync("test").Wait();
}

then from the HangFire scheduler i am calling the sync method as follow:-

RecurringJob.AddOrUpdate(() => ss.Scan(), Cron.Minutely);

so i know that using .Wait() will mainly occupy the iis thread during the method execution ,, but as i mentioned i need to do it in this way as i can not directly call an async TASK inside the hangefire scheduler .

so what will happen when i use .Wait() to call the async method ?, will the whole method's operations be done in a sync way ? for example as shown above i have three async operations inside the ScanAsync() ;SingleOrDefualtAsync,ToListAsync & SaveChangesAsync, so will they be executed in sync way because i am calling the ScanAsync method using .wait() ?

2
Can't you call it like this? RecurringJob.AddOrUpdate(async () => await ss.ScanAsync(), Cron.Minutely); - Igor
One thing I believe you'll need to do is check the task to see if it had an exception. Something like this: var task = ScanAsync("test"); task.Wait(); if(task.Exception != null) {throw task.Texception}; If you don't do this, you may not find out if you had an exception in your background job. - mason
@Igor No, Hangfire specifically does not support directly executing an async method. See this issue. - mason
@Igor it will raise an error , and in this case the Startupclass where the "RecurringJob.AddOrUpdate(async () => await ss.ScanAsync(), Cron.Minutely);" is defined will need to be an async tasks which will not work, because i can not use await inside non-async task method - john Gu

2 Answers

3
votes

so what will happen when i use .Wait() to call the async method ?, will the whole method's operations be done in a sync way ? for example as shown above i have three async operations inside the ScanAsync() ;SingleOrDefualtAsync,ToListAsync & SaveChangesAsync, so will they be executed in sync way because i am calling the ScanAsync method using .wait() ?

The methods querying the database will still be executed asynchronously, but the fact that you're calling Wait means that even though you're releasing the thread, it wont return to the ASP.NET ThreadPool as you're halting it.

This is also a potential for deadlocks, as ASP.NET has a custom synchronization context which makes sure the context of the request is availiable when in a continuation of an async call.

I would recommend that instead, you'd use the synchronous API provided by entity-framework, as you won't actually be enjoying the scalability one can get from asynchronous calls.

Edit:

In the comments, you asked:

As i am currently doing with hangefire eliminate the async effect ? if yes then will it be better to use sync methods instead ? or using sync or async with hangeffire will be exactly the same

First, you have to understand what the benefits of async are. You don't do it because it's cool, you do it because it serves a purpose. What is that purpose? Being able to scale under load. How does it do that? When you await an async method, control is yielded back to the caller. For example, you have an incoming request, you query you database. You can either sit there and wait for the query to finish, or you can re-use that thread to serve more incomong requests. That's the true power.

If you don't actually plan on receiving a decent amount of requests (such that you'll starve the thread-pool), you won't actually see any benefit from async. Currently, the way you've implemented it, you won't see any of those benefits because you're blocking the async calls. All you'll see, possibly, are deadlocks.

0
votes

This very much depends on the way HangFire is implemented. If it just queuing tasks to be invoked in ThreadPool the only effect will be, that one of your threads will be blocked until the request is ended. However if there is a custom SynchronizationContext this can lead to a serious deadlock.

Consider, if you really want to wait for your scheduled job to be done. Maybe all you want is just a fire and forget pattern. This way your method will be like:

public void Scan()
{
    ScanAsync("test"); // smoothly ignore the task
}

If you do need to wait, you can instead try using async void method:

public async void Scan()
{
    await ScanAsync("test");
    DoSomeOtherJob();
}

There are many controversies about using async void as you cannot wait for this method to end, nor you can handle possible errors.

However, in event driven application this can be the only way. For more informations you can refer to: Async Void, ASP.Net, and Count of Outstanding Operations