Async/await in repository - c#

How can I make async/await method in repository? Should I use Task.Run?
public virtual void Add(T entity)
{
try
{
if (entity == null)
{
throw new ArgumentNullException(nameof(entity));
}
_context.Entry(entity);
Entities.Add(entity);
}
catch (DbEntityValidationException dbEx)
{
...
}
}

Using async/await is only useful if your function is async and you expect that the clients that call your functions are async (and their clients etc).
The reason is, because calling an async function does not mean that the function is performed. It means that a Task is scheduled to run the function performed by a thread in a pool of available threads. After the task is scheduled your function can continue with the next statement. When it needs the result of the scheduled task it awaits until the scheduled task is finished.
The advantage above starting a thread yourself is that this saves the overhead to start a new thread and do the cleanup afterwards. The disadvantage is that you are not certain that a thread is available the moment you schedule the task.
If your function is not declared async you still can schedule a task using Task.Run( () => OtherFunction(...)), but you can't await for it. To wait for the task to finish you have to call Task.Wait(...). In the meantime the thread that called your function can't continue. If this thread is the UI thread you'll notice this because your UI is not responsive.
So if you want to make proper use of other async functions, its best to declare your function async and return Task instead of void and Task<TResult> instead of TResult. Call the other async function, do other things and await the task before returning. The clients need to be async and return Task / Task<TResult>. The only async client that may return void is the event handler.
Example:
public async void button1_clicked(object sender, ...)
{
Task<int> task1 = this.DoSomethingAsync(...);
// while task1 is running you can do other things
// you can even schedule another task:
Task task2 = this.DoSomethingElseAsync(...);
// do other things. After a while you need the result of task1:
int task1Result = await task1;
// or if you want to await until both tasks are finished:
await Task.WhenAll(new Task[]{task1, task2});
int task1Result = task1.Result;
}
private async Task<int> DoSomethingAsync(...)
{
// schedule another async task and await:
await DoSomethingElseAsync(...);
return 42;
}
private async Task DoSomethingElseAsync(...)
{
// do something really important:
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
}
If you don't want to force your clients to be async, consider creating two versions, one async and one non-async. This is quite common. See definitions in Files, Streams, DbConnections etc.

Related

Creating/initializing task without starting it immediately

I want to create/initialize a Task object from a async Task method without starting it directly. When searching online I only find answer with Task created from void methods.
This is the task method I want to execute. As I need to do some web requests async, the method needs to be async. I want to do exception handling as well, so it can't be an async void.
private async Task WebRequestTask()
{
try
{
string ResponseText = await httpClient.GetStringAsync("https://fakeurl.com");
// process response code
}
catch (Exception ex)
{
// handle error
}
}
This is my main method where I want to create the task:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Task RequestTask = ... // here I want to initialize the task without starting
// chain continuation tasks to RequestTask
RequestTask.Start();
}
I've tried following solutions but nothing answers my need:
Solution 1
Task RequestTask = new Task(WebRequestTask);
Task RequestTask = WebRequestTask;
-> both cause a compiler error
Solution 2
Task RequestTask = Task.Run(WebRequestTask);
-> this start the task async and the current method continues (but here it could be possible an exception is thrown before the continuation tasks are chained)
Solution 3
Task RequestTask = WebRequestTask();
-> this start the task synchronously and chaining happens after the task is finished
Solutions 4
Task<Task> OuterTask = new Task<Task>(LoginToAzure);
await LoginAzureTask.Unwrap();
-> this start the outer task but the inner task is never called
How can I attach this Task method to an Task object, so that I can first attach continuation tasks/set some options and then start it? When it's possible, I'd like to use the cancellation token as well.
I want to create/initialize a Task object from a async Task method without starting it directly.
The best way to do this is to use delegates. E.g.:
private void StartTask()
{
TokenSource = new CancellationTokenSource();
Func<Task> taskFactory = () => WebRequestTask(TokenSource);
// chain continuation tasks
Task task = taskFactory(); // Start the task
... // await the task or save it somewhere
}
Side note: I strongly recommend implementing "chain continuation tasks" using await rather than ContinueWith. await is a clean and safe approach; ContinueWith is a low-level, dangerous method.
I want to do exception handling as well, so it can't be an async void.
Since you are handling the error inside WebRequestTask() that doesn't really matter.
The preferred option would be to make StartTask an async Task but if that isn't possible, make it async void. That takes care of the "without starting" requirement too.
So, keep it simple:
private async void StartTask()
{
TokenSource = new CancellationTokenSource();
try
{
await WebRequestTask(TokenSource.Token);
// here I want to initialize the task without starting -- irrelevant in an async void
await otherTask(); // continuation(s)
}
catch()
{
// handle residual errors
}
}
The asynchronous methods that are implemented with the async keyword are creating "hot" Tasks, in other words Tasks that are already started. You can defer the execution of an async method by wrapping it in another Task, thus creating a nested Task<Task>, like this:
{
TokenSource = new CancellationTokenSource();
var token = TokenSource.Token;
Task<Task> taskTask = new Task<Task>(() => WebRequestAsync(token));
Task task = taskTask.Unwrap(); // The task is not started yet
// Chain continuations to task
taskTask.RunSynchronously(TaskScheduler.Default);
// The task is now started
}
private async Task WebRequestAsync(CancellationToken cancellationToken)
{
try
{
string responseText = await httpClient.GetStringAsync(
"https://fakeurl.com", cancellationToken);
// Process response code
}
catch (Exception ex)
{
// Handle error
}
}
The taskTask variable is the wrapper that represents just the execution of the WebRequestAsync method, not the completion of the Task that this method creates. The wrapper task is completed immediately after calling the RunSynchronously method, and after that point your code will have no use for it. The task variable "unwraps" the wrapper, and represents the actual asynchronous web request. It is completed when the asynchronous operation completes.
As you can see this technique is rather cumbersome. You'll rarely see it in application code, because pure async-await composition is much more convenient to use.

