c# - Catch Exception thrown by ASYNC DelegateCommand.Execute() when used as ICommand -
i'm using delegatecommands (prism) inside viewmodels expose outside icommands.
the caviat is: delegatecommand.execute implemented task execute(...) whereas icommand.execute implemented simple void execute(...).
i noticed this, because exceptions swallowed in execute handler. while typical behaviour asyncs not awaited did not expect occur icommand.execute (which has no sign of being async).
if execute icommand not able catch thrown exceptions delegatecommand since delegatecommands execute() method async whereas icommands not.
is there way catch thrown exception when using delegatecommand icommand?
[test] public void delegatetoicommandexecute() { var dcommand = new delegatecommand(() => { throw new exception(); }); icommand command = dcommand; command.execute(null); // doesn't fail due exception }
making nunit test case async works, visual studio complains have async method without await await icommand.execute
not possible.
casting explicitly delegatecommand possible fix unit test , not application's behaviour when exception thrown.
how should application working icommand
deal async underlying calls swallowing exception?
delegatebase (from delegatecommand inherits) defines execute async void execute
, awaits own task execute()
call). while calling icommand.execute end calling async void under hood.
exceptions swallowed in execute handler.
they should not have been. according the source code, icommand.execute
(correctly) implemented async void
method await
s asynchronous command.
this means icommand.execute
call not swallowing exception. however, cannot caught directly, because is asynchronous method. describe happens in detail in async best practices article: in case, exception re-raised within context of original call icommand.execute
.
when icommand.execute
called ui thread (i.e., mvvm binding), exception raised on ui thread, , whatever default behavior ui framework takes there (usually there's last-chance handler followed dialog/modal). when it's called unit test, uses whatever context provided unit test framework. describe async unit testing further in msdn article, gist of this: if make unit test async void
, (the current version of) nunit give context. don't rely on behavior; has been recognized poor design decision , removed next version of nunit v3. if unit test framework not provide context (which should case, , will case in future), exception re-raised on thread pool context, cause arbitrary thread within test runner fail. how test runner responds indeterminate: in fact, if have 1 test, possible test runner finish before exception seen, indeed appear "lost". possible test runner ignore exceptions can't match specific test.
instead, solution two-fold:
- expose viewmodel properties type
delegatecommand
instead oficommand
. unfortunate, , wish prism hadiasynccommand
expose instead, is. (fwiw, use ownasynccommand
implementiasynccommand
). - make unit tests
async task
(notasync void
), ,await
command's execution naturally. - if of code calling
execute
directly (instead of using command bindings), should updatedasync task
(orasync task<t>
) ,await
task returnedexecute
.
note exception in icommand.execute
not ignored @ runtime, have same effect exception raised event handler: must handled globally if handled @ all. not want. particularly problem asynchronous commands, since involve i/o operations prone errors that want handle gracefully.
to solve "meta-problem", you'll need revisit how want asynchronous commands behave. not uncommon put try
/catch
@ top of delegate, , update data-bound properties if fail. explore variety of similar solutions in msdn article on async mvvm commands, case "one size fits all" not apply.
Comments
Post a Comment