0

I would like to show an apologetic message and close my WPF application if any request to Elasticsearch failes. There are a lot of requests in the app so the most convinient way I think is to throw an exception in OnRequestCompleted callback if something goes wrong and then handle it globally. But each of 4 ways of handling unhandled exceptions listed here do nothing and the app just crashes. Here is the simpliest example. What's wrong there?

App.xaml

<Application x:Class="WPF_Elasticsearch_2._3.App"
             xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             DispatcherUnhandledException="App_OnDispatcherUnhandledException"
             StartupUri="MainWindow.xaml">
</Application>

App.xaml.cs

using System;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Threading;
using Nest;

namespace WPF_Elasticsearch_2._3
{
    public partial class App : Application
    {
        public static IElasticClient ElasticClient { get; private set; }

        public App()
        {
            AppDomain.CurrentDomain.UnhandledException += App_OnUnhandledException;
            TaskScheduler.UnobservedTaskException += App_OnUnobservedTaskException;
            Dispatcher.UnhandledException += App_OnDispatcherUnhandledException;

            var connectionSettings = new ConnectionSettings(new Uri("http://localhost:9200"))
                .OnRequestCompleted(callDetails =>
                {
                    if (!callDetails.Success)
                        throw new Exception("Unsuccessful request!");
                });

            ElasticClient = new ElasticClient(connectionSettings);
        }

        private void App_OnDispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
        {
            MessageBox.Show(e.Exception.Message);
            e.Handled = true;
            Shutdown(-1);
        }

        private void App_OnUnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            MessageBox.Show(((Exception)e.ExceptionObject).Message);
            Shutdown(-1);
        }

        private void App_OnUnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
        {
            MessageBox.Show(e.Exception.Message);
            e.SetObserved();
            Shutdown(-1);
        }
    }
}

MainWindow.xaml

<Window x:Class="WPF_Elasticsearch_2._3.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d"
        Title="MainWindow" Height="240" Width="320">

    <Button Click="ButtonBase_OnClick">Make request</Button>
</Window>

MainWindow.xaml.cs

using System.Windows;

namespace WPF_Elasticsearch_2._3
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private void ButtonBase_OnClick(object sender, RoutedEventArgs e)
        {
            App.ElasticClient.Search<object>(descriptor => descriptor
                .Index("test-index")
                .Type("test-type"));
        }
    }
}
Community
  • 1
  • 1
N. Kudryavtsev
  • 3,556
  • 1
  • 26
  • 30
  • do you use different appDomains? have you tried to move the EventHandler code for OnRequestCompleted to a "real method" instead of using a anonymous? You may need an invoke? – Mat Jun 23 '16 at 16:44
  • 1
    I wouldn't really pay attention to that post you linked. If you're going to throw your own exception, you still need to handle it somewhere or it will crash the application. In that respect, to get the behaviour you want, you likely don't _need_ to throw an exception, you can simply produce the behaviour you want where you are throwing the exception. In general to handle exceptions you will want to use a `try/catch` block – pay Jun 23 '16 at 16:44
  • @pay Some nooby but important questions follow from there: - What is the conception of unhandled exception events for, and how is it possible to make it work though? - Where should I place the try/catch block to be able to handle all exceptions? Console and WinForms apps have a simple-to-understand Main method unlike WPF. I wouldn't just produce some behaviour because the app has complex structure and there may appear other messages about errors when the first message is on screen, and I need to stop everything is the error happened. – N. Kudryavtsev Jun 23 '16 at 17:06
  • @Mat I didn't create any AppDomain myself, the files presented here are the only ones where I changed the default generated code. Carrying the callback method to the App class and passing it to OnRequestCompleted as argument doesn't help. – N. Kudryavtsev Jun 23 '16 at 17:24
  • I wouldn't say unhandled exceptions are _for_ anything, the exception itself is for some reason, but it is only "unhandled" because the programmer did not handle it. You can have as many try/catch blocks as you wish, so as you say, "Where should I place the try/catch block to be able to handle all exceptions?" - the answer to that is anywhere you need them. – pay Jun 23 '16 at 18:07
  • @pay In WinForms in Progam.Main we can place Application.Run(new Form1()); into try block to be able to handle every exception occured, at the highest level. My second question is how to do the same in WPF. Not about best practices, I _do_ know that exeptions must be handled as close as possible to places where they occur. – N. Kudryavtsev Jun 23 '16 at 18:56
  • I see. The nature of how a WPF application is initialized wouldn't allow you to have a try/catch in the same manner you are describing here. You would have to try/catch individual functions and event handlers in WPF, as you can't really 'wrap the entire application' in a try/catch. – pay Jun 23 '16 at 18:57
  • @pay You've said "I wouldn't say unhandled exceptions are for anything" - OK, what are they for though and why don't they work there? – N. Kudryavtsev Jun 23 '16 at 19:10
  • Every exception by nature is "unhandled", until it is handled. Exceptions are to tell you something has gone wrong. How you handle them is up to you. I'm not sure what you mean by why don't they work? – pay Jun 23 '16 at 19:19
  • @pay I mean that none of events *DispatcherUnhandledException*, *UnhandledException* and *UnobservedTaskException* were fired, and I can't understand why. And therefore how to use them. – N. Kudryavtsev Jun 23 '16 at 19:39
  • That's why I said in my initial comment that you should ignore that post, however, you seem to be pretty close to having that working. Look at the example here: https://msdn.microsoft.com/en-us/library/system.appdomain.unhandledexception.aspx – pay Jun 23 '16 at 19:43
  • @pay This example differs from my code only by SecurityPermission attribute, explicit delegate creation and static modifier before the handler. Eliminating this differences didn't help. OK, maybe it works in console. But _why_ doesn't it work in WPF? _Why_ I "should ignore that post"? Do you know what exactly is wrong? I really appreciate your help but I haven't got any concrete information yet :( – N. Kudryavtsev Jun 23 '16 at 21:30
  • The actual entry point of your application is in `MainWindow.xaml.cs`. Try putting the assigning the event handlers in there. `public MainWindow()` is where the application starts. – pay Jun 23 '16 at 21:33

1 Answers1

1

You can configure the client to throw on exceptions using

var connectionSettings = new ConnectionSettings(new Uri("http://localhost:9200"))
        .ThrowExceptions();

var client = new ElasticClient(connectionSettings);

In this way, you won't need to throw an exception in .OnRequestCompleted().

As others have said in the comments however, you will still need to handle these exceptions in some way; if using .ThrowExceptions(), you might wrap your calls in try/catch blocks, signalling a message to the UI when an Elasticsearch exception is caught in catch.

Russ Cam
  • 124,184
  • 33
  • 204
  • 266