SignalR is not Async?

While messing around with SignalR I found a behaviour that confuse me.
Calling StartCountDown from a client then make a call to Join behaves like
wait 10 seconds
Call clients CountDownStarted
Then call PlayerJoined
What I expected.
Call start CountDown, return
immediately call PlayerJoined
After 10 seconds complete CountDownStarted.
public class AHub : Hub
{
public async Task Join(string player)
{
await Clients.All.PlayerJoined(player);
}
public async Task StartCountDown()
{
await Task.Delay(10000);
await Clients.All.CountDownStarted();
}
}
This is from a SignalR Hub
This is a common misconception about the async and await pattern. Awaiting something does actually await the completion of the task.
If you want to run the task unobserved (or colloquially known as fire and forget), you could do thus
// task gets started hot and unobserved, remove the warning with a discard
_ = StartCountDownAsync();
Note : An exception that's raised in a method that returns a Task or Task<TResult> is stored in the returned task. If you don't await the task or explicitly check for exceptions, the exception is lost. If you await the task, its exception is rethrown.
As a best practice, you should always await the call.
Though, you have other options. Which is to start a task, complete other tasks, and then await the completion of the original
Given
public async Task SomeTask1() { }
public async Task SomeTask2() { }
public async Task SlowApiAsync() { }
You might want
var slowApiTask = SlowApiAsync();
await SomeTask1();
await SomeTask2();
await slowApiTask;
Or if you want to run all the tasks concurrently (and yet await them all)
var slowApiTask = SlowApiAsync();
var task1 = SomeTask1();
var task2 = SomeTask2();
await Task.WhenAll(slowApiTask,task1,task2)

async inside using statement without an await, is this safe?

