0

I have a daily scheduler sequential scenario that needs to run every midnight:

  1. Check_Tenant_Expiry and Get its return value (true/false)
  2. Run_Daily_Report (pass the returning value from Check_Tenant_Expiry)

I expect to do Check_Tenant_Expiry and after it completed it will continue with Run_Daily_Report, I use the code below

bool _xxx = await Check_Tenant_Expiry().ContinueWith(t =>
{
    Run_Daily_Report(_xxx); // THE WARNING GOES HERE
}, TaskContinuationOptions.OnlyOnRanToCompletion);

public static async Task<bool> Check_Tenant_Expiry()
{ 
   bool _ret = false;
   ...
   ...
   return await Task.FromResult(_ret);
}

public static async Task Run_Daily_Report()
{ .... }

questions:

  1. Why I got a warning on Run_Daily_Report:

Severity Code Description Project File Line Suppression State Warning CS4014 Because this call is not awaited, execution of the current method continues before the call is completed. Consider applying the 'await' operator to the result of the call.

  1. I am not sure that I already have correct logic for the sequential multilevel process like first I need to run Check_Tenant_Expiry() until it finished completely and then continue with Run_Daily_Report()

I need advice.

Many thanks in advance Don

Don2
  • 313
  • 3
  • 12
  • 1
    I'm curious why you don't just `var x = await CheckTenantExpiry(); await RunDailyReport(x);`? – Caius Jard May 30 '21 at 04:36
  • By the way, your methods should really have an "Async" suffix on the name – Caius Jard May 30 '21 at 04:42
  • 1
    @CaiusJard - By that token, it's also weird to have underscores. The normal naming convention is Pascal-casing. – Enigmativity May 30 '21 at 04:56
  • 1
    @Enigmativity Yep, Actually it is. I think 'CheckTenantExpiryAsync()' would be the best. – Masuri May 30 '21 at 05:00
  • 2
    Why do `await Task.FromResult(_ret)`? That seems awkward. – Enigmativity May 30 '21 at 05:05
  • How about making body of Check_Tenant_Expiry() like return await Task.Run(() => { bool returnValue = false; /* logic here */ return returnValue; }); – Masuri May 30 '21 at 05:12
  • Questions are expected to have exactly one question in them. Posts with more than one question lack focus/are too broad. See duplicate for the answer to your first question. The warning is emitted because failing to observe the `Task` object returned by the call, either by awaiting it or storing it in a variable, can result in serious problems, as the duplicates explain. – Peter Duniho May 30 '21 at 06:27
  • Note that your second question really isn't appropriate for this site anyway. If the code works and you just want someone to say whether it's written how it should be, that's a code-review/software-engineering type question and belongs on another site (if you can make sure you present the question in the manner required for one of those sites). Consider e.g. codereview.stackexchange.com and softwareengineering.stackexchange.com. – Peter Duniho May 30 '21 at 06:29

3 Answers3

3

I expect to do Check_Tenant_Expiry and after it completed it will continue with Run_Daily_Report

You shouldn't use ContinueWith for this. ContinueWith is a low-level method with dangerous default behavior. At the very least, you must pass a TaskScheduler. In addition, ContinueWith doesn't understand asynchronous code, so if you pass async code in, you'll also need to use Unwrap.

Instead, you should just use await, which is simpler, more maintainable, and does the right thing by default:

bool _xxx = await Check_Tenant_Expiry();
await Run_Daily_Report(_xxx);

On a side note, this seems suspect:

return await Task.FromResult(_ret);

That's literally a less efficient form of this:

return _ret;

I suspect that the await Task.FromResult was added to avoid a compiler warning telling you that the method will run synchronously. If you were in fact getting that warning, then the proper solution is to either make the method properly asynchronous (not adding an await just to make the warning go away), or to remove the async and leave the method synchronous.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
0

Try this

await Check_Tenant_Expiry().ContinueWith( async (t) =>
{
     await Run_Daily_Report(); 
}, TaskContinuationOptions.OnlyOnRanToCompletion);
Biju Kalanjoor
  • 532
  • 1
  • 6
  • 12
  • 2
    Why? Two awaits with a standard `try`/`catch` would be better. – Enigmativity May 30 '21 at 04:57
  • 1
    Yes, I see that. My question still stands. – Enigmativity May 30 '21 at 06:49
  • @Enigmativity, if we proceed with ContinueWith() we have lot more option with TaskContinuationOptions . – Biju Kalanjoor May 30 '21 at 07:40
  • Which of those are useful here other than `OnlyOnRanToCompletion`? – Enigmativity May 30 '21 at 09:52
  • 2
    You're missing a `TaskScheduler` and passing an `async` lambda. `ContinueWith` doesn't work as expected in either of these scenarios. If you want to use `ContinueWith`, then you should *always* pass a `TaskScheduler`, and if you're passing an `async` lambda, you should also use `Unwrap`. Or just use `await`, which is simpler, cleaner, and does the right thing by default. – Stephen Cleary May 30 '21 at 12:28
-1
  1. You made a function with async keyword, which means you told compiler excute "Run_Daily_Reporty" method by asynchronously. But you didn't execute the method with await keyword, which means It's run by synchronously. That's why compiler told you "it's right?"

Try this

// You must use async keyword when you want to use await keyword in lambda function.
await Check_Tenant_Expiry().ContinueWith(async(t) =>
{
    await Run_Daily_Report(t.Result);
}, TaskContinuationOptions.OnlyOnRanToCompletion);
  1. I think you made code the way you wanted to work.

I think you might not know the async/await keywords yet. They can make your asynchronous code looks like synchronous code that we've been making every day, which means it's way more easy to understand. How about making code like below. It's going to make the same result with the code you made.

bool result = await Check_Tenant_Expiry();
await Run_Daily_Report(result);
Masuri
  • 894
  • 2
  • 9
  • 19
  • Btw, how to get the value from Check_Tenant_Entry() and use it in Run_Daily_Report() simple example, let's say Check_Tenant_Expiry() returns Task bool _xx = await Check_Tenant_Expiry().ContinueWith(async(t) => { await Run_Daily_Report(_xx); }, TaskContinuationOptions.OnlyOnRanToCompletion); – Don2 May 30 '21 at 04:24
  • It's easy! you just use 't.Result' in lambda function like await Run_Daily_Report(t.Result); When you want to know more about this check this msdn page [https://learn.microsoft.com/en-us/dotnet/api/system.threading.tasks.task-1.result?view=net-5.0] – Masuri May 30 '21 at 04:35
  • The `ContinueWith` is problematic. You're missing a `TaskScheduler` and passing an `async` lambda. `ContinueWith` doesn't work as expected in either of these scenarios. If you want to use `ContinueWith`, then you should *always* pass a `TaskScheduler`, and if you're passing an `async` lambda, you should also use `Unwrap`. Or just use `await`, which is simpler, cleaner, and does the right thing by default. – Stephen Cleary May 30 '21 at 12:29
  • @StephenCleary Even if I don't want to do something on the UI thread, Should I alway pass TaskScheduler to ContinueWith? – Masuri May 30 '21 at 14:55
  • @Masuri: You should always pass a `TaskScheduler` to `ContinueWith`. It doesn't have anything to do with UI threads. There are no known exceptions to that rule. – Stephen Cleary May 30 '21 at 22:42