Reputation: 25
I am using MVVM Light and there is a interface IDialogService which for used for showing dialogs. This interface has been implemented in App.xaml.cs
One specific method is interesting:
Task<bool> ShowMessage(string message, string title, string buttonConfirmText, string buttonCancelText, Action<bool> afterHideCallback);
The method is implemented as:
public Task<bool> ShowMessage(string message, string title, string buttonConfirmText,
string buttonCancelText,
Action<bool> afterHideCallback)
{
return Task.Factory.StartNew(() =>
{
var style = new Style(typeof(MessageBox));
style.Setters.Add(new Setter(MessageBox.OkButtonContentProperty, buttonConfirmText));
style.Setters.Add(new Setter(MessageBox.CancelButtonContentProperty, buttonCancelText));
var result = MessageBox.Show(_GetActiveWindow(), message, title,
MessageBoxButton.OKCancel,
MessageBoxImage.Question, style) == MessageBoxResult.OK;
if (afterHideCallback != null) afterHideCallback(result);
return result;
Where _currentTaskScheduler = TaskScheduler.FromCurrentSynchronizationContext();
is defined in OnStartup
So usually we should call this method with await in front to get boolean value:
var result = await DialogService.ShowMessage(
Resources.Areyousure,Resources.Warning,
Resources.Yes, Resources.No, null);
So far so good. Now I have a wrapper method to execute code and catch exceptions and then show messaged box with error.
public bool TryCatchExecution(Action action, string successMessage = null)
{
try
{
action();
if (!string.IsNullOrEmpty(successMessage))
DialogService.ShowMessage(successMessage, Resources.Success);
return true;
}
catch (LogException ex)
{
DialogService.ShowError(ex.Error.LogMessage, Resources.Error, Resources.OK, null);
}
catch (Exception ex)
{
DialogService.ShowError(ex.Message, Resources.Error, Resources.OK, null);
}
return false;
}
And now I have a problem. If I use like sample A the GUI thread is blocked at the line var result = DialogService.ShowMessage
. But if I use as in sample B the GUI thread in not blocked, message box shown and everything works like it should. Until I get an exception. The exception is unhanded by code. The error is "A first chance exception of type 'System.ServiceModel.FaultException`1' occurred in mscorlib.dll" and application crashes. As I have been reading this has something to do with SynchronizationContext.
//Sample A
private void ExecuteDeleteCommand()
{
TryCatchExecution(() =>
{
var result = DialogService.ShowMessage(
Resources.Areyousure,
Resources.Warning,
Resources.Yes,
Resources.No, null).Result;
if (!result) return;
_datalayer.DeleteField(FieldSelected);
Refresh();
FieldEdit = new MsgSqlFieldMapping();
RaisePropertyChanged("SqlRepository");
DialogService.ShowMessage(Resources.OperationSucceeded, Resources.Success);
});
}
//Sample B
private void ExecuteDeleteCommand()
{
TryCatchExecution(async () =>
{
var result =await DialogService.ShowMessage(
Resources.Areyousure,
Resources.Warning,
Resources.Yes,
Resources.No, null);
if (!result) return;
_datalayer.DeleteField(FieldSelected);
Refresh();
FieldEdit = new MsgSqlFieldMapping();
RaisePropertyChanged("SqlRepository");
await DialogService.ShowMessage(Resources.OperationSucceeded, Resources.Success);
});
}
Please help me to understand whats happening here and how to deal with it.
THNX a lot.
Upvotes: 0
Views: 478
Reputation: 456437
Your problem is due to async void
- specifically, by passing an async
lambda as an argument of type Action
, you're creating an async void
method. One of the problems with async void
methods is that you can't catch exceptions (at least, not the normal way).
To resolve this, create an overload of your helper method that takes the async equivalent of Action
, which is Func<Task>
:
public async Task<bool> TryCatchExecution(Func<Task> action, string successMessage = null)
{
try
{
await action();
if (!string.IsNullOrEmpty(successMessage))
DialogService.ShowMessage(successMessage, Resources.Success);
return true;
}
catch (LogException ex)
{
DialogService.ShowError(ex.Error.LogMessage, Resources.Error, Resources.OK, null);
}
catch (Exception ex)
{
DialogService.ShowError(ex.Message, Resources.Error, Resources.OK, null);
}
return false;
}
Upvotes: 2