If an asynchronous call is made within a using statement, and the result of the call is processed asynchronously (i.e. the method within which this happens is async and returns before the result is loaded and processed), can the using statement go out of scope?
In other words, is it safe to do something like this:
async void LoadAndProcessStuff()
{
using(var ctx = CreateDBContext()){
someResource.LoadStuffsAsync().ForEachAsync(stuff => ctx.Stuffs.Add(stuff));
}
}
or
async void LoadAndProcessStuff2()
{
using(var ctx = CreateDBContext()){
ctx.Stuffs.Select(stuff => someResource.LoadMoreStuffsAsync(stuff))
.ForEachAsync(stuff => ctx.Stuffs2.AddRange(stuff.Result));
}
}
Or could ctx be Disposed by the time the ForEachAsync is called and cause an exception?
In an async method, the "using" keyword should be safe. The compiler just rewrites it as a "finally" expression, which works like all other exception handling in async methods. Note that Dispose() should NOT block or be long running, it should just release resources - unless you want to cripple your server.
This is an easy test case for the safe case:
using System;
using System.Threading.Tasks;
namespace so {
sealed class Garbage : IDisposable {
public void Dispose() {
Console.WriteLine("Disposing Garbage");
}
}
// Garbage is safely Disposed() only after delay is done
class Program {
public static async Task Main() {
using (Garbage g = new Garbage()) {
Console.WriteLine("Delay Starting");
await Task.Delay(1000);
Console.WriteLine("Delay Finished");
}
}
}
}
However, if you have a non-async method that is returning a Task instead of awaiting it, it may be unsafe. That is because synchronous methods are only called once. There is no coroutine or state machine generated by compiler, so Dispose() HAS to be called right away - potentially while the Task is still running.
public Task DoWorkAsync()
{
using (var service = new Service())
{
var arg1 = ComputeArg();
var arg2 = ComputeArg();
// NOT SAFE - service will be disposed
// There is a high-probability that this idiom results
// in an ObjectDisposedException
return service.AwaitableMethodAsync(arg1, arg2);
}
}
For ref: http://www.thebillwagner.com/Blog/Item/2017-05-03-ThecuriouscaseofasyncawaitandIDisposable
If you want to safely do async/parallel stuff (and background stuff), the best is to use Tasks, async/await and ConfigureAwait. And keep in mind that it's always better to run the stuff inside a using before the execution leaves the method, so you have to think and encapsulate your code accordingly.
Here are some examples of what you may want to do :
execute a long running task asynchronously
public async Task ParentMethodAsync() {
DoSomeSyncStuff();
await DoBigStuffAsync();
DoSomeSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
await Task.Run(() => {
DoBigSyncStuff();
});
}
With that code your execution will be :
DoSomeSyncStuff
then DoBigSyncStuff will be run asynchronously inside
DoBigStuffAsync
ParentMethodAsync will wait for this to complete before running
DoSomeSyncStuffAfterAsyncBigStuff
execute a long running task asynchronously on background
public async Task ParentMethodAsync() {
DoSomeSyncStuff();
// original context/thread
await DoBigStuffAsync();
// same context/thread as original
DoSomeSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
// here you are on the original context/thread
await Task.Run(() => {
// this will run on a background thread if possible
DoBigSyncStuff();
}).ConfigureAwait(false);
// here the context/thread will not be the same as original one
}
Here same running order and blocking points, but with ConfigureAwait(false) you specify that you don't care synchronizing on the original context. Note that ParentMethodAsync context/thread is not impacted
execute stuff asynchronously and continue stuff at the same time
public async Task ParentMethodAsync() {
DoSomeSyncStuff();
Task bigStuffTask = DoBigStuffAsync();
DoSomeSyncStuffBeforeEndOfBigStuff();
await bigStuffTask;
DoAnotherSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
await Task.Run(() => {
DoBigSyncStuff();
});
}
With that code your execution will be :
DoSomeSyncStuff
then DoBigSyncStuff will start running asynchronously inside
DoBigStuffAsync
ParentMethodAsync will not wait for bigStuffTask to complete and will run DoSomeSyncStuffBeforeEndOfBigStuff
bigStuffTask (or DoBigStuffAsync) may complete before or after
DoSomeSyncStuffBeforeEndOfBigStuff does
the await bigStuffTask will force ParentMethodAsync to wait for
bigStuffTask to complete before running
DoAnotherSyncStuffAfterAsyncBigStuff
execute multiple stuff asynchronously
public async Task ParentMethodAsync() {
DoSomeSyncStuff();
Task bigStuffTask = DoBigStuffAsync();
Task bigStuff2Task = DoBigStuff2Async();
await Task.WhenAll(bigStuffTask, bigStuff2Task);
DoAnotherSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
await Task.Run(() => {
DoBigSyncStuff();
});
}
With that code your execution will be :
DoSomeSyncStuff
then DoBigSyncStuff will start running asynchronously inside DoBigStuffAsync
then bigStuff2Task will start running asynchronously inside DoBigStuff2Async
ParentMethodAsync will wait for bigStuffTask and bigStuff2Task to complete
once both completed, DoAnotherSyncStuffAfterAsyncBigStuff will run (synchronously)
execute stuff and don't wait/care for it to complete (Fire-and-forget)
public async Task ParentMethodAsync() {
DoSomeSyncStuff();
Task bigStuffTask = DoBigStuffAsync();
Task bigStuff2Task = DoBigStuff2Async();
// this one is fired and forgotten
DoFireAndForgetStuffAsync();
await Task.WhenAll(bigStuffTask, bigStuff2Task);
DoAnotherSyncStuffAfterAsyncBigStuff();
}
public async Task DoBigStuffAsync() {
await Task.Run(() => {
DoBigSyncStuff();
});
}
public async void DoFireAndForgetStuffAsync() {
Task.Run(() => {
try {
DoSomeFireAndForgetStuff();
} catch (Exception e) {
// handle your exception
}
});
}
With that code your execution will be :
DoSomeSyncStuff
then DoBigSyncStuff will start running asynchronously inside DoBigStuffAsync
then bigStuff2Task will start running asynchronously inside DoBigStuff2Async
then DoSomeFireAndForgetStuff will start running asynchronously
and your code will never care to know if it completed or not
afterwards
ParentMethodAsync will wait for bigStuffTask and bigStuff2Task to complete
once both completed, DoAnotherSyncStuffAfterAsyncBigStuff will run (synchronously)
Please note that async void method should be used wisely and should always have their own exception handling inside.
Otherwise, if an exception is thrown, as you have no way to control when and what the execution context will be in, you may end up with unexpected and random crashes.
There are other stuff you could learn from there for example, of with youtube livecode (e.g. this one from Xamarin Evolve in which James Clancey explains the thread aspects through a simple xamarin app)
I hope it will help you achieve what you want to !

