0
votes

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.

Image of the exception

EDIT 3

Turned out that the way I was utilising async and relayCommand had nothing to do with my exception. Problem was fixed.

1
There's rarely anything meaningful to be said about a TargetInvocationException without checking its InnerException.Jeroen Mostert
InnerException is empty or I do not know how to see it. On exception settings I have enabled the (Common Language Runtime Exections) I am using visual studio 2015.ajr
You might have some luck getting to the actual error by making Visual Studio break to the debugger as soon as the exception is thrown. You can do this from the Exception Window, just check "Common Language Runtime", and then make the error happen. Be sure to turn that off when you are done though, that behavior can get very annoying. blogs.msdn.microsoft.com/visualstudioalm/2015/02/23/…Bradley Uffner
Unfortunately, I have already checked in everything under the Common Language Runtime Exceptions mentioned by the link. Thanks for the tip thought!ajr

1 Answers

2
votes

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()));