I have a class used as a parent to all my ViewModels. It contains a specific method used to call others methods, and show loading messages and message boxes on error (mainly):
public class BaseViewModel
{
async public void Try(Func<Task> action, string errorMessage = null, string waitMessage = null)
{
try
{
if (waitMessage != null)
ShowLoading(waitMessage);
await action();
}
catch (Exception e)
{
ShowError(errorMessage, e);
}
finally
{
HideLoading();
}
}
}
It is asynchronous, so my ShowLoading can be animated and stuff like that.
Is it correctly implemented?
It will always get anonymous (lambda) parameterless Tasks. My main issue is on how to actually construct these Tasks. Let's say I have a Command in a ViewModelBase's child, which call the following method when executed:
private void OnMyCommandExecute()
{
Try(() =>
{
Thread.Sleep(5000);
}, "error", "please wait");
}
It does not compile because Not all code paths return a value in lambda expression of type 'System.Func<System.Threading.Tasks.Task>'. Obvious, since we await this Func. Which leads me to the second question:
What should I put inside my Try call in this example for it to work?
I tried some really ugly things, and I really hope the answer is way different, else it will be a pain of readability:
Try(async () =>
{
return await Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
});
}
It does not compile, but at this point, it's better like that. Error on return: Since 'System.Func<System.Threading.Tasks.Task>' is anasyncmethod that returns 'Task', a return keyword must not be followed by an object expression. Did you intend to return 'Task<T>'?
Try accepts a method that returns a Task. In your first example you're providing a method that is void.
In your second example you're providing a method that returns a Task<Task>, but trying to use it in a context where a Task (non-generic) is expected.
If you want to use a non-async lambda, then just have that lambda return the Task that you want to use:
Try(()=>Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
}));
If you want to use an async lambda, then you need to await the task without returning it:
Try(async () => await Task.Factory.StartNew(() =>
{
SharePointService.Connect(Connection);
IsConnected = true;
}));
Note that there's no real purpose to having an async lambda here. These two snippets will both perform identically, but the second adds some extra overhead in code bloat as well as a whole state machine that just isn't actually needed at runtime.
What should I put inside my Try call in this example for it to work?
You need to make that lambda expression async by adding (surprisingly) async:
Try(async () =>
{
Thread.Sleep(5000);
}, "error", "please wait");
However, while this will enable you to create an async delegate there's nothing actually asynchronous about it (it blocks the calling thread with Thread.Sleep). If this is just an example then:
Try(async () =>
{
await Task.Delay(5000);
}, "error", "please wait");
is a better one. If it isn't don't use async at all.
Is it correctly implemented?
Not really. async void should almost always be avoided (unless in a UI event handler). Use async Task instead and make sure to await the returned task in some point to ensure the operation completed without any exceptions.
In order for Try to be as transparent as possible, I ended up with this.
async public Task Try(Action action, string errorMessage = null, string waitMessage = null)
{
try
{
if (waitMessage != null)
{
ShowLoading(waitMessage);
await Task.Factory.StartNew(() => action());
}
else
action();
}
catch (Exception e)
{
ShowError(errorMessage, e);
}
finally
{
HideLoading();
}
}
Therefore, you don't have to work with Task.Factory.StartNew or async/await when you call it:
Try(() =>
{
Thread.Sleep(5000);
}, "error", "please wait");
Related
I am currently working on a simulation class library that will be used by other programmers to create a dynamic simulation timeline and run these simulations.
In a sense, this is done by letting the user of the library specify actions on certain points in simulated time.
But I am now running into problems where an action that has been created by the user can be either async or not. To keep the simulation consistent though I need to execute both variants synchronously. I boiled the problem down to a very simple example:
class Program
{
static void Main()
{
Action action = ActionFromUser();
action();
Console.WriteLine("2");
Console.ReadKey();
}
static Action ActionFromUser()
{
Random rng = new Random();
if (rng.Next(0, 2) == 0) // 50/50
{
return async () =>
{
await Task.Delay(1000);
Console.WriteLine("1");
};
}
else
{
return () =>
{
Thread.Sleep(1000);
Console.WriteLine("1");
};
}
}
}
The problem now comes down to that the output is dependent on whether the action is async or sync which is the behavior I want to get rid of.
The action will always be without parameters and without a return value.
I do not want to use reflection (action.Method.IsDefined(typeof(AsyncStateMachineAttribute), ...).
I tried switching from Action to delegate (Func does not seem to be a good option since I have to specify a return value type) but I haven't found anything there that would help me.
An async lambda on an Action, is an async void. It's highly unlikely you'd want this . If in doubt change it to a Func<Task> and return a completed task on the synchronous result, and await the results.
Note : this is a contrived example, I'd imagine your code is a lot different, however it illustrates the point:
static Func<Task> FromUserAsync()
{
Random rng = new Random();
if (rng.Next(0, 2) == 0) // 50/50
{
return async () =>
{
await Task.Delay(1000);
Console.WriteLine("1");
};
}
else
{
return () =>
{
Thread.Sleep(1000);
Console.WriteLine("1");
return Task.CompletedTask;
};
}
}
One last note, you should probably catch any exceptions in the synchronous path and place them on the task, with Task.FromException
I have the following code:
var result = MessageBoxHelper.MsgBox
.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo)
.ContinueWith((answer) =>
{
if (answer.Result == MessageBoxResult.Yes)
{
Task<bool> asyncTask = ExecuteAsyncFunc();
//asyncTask.Unwrap(); ??
asyncTask.ContinueWith((a) =>
{
// More
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}
Invoked elsewhere like this:
public async Task<bool> ExecuteAsyncFunc()
{
await CallAnotherAsyncFunction();
}
I believe that I need to call Unwrap(), where I have tried to, because I am calling await inside a ContinueWith() block. However, I get the following error when I uncomment it:
Error CS1929 'Task' does not contain a definition for 'Unwrap'
and the best extension method overload
'TaskExtensions.Unwrap(Task)' requires a receiver of type
'Task'
Do I need to use Unwrap in this context, and if so, what am I doing wrong?
The short answer is to use await instead of ContinueWith:
var result = MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo);
if (answer == MessageBoxResult.Yes)
{
var a = await ExecuteAsyncFunc();
// More
}
The long answer is that ContinueWith has some dangerous default options (including using an ambient TaskScheduler), and await automatically does everything correctly for you. I go into more detail on my blog.
Do I need to use Unwrap in this context, and if so, what am I doing
wrong?
You need to Unwrap only when your return type is a Task<Task>, and you actually want to do something with the inner task.
For example:
Task<Task> exampleTask = Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
});
If I wanted to actually asynchronously wait on the inner Task, i'd call Unwrap and await on that:
Task exampleTask = await Task.Factory.StartNew(async () =>
{
await Task.Delay(1000);
}).Unwrap();
If for any reason you'd want to await on any task returned from your continuation, or want to monitor the inner task, then you'd call Unwrap. There is no need to do that here.
What you're doing is simply invoking an async method inside your ContinueWith, but it doesn't seem like you want to await the result at all.
I'd recommend using async-await wherever possible to reduce the verbosity of ContinueWith. Mixing those two together usually yields bad results as thing get rather complicated. Refactoring that code ends up much cleaner with much less cyclomatic complexity:
var result = await MessageBoxHelper.MsgBox.ShowAsync("Press Yes to proceed",
MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
bool answer = await ExecuteFuncAsync();
}
You say that you have to call unwrap due to your using await inside the ContinueWith; I don't actually see you using await though. I assume what you meant is that you want to await but are not sure how, and thus thought unwrapping is needed. If that is the case, then what you want is to just make your nested Task awaitable. You can do that by making the delegate you provide async
var result = MessageBoxHelper.MsgBox
.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo)
.ContinueWith(async (answer) =>
{
if (answer.Result == MessageBoxResult.Yes)
{
await ExecuteAsyncFunc();
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
public async Task<bool> ExecuteAsyncFunc()
{
await CallAnotherAsyncFunction();
}
This allows you to await within the delegate, within the ContinueWith call and not have to deal with unwrapping.
If you are going to do anything with the Task, such as return it without unwrapping, then this is the right way to go.
public Task<BoolOrSomeOtherReturnType> Foo()
{
return MessageBoxHelper.MsgBox
.ShowAsync /* Continue with etc here */
}
But if you are going to act on the results within the Foo method, then there is no need to use ContinueWith, just await it.
public async Task<bool> Foo()
{
var result = await MessageBoxHelper.MsgBox
.ShowAsync("Press Yes to proceed", MessageBoxButton.YesNo);
if (result == MessageBoxResult.Yes)
{
await ExecuteAsyncFunc();
return true;
}
return false;
}
That simplifies your code quiet a bit. The other thing to note is that your ExecuteAsyncFunc() method does not act on the return value. You simply await and do nothing else.
I noticed that you're not returning true/false, so I assume there is more code there that you just omitted for clarity sake. If that isn't the case, you can save yourself some overhead and just return the Task, allowing someone further up the callstack to await it instead. If your call to CallAnotherAsyncFunction returns Task<bool> then you can just return that call in the same way as well. You only need to await if you have to prevent the method from going any further so you can react to the result of the awaited method. If you don't have to, just return the Task.
public Task<bool> ExecuteAsyncFunc()
{
return CallAnotherAsyncFunction();
}
These two functions work but my problem is that the anonymous function (the one that receives the ServiceCheck as argument) is never called.
What do I have to do to make the CheckServiceConnection function return the string so that the anonymous function will run?
internal async void CheckServiceConnection()
{
await _da.CheckServiceConnection((ServiceCheck) =>
{
GeneralEventArgs args = new GeneralEventArgs();
args.GeneralObject = (object)ServiceCheck;
ServiceConnection(this, args);
});
}
public Task<string> CheckServiceConnection(Action<string> OnComplited)
{
var tcs = new TaskCompletionSource<string>();
ws.CheckServiceCompleted += (s, e) =>
{
if (e.Error != null)
tcs.TrySetException(e.Error);
else if (e.Cancelled)
tcs.TrySetCanceled();
else if (e.Result != null)
tcs.TrySetResult(e.Result);
};
try
{
ws.CheckServiceAsync();
}
catch (Exception ex)
{
ws.LogAsync(ex.Message, "DataManager.CheckServiceConnection()");
}
return tcs.Task;
}
When I write the CheckServiceConnection function like that - it is working
(and of course that CheckServiceConnection receives no arguments):
internal async void CheckServiceConnection()
{
var ServiceCheck = await _da.CheckServiceConnection();
GeneralEventArgs args = new GeneralEventArgs();
args.GeneralObject = (object)ServiceCheck;
ServiceConnection(this, args);
}
Your CheckServiceConnection method never calls the OnComplited action argument passed to it. That's why your anonymous function in the first function never gets called.
If you want to pass an anonymous function to CheckServiceConnection and have it called, you need to call it. That is, you add a line like this:
OnComplited( "some string" );
You're never calling the argument. It's not going to be called automatically.
However, I don't see much of a reason to do this anyway. You're awaiting the method, so why not just do this?
internal async void CheckServiceConnection()
{
var result = await _da.CheckServiceConnection();
GeneralEventArgs args = new GeneralEventArgs();
args.GeneralObject = result;
ServiceConnection(this, args);
}
Also, are you sure the CheckServiceConnection method shouldn't return Task? That's rarely a good idea.
All in all, it seems like you're mixing a lot of different approaches to notifications, asynchronous code etc. Maybe you want to revise your design a bit?
Consider this method:
public Status SendMessage(InParam inParam)
{
try
{
Task.Run(() => MethodAsync(inParam));
return Status.Success;
}
catch (Exception ex)
{
// Log the exception
return Status.Failed;
}
}
The MethodAsync method:
public async Task<Status> MethodAsync(InParam inParam)
{
try
{
return await processor.Process(inParam);
}
catch (Exception ex)
{
// Log exception
return Status.Failed;
}
}
and the Process method:
public async Task<Status> Process(InParam inParam)
{
try
{
IMessage endpoint = (IMessage)Activator
.CreateInstance(Type.GetType(_message.AgentDLLName), args);
_messageDispatchers.Add(endpoint);
foreach (IMessage dispatcher in _messageDispatchers)
{
await Task.Run(() => dispatcher.SendMessage(_message));
}
return await Task.Run(() => Status.Success);
}
catch (Exception ex)
{
// Log exception
return Status.Failed;
}
}
So the use case is that at the end of some processing workflow an email has to be sent.
This email sending part was taking some time and the user had to wait, so the original developer put this code.
I am trying to make SendMessage call MethodAsync and should not wait for it to return.
I have read that in an async workflow the complete stack needs to be async for it to work properly. The SendMessage is not marked async.
Is this the correct way to call MethodAsync since Task.Run returns an awaitable?
As MethodAsync is marked as async the call Task.Run(() => MethodAsync(inParam)); does not make much sense.
If you want to implement it as a "fire-and-forget"-call (BAD by the way) you can simply call MethodAsync(inParam);, because this also starts the awaited method inside MethodAsync "in its own task" (simplified) and returns that. If you then do not "wait that awaitable" your code inside SendMessage will continue to execute while it is still running.
BUT as already said: "fire-and-forget" is bad design in almost all cases. Can you explain your use-case a little more, so we may can provide a better approach?
UPDATE:
If there is REALLY no way to either make SendMessage async or have a synchronous counterpart of MethodAsync I recommend the following:
public Status SendMessage(InParam inParam)
{
try
{
return AsyncPump.Run(() => MethodAsync(inParam));
}
catch (Exception ex)
{
// Log the exception
return Status.Failed;
}
}
Using the AsyncPump you can return "the real result" and have no deadlocking problems.
In your example implementation of SendMessage the try/catch also makes less sense, as the method will very likely return way before any exeption will happen inside MethodAsync.
UPDATE 2 (after updated question):
I would recommend "going async all the way". Meaning also make SendMessage async (and all methods up to the UI) so you can await the "real result", but do not lock the UI while you are waiting...
UPDATE 3:
I would also change
foreach (IMessage dispatcher in _messageDispatchers)
{
await Task.Run(() => dispatcher.SendMessage(_message));
}
to
await Task.Run(() =>
{
foreach (IMessage dispatcher in _messageDispatchers)
dispatcher.SendMessage(_message);
});
This casues less context-switches. Or even:
await Task.Run(() => Parallel.ForEach(_messageDispatchers, d => d.SendMessage(_message)));
I have the following code
var exceptions = new ConcurrentQueue<Exception>();
Task task = Task.Factory.StartNew(() =>
{
try
{
Parallel.Invoke(
async () => await _aViewModel.LoadData(_someId),
async () => await _bViewModel.LoadData(_someId)
);
}
catch (Exception ex)
{
exceptions.Enqueue(ex);
}
}).ContinueWith((continuation) =>
{
if (exceptions.Count > 0) throw new AggregateException(exceptions);
});
I am using Task.StartNew here because the LoadData method use the Dispatcher.StartAsync method to invoke on the main UI thread internally.
The problem I have is that if I force _aViewModel.LoadData to throw an exception it is not caught in the Catch(Exception) clause (nor if I catch AggregateException). I don't understand why!?
Parallel.Invoke is not async-aware. So your async lambdas are being converted to async void methods, which have extremely awkward error semantics (they are not allowed to leave the async void method; instead, they are captured and re-raised directly on the SynchronizationContext that was active at the time the async void method started - in this case, the thread pool).
I'm not sure why you have the Parallel.Invoke in the first place. Since your method is already async, you could just do something like this:
Task task = Task.Factory.StartNew(async () =>
{
try
{
Task.WaitAll(
_aViewModel.LoadData(_someId),
_bViewModel.LoadData(_someId)
);
}
catch (Exception ex)
{
exceptions.Enqueue(ex);
}
})...
P.S. If you have the time, rethink the structure of this whole part of the code. Dispatcher.StartAsync is a code smell. The UI should be (asynchronously) requesting data; the data retrieval objects should not have to know about the UI.
Parallel.Invoke takes an array of Action delegates. It has no means of knowing that your delegates are actually async methods, and therefore it returns before your tasks have completed.
For an in-depth explanation of this behaviour, watch Lucian Wischik's Channel 9 video on the subject.
Try changing your code to use the Task.WhenAll method instead.
var aTask = _aViewModel.LoadData(_someId);
var bTask = _bViewModel.LoadData(_someId);
await Task.WhenAll(aTask, bTask);