Executing multiple tasks asynchronously in C#

Environment
Windows 7
Visual Studio
C#
What I'm trying to do
I'm trying to build an app to evaluate company products. For security, the description below is made abstract to some extent.
What this app does is changing a certain parameter in the product and see how a certain value of the product changes. So I need to do two things.
Change the parameter at a certain interval
Display the value in a textbox at a certain interval
The diagram is like this.
These tasks should be repeated until a cancel button is pressed.
The UI has these controls:
button1 : start button
button2 : cancel button
textbox1 : to show values obtained from the device
So here is the code I wrote.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
CancellationTokenSource cts = new CancellationTokenSource();
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
private async Task Task1()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
private void ChangeParameter(double param)
{
// change device paremeter
Console.WriteLine("devicep parameter changed : " + param);
}
private async Task Task2()
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(100);
int data = GetDataFromDevice();
UpdateTextBoxWithData(data);
}
cts.Token.ThrowIfCancellationRequested();
}
private int GetDataFromDevice()
{
//pseudo code
var rnd = new Random();
return rnd.Next(100);
}
private void UpdateTextBoxWithData(int data)
{
textBox1.AppendText(data.ToString() + "\n");
// debug
Console.WriteLine("data : " + data);
}
private void button2_Click(object sender, EventArgs e)
{
cts.Cancel();
}
}
Issues
However, there are two issues in this code.
UI freezes.
Task2 is never executed.
The second issue is derived from await since it executes tasks one by one. I could have used Task.Run() but this doesn't allow adding values to textBox since it's different from the UI thread.
How can I solve these issues? Any help would be appreciated.
First of all, async methods can be illusive as they won't turn your methods magically asynchronous. Instead, you can consider an async method as a setup for a state machine (see a detailed explanation here), where you schedule the chain of operations by the await calls.
For that reason, your async methods must execute as fast as possible. Do not do any blocking operation in such a setup method. If you have a blocking operation, which you want to execute in the async method, schedule it by an await Task.Run(() => MyLongOperation()); call.
So for example this will return immediately:
private async Task Task1()
{
await Task.Run(() =>
{
while (!cts.IsCancellationRequested)
{
Thread.Sleep(500);
ChangeParameter(0);
Thread.Sleep(1000);
ChangeParameter(10);
Thread.Sleep(500);
ChangeParameter(0);
}
}
}
A small remark: others may suggest to use Task.Delay instead of Thread.Sleep. I would say that use Task.Delay only if it is the part of the configuration of your state machine. But if the delay is intended to be used as a part of the long-lasting operation, which you don't want to split up, you can simply stay at the Thread.Sleep.
Finally, a remark for this part:
private async void button1_Click(object sender, EventArgs e)
{
await Task1();
await Task2();
}
This configures your tasks to be executed after each other. If you want to execute them parallel, do it like this:
private async void button1_Click(object sender, EventArgs e)
{
Task t1 = Task1();
Task t2 = Task2();
await Task.WhenAll(new[] { t1, t2 });
}
Edit: An extra note for long-lasting tasks: By default, Task.Run executes the tasks on pool threads. Scheduling too many parallel and long lasting tasks might cause starvation and the whole application may freeze for long seconds. So for long-lasting operation you might want to use Task.Factory.StartNew with TaskCreationOptions.LongRunning option instead of Task.Run.
// await Task.Run(() => LooongOperation(), token);
await Task.Factory.StartNew(() => LooongOperation(), token, TaskCreationOptions.LongRunning, TaskScheduler.Default);
The problem is you not using await in your tasks so they executing synchronously.
You should use something like this to maintain your UI responsive (NOTE this is not production code, I'm just showing an idea):
private void button1_Click(object sender, EventArgs e)
{
try
{
await Task.WhenAll(Task1(cts.Token), Task2(cts.Token));
}
catch (TaskCancelledException ex)
{
}
}
private async Task Task1(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(500, token); // pass token to ensure delay canceled exactly when cancel is pressed
ChangeParameter(0);
await Task.Delay(1000, token);
ChangeParameter(10);
await Task.Delay(500, token);
ChangeParameter(0);
}
}
private async Task Task2(CancellationToken token)
{
while (true)
{
token.ThrowIfCancellationRequested();
await Task.Delay(100, token);
int data = await Task.Run(() => GetDataFromDevice()); //assuming this could be long running operation it shouldn't be on ui thread
UpdateTextBoxWithData(data);
}
}
Basically, when you need to run something on background you should wrap that in Task.Run() and then await for result. Simply adding async to your method won't make this method asynchronous.
To make your code clearer, I suggest you to move methods like GetDataFromDevice or ChangeParameter to services layer. Also, take a look at IProgress as comments suggests to update your UI according to progress of some process.
There are many issues with this code:
async/await doesn't make the code asynchronous automagically. It allows you to await the results of already asynchronous operations. If you want to run something in the background that isn't already asynchronous, you need to use Task.Run or a similar method to start a Task.
await returns execution to the original synchronization context. In this case, the UI thread. By using Thread.Sleep, you are freezing the UI thread
You can't update the UI from another thread and that goes for Tasks too. You can use the IProgress interface though to report progress. A lot of BCL classes use this interface, just like CancellationToken
Maxim Kosov already cleaned up the code and shows how to properly use async/await and Task.Run, so I'll just post how to use IProgress< T> and its impelementation, Progress< T>
IProgress is used to publich a progress update with the IProgress< T>.Report method. Its default implementation, Progress, raises the ProgressChanged event and/or calls the Action<T> passed to its constructor, on the UI thread. Specifically, on the synchronization context captured when the class was created.
You can create a progress object in your constructor or your button click event, eg
private async void button1_Click(object sender, EventArgs e)
{
var progress=new Progress<int>(data=>UpdateTextBoxWithData(data));
//...
//Allow for cancellation of the task itself
var token=cts.Token;
await Task.Run(()=>MeasureInBackground(token,progress),token);
}
private async Task MeasureInBackground(CancellationToken token,IProgress<int> progress)
{
while (!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(data);
}
}
Note that using Thread.Sleep inside a task is not a good idea because it wastes a threadpool thread doing nothing. It's better to use await Task.Delay() which requires that the signature of the method change to async Task. There is a Task.Run(Func) overload just for this purpose.
The method is a bit different from Maxim Kosov's code to show that IProgress really communicates across threads. IProgress can handle complex classes, so you could return both a progress percentage and a message, eg:
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100,token);
int data = GetDataFromDevice();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
Here I'm just being lazy and return a Tuple<int,string>. A specialized progress class would be more appropriate in production code.
The advantage of using an Action is that you don't need to manage event handlers and the objects are local to the async method. Cleanup is performed by .NET itself.
If your device API provides truly asynchronous calls, you don't need Task.Run. This means that you don't have to waste a Task in a tigh loop, eg:
private async Task MeasureInBackground(CancellationToken token,IProgress<Tuple<int,string>> progress)
{
while(!token.IsCancellationRequested)
{
await Task.Delay(100, token);
int data = await GetDataFromDeviceAsync();
progress.Report(Tuple.Create(data,"Working"));
}
progress.Report(Tuple.Create(-1,"Cancelled!"));
}
Most drivers perform IO tasks using an OS feature called completion ports, essentially callbacks that are called when the driver completes an operation. This way they don't need to block while waiting for a network, database or file system response.
EDIT
In the last example, Task.Run is no longer needed. Just using await would be enough:
await MeasureInBackground(token,progress);

