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.
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;
}
Related
I'm writing an unhappy flow unit test.
In my code I setup a function to throw async and later I validate that the function was called once.
This scenario fails and succeeds irregularly, as sometimes it shows no calls were made and sometimes the function is called once (as planned).
Do you know why?
Let me know if any more code is needed.
Thanks!
The test:
[Fact]
public async Task ContainerScan_ControllerTest_ErrorFlow()
{
//Given
TestsRegistrar.ContainerClient.Reset();
TestsRegistrar.ContainerClient.Setup(x => x.MyAsyncFunc(It.IsAny<Guid>(), It.IsAny<List<ContainerScanRecord>>())).ThrowsAsync(new Exception("bad stuff happened"));
var controller = new ContainersRiskGeneratorController(TestsRegistrar.ContainersGenerator, new Mock<IExtendedLogger>().Object, new Mock<IExtendedLoggerEnricher>().Object);
var scanId = Guid.NewGuid();
//When
var result = await controller.SomeUpperFuncthatCallsMyAsyncFunc(scanId, new RiskGenerator.Dto.V3.ContainersScanRequest
{
ContainerRecords = new List<RiskGenerator.Dto.V3.SomeDto>
{
new RiskGenerator.Dto.V3.SomeDto{
params here
}
}
});
//Then
result.Should().BeEmpty();
TestsRegistrar.ContainerClient.Verify(x => x.MyAsyncFunc(It.IsAny<Guid>(), It.IsAny<List<ContainerScanRecord>>()), Times.Once, "Container Engine should be called once");
}
UDPATE #1: added the calling funcs
The code for SomeUpperFuncthatCallsMyAsyncFunc:
[HttpPost("someUrl/{scanId:Guid?}")]
[DisableRequestSizeLimit]
public async Task<IReadOnlyCollection<SomeDto>> SomeUpperFuncthatCallsMyAsyncFunc(Guid? _, [FromBody] ContainersScanRequest request)
{
try
{
Ensure.NotNull(nameof(request), request);
_extendedLoggerEnricher.Enrich(new { request.ScanId });
_logger.Info("GenerateContainerRiskAsync called", new { request.ScanId });
var response =
await _riskGenerator.TheCallingFuncToMyAsyncFunc(
request.ScanId,
request.ContainerRecords
.Where(x=> !string.IsNullOrEmpty(x.DockerFilePath))
.Select(ContainerScanRiskRecordDto.ToContainerScanRecord).ToList())
.ConfigureAwait(false);
return response.Select(VulnerableContainerScanRecordDto.ToContainerScanVulnerablerRecord).ToArray();
}
catch (Exception exception)
{
_logger.Error("Exception thrown during generate GenerateContainerRiskAsync", exception, new { request.ScanId });
StatusCode((int)HttpStatusCode.InternalServerError);
return new List<SomeDto>();
}
}
And the func that calls directly to MyAsyncFunc:
public async Task<List<(ContainerScanRecord record, ImageVulnerabilitiesDto vulnerabilities)>> TheCallingFuncToMyAsyncFunc(Guid id, IReadOnlyCollection<ContainerScanRecord> containerRecords)
{
try
{
return await
_containerEngineClient.MyAsyncFunc(id, containerRecords).ConfigureAwait(false);
}
catch (Exception exception)
{
_logger.Error($"[{id}] Could not generate GenerateContainerRiskDataAsync: {exception.Message}", exception);
throw;
}
}
UDPATE #2: figured it out!
Well... it turned out to be a nasty race condition with some other class that initialized the data.
That's why it happened randomly.
Sorry for the hustle and thank you all for your time.
As I found out recently, when function throws, its invocation can not be counted. So this is why you may experience such problems.
What you could do is to verify that logger logs an error (the same way you assert that your method was called).
I am writing a Visual Studio extension in C# and I get a strange behavior on managing exception and displaying error messages. Basically, I just want to add some details to the exception message to help me investigate in case of a problem.
It all starts from a command on a context menu item and I suspect it may be related to threads management behind the async/await mechanism. But I am not sure I guess correctly and I am not able to find any solution. HELP!
It starts from my menu item callback:
internal sealed class My_RunAnalysis
{
//...
public static async Task InitializeAsync(AsyncPackage package)
{
// Switch to the main thread - the call to AddCommand in PS_RunAnalysis's constructor requires
// the UI thread.
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
OleMenuCommandService commandService = await package.GetServiceAsync((typeof(IMenuCommandService))) as OleMenuCommandService;
Instance = new My_RunAnalysis(package, commandService);
}
//...
private async void ExecuteAsync(object sender, EventArgs e)
{
try
{
await My_ViewModel.RunAnalysisAsync();
}
catch (Exception exc)
{
await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);
MessageBox.Show(exc.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning);
}
}
}
//...
class My_ViewModel
{
async public static Task RunAnalysisAsync()
{
await My_Model.GetResultsListAsync();
}
}
//...
class My_Model
async public static Task GetResultsListAsync()
{
ResultsList = new My_ResultsList();
var rawResultsList = await QueryServerAsync<RawResultsListResponse>("GET", My_Request.GetResults());
//...
}
async public static Task<JsonResponse> QueryServerAsync<JsonResponse>(string method,
string request)
{
try
{
HttpResponseMessage response;
switch (method)
{
case "GET":
response = await _httpClient.GetAsync(request);
break;
case "POST":
default:
StringContent httpContent = new StringContent("", Encoding.UTF8, "application/json");
response = await _httpClient.PostAsync(request, httpContent);
break;
}
if (!response.IsSuccessStatusCode) //<<<<<<CASE #1
{
throw new My_Exception(
response.ReasonPhrase,
"Exception while querying server for " + request);
}
string serializedJson = await response.Content.ReadAsStringAsync();
// CASE #2>>>>>
var jsonResponse = serializer.Deserialize<JsonResponse>(serializedJson);
return jsonResponse;
}
catch (Exception e)
{
throw new My_Exception(
e.Message,
"Exception while querying server for " + request);
}
}
The strange thing is that:
When an error occurs in case #1 and I create a custom exception (my server responded but there was an internal error and I have a clean error code), the MessageBox in the catch of My_ViewModel::RunAnalysisAsync() will show correctly and immediately.
When a native exception occurs in case #2 (my server responded with malformed json and I get an exception from serializer.Deserialize), the MessageBox in the catch of My_ViewModel::RunAnalysisAsync() will not show, the IDE will hang for around 15s before restarting (and still not show the MessageBox).
Any idea what's wrong?
Thanks!
EDIT:
Seeing that the template for my custom command initializes also with SwitchToMainThreadAsync, I have tried to do the same with the Execute method. I updated the code above but it still does not work: an exception thrown by serializer.Deserialize will still freeze the UI for 10 to 15s and the MessageBox will not show!
Also note that the debugger can step immediately on "await ThreadHelper.JoinableTaskFactory.SwitchToMainThreadAsync(package.DisposalToken);" and go on next step to MessageBox. I would tend to suppose it means that the switch to the main thread is immediate but there is still something wrong...
Any idea what's wrong? I really need to capture exceptions a reliable way...
I could not find any explanation to the MessageBox working on a case and not on the other one. I ended up going to some log solution using FileStream.WriteAsync. Hence everything keeps async and I don't have to use MessageBox anymore.
use await JoinableTaskFactory.SwitchToMainThreadAsync(); to switch to the main thread JoinableTaskFactory is a member of AsyncPackage.
If it still doesn't work try
public static void ShowMessageBox(string title, string text)
{
Microsoft.VisualStudio.Shell.ThreadHelper.ThrowIfNotOnUIThread();
IVsUIShell uiShell = Microsoft.VisualStudio.Shell.ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShell)) as IVsUIShell;
Guid clsid = Guid.Empty;
int result;
Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox(
0,
ref clsid,
title,
text,
string.Empty,
0,
OLEMSGBUTTON.OLEMSGBUTTON_OK,
OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST,
OLEMSGICON.OLEMSGICON_INFO,
0, // false
out result));
}
Mvvm light RelayCommand is causing me some headache with async methods. WPF button is tied with the following relayCommand:
private RelayCommand _importDeviceCommand;
/// <summary>
/// Import device button for SelectDeviceView.
/// </summary>
public RelayCommand ImportDeviceCommand
{
get
{
return _importDeviceCommand
?? (_importDeviceCommand = new RelayCommand(async () => await AddDeviceClickExecute(),
() => _selectedCableType != null
&& _selectedAddDevice != null
&& _selectedPointNames != null
&& _selectedPointNames.Any()));
}
}
I'm probably misusing it in some form because keep occasionally encountering the following exception always when the method AddDeviceClickExecute is done.
An unhandled exception of type
'System.Reflection.TargetInvocationException' occurred in mscorlib.dll
What are the possible solutions?
Could it have something to do with the lambda method calls?
Therefore, how can I refactor the relayCommand in such a way that
no lambda is used?
EDIT 1
The called async method, the try/catch is not unfortunately making any difference?
private async Task AddDeviceClickExecute()
{
_linkTheSocket = true;
var deviceImporter = new DeviceImporterAsync2(_projectContext, _deviceContext);
var progress = new Progress<string>(status =>
{
_importDeviceProgress = status;
RaisePropertyChanged("ImportDeviceProgress");
});
try
{
await deviceImporter.InvokeSimpleDeviceImport(UserSelectedSockets, progress);
}
catch (Exception ex)
{
MessageBox.Show(ex.ToString(), "Exception during simple device import", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
EDIT 2
The following exception occurs right after the AddDeviceClickExecute exits.
EDIT 3
Turned out that the way I was utilising async and relayCommand had
nothing to do with my exception. Problem was fixed.
You'll see an unhandled exception any time you have an exception escape an async void method (namely, the lambda you're passing to RelayCommand).
This is normal and expected behavior; it's the same behavior you'd see from a synchronous RelayCommand lambda, except for the TargetInvocationException wrapper. As others have noted, just examine the InnerException to see the actual underlying exception.
If you want to catch these exceptions, you should wrap the entire body of the async void method in a try/catch. This is possible but somewhat awkward within a lambda:
return _importDeviceCommand
?? (_importDeviceCommand = new RelayCommand(async () =>
{ try { await AddDeviceClickExecute(); } catch (Exception ex) { ... } },
() => _selectedCableType != null
&& _selectedAddDevice != null
&& _selectedPointNames != null
&& _selectedPointNames.Any()));
But that's getting really awkward IMO. Better to split it into another method:
private async void ImportDevice()
{
try
{
await AddDeviceClickExecute();
}
catch (Exception ex)
{
...
}
}
return _importDeviceCommand
?? (_importDeviceCommand = new RelayCommand(ImportDevice,
() => _selectedCableType != null
&& _selectedAddDevice != null
&& _selectedPointNames != null
&& _selectedPointNames.Any()));
I am new to TAP and async/await in practice in C# so I may have some bad code smells here, so be gentle. :-)
I have a service method that looks as follows:
public OzCpAddOrUpdateEmailAddressToListOutput AddOrUpdateEmailAddressToList(
OzCpAddOrUpdateEmailAddressToListInput aParams)
{
var result = new OzCpAddOrUpdateEmailAddressToListOutput();
try
{
var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
Task<Member> mailChimpResult =
mailChimManager.Members.AddOrUpdateAsync(
aParams.Listid,
new Member
{
EmailAddress = aParams.EmailAddress
});
//Poll async task until it completes.
//Give it at most 8 seconds to do what it needs to do
var outOfTime = DateTime.Now.AddSeconds(8);
while (!mailChimpResult.IsCompleted)
{
if (DateTime.Now > outOfTime)
{
throw new Exception("Timed out waiting for MailChimp API.");
}
}
//Should there have been a problem with the call then we raise an exception
if (mailChimpResult.IsFaulted)
{
throw new Exception(
mailChimpResult.Exception?.Message ??
"Unknown mail chimp library error.",
mailChimpResult.Exception);
}
else
{
//Call to api returned without failing but unless we have
//the email address subscribed we have an issue
if (mailChimpResult.Result.Status != Status.Subscribed)
{
throw new Exception(
$"There was a problem subscribing the email address
{aParams.EmailAddress} to the mailchimp list id
{aParams.Listid}");
}
}
}
catch (Exception ex)
{
result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
}
return result;
}
But when I call in from MVC Controller action mailChimpResult.IsCompleted always returns false and eventually I hit the timeout.
I realise this is because I am not chaining the async calls as per HttpClient IsComplete always return false and because of different threads this behaviour is "expected".
However I want my service method to hide the complexity of the async nature of what it is doing and merely do what appears to be a synchronous call in my action method namely:
var mailChimpResult =
_PlatformMailChimpService.AddOrUpdateEmailAddressToList(
new OzCpAddOrUpdateEmailAddressToListInput
{
EmailAddress = aFormCollection["aEmailAddress"],
Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,
MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value
});
if (mailChimpResult.Result == true)
{
//So something
}
Ideally you should avoid the .Result and .IsFaulted properties of the Task and Task<T> objects, that was code smell number one. When you're using these objects you should use async and await through the entire stack. Consider your service written this way instead:
public async Task<OzCpAddOrUpdateEmailAddressToListOutput>
AddOrUpdateEmailAddressToList(
OzCpAddOrUpdateEmailAddressToListInput aParams)
{
var result = new OzCpAddOrUpdateEmailAddressToListOutput();
try
{
var mailChimManager = new MailChimpManager(aParams.MailChimpApiKey);
Member mailChimpResult =
await mailChimManager.Members.AddOrUpdateAsync(
aParams.Listid,
new Member
{
EmailAddress = aParams.EmailAddress
});
}
catch (Exception ex)
{
result.ResultErrors.AddFatalError(PlatformErrors.UNKNOWN, ex.Message);
}
return result;
}
Notice that I was able to remove all of that unnecessary polling and examining of properties. We mark the method as Task<OzCpAddOrUpdateEmailAddressToListOutput> returning and decorate it with the async keyword. This allows us to use the await keyword in the method body. We await the .AddOrUpdateAsync which yields the Member.
The consuming call into the service looks similar, following the same paradigm of async and await keywords with Task or Task<T> return types:
var mailChimpResult =
await _PlatformMailChimpService.AddOrUpdateEmailAddressToList(
new OzCpAddOrUpdateEmailAddressToListInput
{
EmailAddress = aFormCollection["aEmailAddress"],
Listid = ApplicationSettings.Newsletter.MailChimpListId.Value,
MailChimpApiKey = ApplicationSettings.Newsletter.MailChimpApiKey.Value
});
if (mailChimpResult.Result == true)
{
//So something
}
It is considered best practice to postfix the "Async" word to the method, to signify that it is asynchronous, i.e.; AddOrUpdateEmailAddressToListAsync.
I have a method that takes a callback argument to execute asynchronously, but the catch block doesn't seem to be catching any exceptions thrown by the synchronous call (this.Submit refers to a synchronous method).
public void Submit(FileInfo file, AnswerHandler callback)
{
SubmitFileDelegate submitDelegate = new SubmitFileDelegate(this.Submit);
submitDelegate.BeginInvoke(file, (IAsyncResult ar) =>
{
string result = submitDelegate.EndInvoke(ar);
callback(result);
}, null);
}
Is there a way to catch the exception thrown by the new thread and send it to the original thread? Also, is this the "proper" way to handle async exceptions? I wrote my code so it could be called like this (assuming the exception issue is fixed):
try
{
target.Submit(file, (response) =>
{
// do stuff
});
}
catch (Exception ex)
{
// catch stuff
}
but is there a more proper or elegant way to do this?
If you're targeting .NET 4.0, you can utilize the new Task Parallel Library, and observe the Task object's Exception property.
public Task Submit(FileInfo file)
{
return Task.Factory.StartNew(() => DoSomething(file));
}
private void DoSomething(FileInfo file)
{
throw new Exception();
}
Then use it like this:
Submit(myFileInfo).ContinueWith(task =>
{
// Check task.Exception for any exceptions.
// Do stuff with task.Result
});
where DoSomething is the method you'd like call asynchronously, and the delegate you pass to ContinueWith is your callback.
More information about exception handling in TPL can be found here: http://msdn.microsoft.com/en-us/library/dd997415.aspx
This is not a 'best practice' solution, but I think it's a simple one that should work.
Instead of having the delegate defined as
private delegate string SubmitFileDelegate(FileInfo file);
define it as
private delegate SubmitFileResult SubmitFileDelegate(FileInfo file);
and define the SubmitFileResult as follows:
public class SubmitFileResult
{
public string Result;
public Exception Exception;
}
Then, the method that actually does the file submission (not shown in the question) should be defined like this:
private static SubmitFileResult Submit(FileInfo file)
{
try
{
var submissionResult = ComplexSubmitFileMethod();
return new SubmitFileResult { Result = submissionResult };
}
catch (Exception ex)
{
return new SubmitFileResult {Exception = ex, Result = "ERROR"};
}
}
This way, you'll examine the result object, see if it has the Result or the Exception field set, and act accordingly.
In short, no.
When you call submitDelegate.BeginInvoke, it spawns the new thread, returns, and promptly exits your try/catch block (while the new thread runs in the background).
You could, however, catch all unhandled exceptions like this:
AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(YourException);
This will catch everything in the application domain, however (not just your async method).