1

IErrorHandlerインターフェイスを実装し、それをWCFサービスアプリケーションのディスパッチャーに追加すると、奇妙な動作が発生します。メソッドのみHandleErrorが起動され、メソッドは起動されませんProvideFault

WCFサービスライブラリで同じコードと構成を使用する場合、両方のメソッドはコードの例外時に発生します。

例:

public class ErrorHandler : IErrorHandler, IServiceBehavior
{
    public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
    {
        // Provide the fault
    }

    public bool HandleError(Exception error)
    {
        // If handled return true, otherwise false
    }

    // Validate
    // AddBindingParameters
    // ApplyDispatchBehavior
}

public class ErrorHandlerBehavior : BehaviorExtensionElement
{
    public override Type BehaviorType
    {
        get { return typeof(ErrorHandler); }
    }

    protected override object CreateBehavior()
    {
        return new ErrorHandler();
    }
}

そしてWeb.configで:

    <extensions>
      <behaviorExtensions>
        <add name="ErrorLogging" type="Service.ErrorHandlerBehavior, Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>
...
      <serviceBehaviors>
        <behavior>
          <ErrorLogging />
        </behavior>
      </serviceBehaviors>

コードと構成は同じです(ただし、構成はサービスアプリケーションのWeb.configとサービスライブラリのi App.configにあります)。

にブレークポイントを挿入するProvideFaultと、サービスライブラリに対してのみ呼び出されるHandleErrorことがわかります。これはどのように行うことができますか?私は何かが足りないのですか? ProvideFault

編集

ProvideFault呼び出されているサービス操作のシグニチャがプリミティブ型以外の戻り型を持っている場合、呼び出されないようです。例:

public IEnumerable<MyType> GetMyType(string s1)
{
    throw new Exception("Testing...");
}

...トリガーされませんProvideFault

だが:

public bool DoStuff(string s1, string s2)
{
    throw new Exception("Testing...");
}

...をトリガーしProvideFaultます。

4

1 に答える 1

0

NicklasJepsen、あなたは正解に近かった。IEnumerableでも同じ問題が発生しましたが、戻り値のタイプを変更せずに、なんとか解決できました。

私の場合、問題の理由は、メソッドがyieldreturnステートメントを使用していたことでした。ご存知かもしれませんが、IEnumerableを返し、yield returnを使用して要素を返すメソッドは、「ステートマシン」にコンパイルされます。実際、IEnumerableインスタンスを返します。このインスタンスは、そのステートマシンを実装するメソッドであるイテレーターを返します。この優れた機能はLINQのコアにあり、実行を延期することができます。ステートマシンは、別の要素が必要な場合にのみ機能します。

WCFの場合、私は何が起こるかを知っていると思います。WCFは次のように動作します。

  1. メソッドを呼び出します。

  2. メソッドが失敗した場合、WCFは例外をErrorHandlerメソッド(両方)に渡します。

  3. メソッドが成功すると、WCFは応答を準備します。メソッドによって返されたオブジェクトを、応答形式に従ってシリアル化します。

  4. シリアル化が失敗した場合(メソッドの失敗よりも予想されないこと)、WCFは例外をErrorHandlerに渡しますが、メソッドHandleErrorにのみ渡します。何らかの理由で、この段階ではProvideFaultは呼び出されません。

  5. シリアル化が成功すると、シリアル化されたオブジェクトがクライアントに送信されます。

ここで、次の2つの例を検討してください。

public bool DoStuff();
public IEnumerable<bool> DoALotOfStuff();

DoStuffは確かにステップ2で失敗します。しかし、DoALotOfStuffは失敗しません!これはステートマシンであり、最初の要素をクエリする瞬間まで実行が延期されることを忘れないでください。したがって、このメソッドは失敗せず、IEnumerableインスタンスのみを返します。このインスタンスは、要素が必要になると列挙子を提供する準備ができています。その結果、WCFはメソッドによって返されるものに満足しているため、シリアル化に進みます。そして、それはあなたの陰湿な例外を得るところです!

現在、解決策は非常に単純です。実行を延期しないでください。「ステートマシン」の代わりに、Listのような「実際の」列挙可能なインスタンスを返します。

public IEnumerable<bool> DoALotOfStuff()
{
    return _DoALotOfStuff().ToList();
}

protected IEnumerable<bool> _DoALotOfStuff()
{
    yield return true;
    yield return false;
    throw new Exception( "Testing..." );
}

これで、メソッドが失敗した場合、適切な場所で失敗するため、ProvideFaultが呼び出されます。

于 2013-02-26T06:26:09.527 に答える