C# tasks are executed before Task.WhenAll

Why the tasks are executed before Task.WhenAll??
If you see here, from the below code snippet, first Console.WriteLine("This should be written first.."); should be printed because I am awaiting the tasks beneath to it..
But if you see the output result, the Tasks method result is being printed before the above statement. Ideally, the tasks method should be executed when I await them, but it seems that- the tasks methods are executed the moment I add them in tasks list. Why is it so?
Would you please do let me know why is this happening??
Code:
public static async Task Test()
{
var tasks = new List<Task>();
tasks.Add(PrintNumber(1));
tasks.Add(PrintNumber(2));
tasks.Add(PrintNumber(3));
Console.WriteLine("This should be written first..");
// This should be printed last..
await Task.WhenAll(tasks);
}
public static async Task PrintNumber(int number)
{
await Task.FromResult(0);
Console.WriteLine(number);
}
Output
When you call an async method you get a "hot" task in return. That means that the task already started running (and maybe even completed) before you get to await them. That means that it's quite possible for the tasks to run and complete before the call to Task.WhenAll.
In your case however, while the PrintNumber is marked async it isn't asynchronous at all since you're using Task.FromResult. The synchronous part of an asynchronous method (which is the part until you await an asynchronous task) is always executed synchronously on the calling thread and is done before the call returns. When you use Task.FromResult you get a completed task so all your method is just the synchronous part and is completed before the call returns.
When you await a completed task (as is created by Task.FromResult, it completes synchronously. This means that in your example, nothing is actually happening asynchronously, which explains the order of execution.
If instead, you were to
await Task.Yield();
you'd see output more in line with your expectations.
Task.FromResult won't cause yield and the task will be executed on the same thread. To achieve what you want you can do this:
public static async Task Test()
{
var tasks = new List<Task>();
tasks.Add(PrintNumber(1));
tasks.Add(PrintNumber(2));
tasks.Add(PrintNumber(3));
Console.WriteLine("This should be written first..");
// This should be printed last..
await Task.WhenAll(tasks);
}
public static async Task PrintNumber(int number)
{
await Task.Yield();
Console.WriteLine(number);
}
If you want a Task or tasks to run after something else, its easiest to write your code accordingly.
public static async Task Test()
{
Console.WriteLine("This should be written first..");
// These should be printed last..
await Task.WhenAll(new[]
{
PrintNumber(1),
PrintNumber(2),
PrintNumber(3)
});
}
following on from your comment.
So we have some functions,
async Task<Customer> GetRawCustomer()
{
...
}
async Task<string> GetCity(Customer customer)
{
...
}
async Task<string> GetZipCode(Customer customer)
{
...
}
We could use them like this
var rawCustomer = await GetRawCustomer();
var populationWork = new List<Task>();
Task<string> getCity;
if (string.IsNullOrWhiteSpace(rawCustomer.City))
{
getCity = GetCity(rawCustomer);
populationWork.Add(getCity);
}
Task<string> getZipCode;
if (string.IsNullOrWhiteSpace(rawCustomer.City))
{
getZipCode = GetZipCode(rawCustomer);
populationWork.Add(getZipCode);
}
...
await Task.WhenAll(populationWork);
if (getCity != null)
rawCustomer.City = getCity.Result;
if (getZipCode != null)
rawCustomer.ZipCode = getZipCode.Result;

Categories

Resources