2

WCF サービスと通信する WPF アプリケーションがあります。async現在、次のベースのパターンを使用して、ViewModels (MVVM パターンを使用しています) から WCF サービスを呼び出しています。

public async override void MyCommandImplementation()
{
    using (var proxy = new MyProxy())
    {
        var something = await proxy.GetSomethingAsync();
        this.MyProperty = something;
    }
}

MVVM パターンに従っているICommandため、ViewModel によって公開されるパブリック プロパティがあるため、関連付けられたコマンドの実装はTask<T>、イベント ハンドラーのようなオブジェクトを返しません。したがって、例外処理は実際には非常に単純です。つまり、次のパターンを使用して、WCF サービスからスローされた例外をキャッチできます。

public async override void MyCommandImplementation()
{
    try
    {
        using (var proxy = new MyProxy())
        {
            var something = await proxy.GetSomethingAsync();
        }
    }
    catch (FaultException<MyFaultDetail> ex)
    {
        // Do something here
    }
}

これまでのところ、カスタム WCF 動作のおかげで、サーバーが自動的に SOAP Fault に変換される例外をスローした場合、すべてが期待どおりに機能します。

サービスのどこでもスローできる一般的な例外がいくつかあるため (たとえば、各 WCF 操作はAuthenticationException、クライアント側で例外に変換されるFaultException<AuthenticationFaultDetail>例外をスローできます)、いくつかの例外を自分のサービスの共通の場所で処理することにしました。つまり、Application.DispatcherUnhandledExceptionイベントを処理します。これは問題なく動作し、あらゆる場所ですべての例外をキャッチFaultException<AuthenticationFaultDetail>し、ユーザーにエラー メッセージを表示して、アプリケーションが終了しないようにすることができます。

private static void Application_DispatcherUnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e)
{
    // Exception handler that handles some common exceptions
    // such as FaultException<AuthenticationFaultDetail>
    if (GlobalExceptionHandler.HandleException(e.Exception))
    {
        // Expected exception, so we're fine
        e.Handled = true;
    }
    else
    {
        // We're not fine. We couldn't handle the exception
        // so we'll exit the application
        // Log...etc.
    }
}

FaultExceptionパターンのおかげでUIスレッドでスローされ、キーワードasyncの前後で同期コンテキストが切り替わるため、すべてがうまく機能します。await

私の問題は、UI スレッド以外の別のスレッドで他の例外がスローされる可能性があることです。たとえばEndPointNotFoundException、行でスローされた場合await proxy.GetSomethingAsync();(サーバー側で WCF サービスがシャットダウンされた場合) です。

これらの例外はApplication.DispatcherUnhandledException、UI スレッドでスローされないため、イベント ハンドラーで処理されません。イベントでそれらを処理できますがAppDomain.UnhandledException、ログを記録してアプリケーションを終了する以外に何もできません (基本的に「e.Handled」のようなプロパティはありません)。

私の質問は、アプリケーションのある場所で非同期 WCF 呼び出しが発生した場合に、バックグラウンド スレッドでスローされた例外をどのように処理できるかということです。

私が今考えることができる最高のものは、次のようなものです:

public class ExceptionHandler : IDisposable
{
    public void HandleException(Exception ex)
    {
        // Do something clever here
    }
    public void Dispose()
    {
        // Do nothing here, I just want the 'using' syntactic sugar
    }
}

...

public async override void MyCommandImplementation()
{
    using (var handler = new ExceptionHandler())
    {
        try
        {
            using (var proxy = new MyProxy())
            {
                var something = await proxy.GetSomethingAsync();
            }
        }
        catch (FaultException<MyFaultDetail> ex)
        {
            // Do something here
        }
        catch (Exception ex)
        {
            // For other exceptions in any thread
            handler.HandleException(ex);
        }
    }
}

しかし、これには大量のコードをリファクタリングする必要があります (Web サービスを非同期的に呼び出すたびに)。

膨大な量のコードをリファクタリングしないようにするアイデアは役に立ちます。

4

2 に答える 2

1

私は現在、アスペクト指向プログラミングに夢中です。そのため、サービス呼び出しにPostsharpsメソッド インターセプト アスペクトを使用しています。これにより、特にサービスの呼び出しに使用されるコードを集中化できます。ロギングとスレッド同期にも使用します。

編集: await キーワードがまだサポートされていないことがわかりました。(3.1で登場)。

次に例を示します。

[Serializable]
internal class ServiceCall : MethodInterceptionAspect
{
  public override void OnInvoke(MethodInterceptionArgs args)
  {
    try
    {
        args.Proceed();
    }
    catch (FaultException<DCErrorMessage> f)
    {
        showError(f.Detail.Message + "\r\n" + f.Detail.Callstack, f.Detail.Title);
    }
    catch (Exception e)
    {
        showError(e.Message, "Error");
    }
}

そして、これがその使用方法です

[ServiceCall]
public Something getSomethingAsync()
{
   return await _client.getSomethingAsync();
}

アスペクトは、クラスまたはアセンブリ全体にも適用できます。

于 2013-10-03T12:52:35.970 に答える