wcferror-handlingierrorhandler

IErrorHandler not being binded, and not handling error


I have a WCF service, and I have a FaultContract with an IErrorHandler binded to it, and it was working great. For some reason, that I don't know, it stopped being binded at runtime. When I place breakpoints on ApplyDispatchBehavior and Validate, they aren't being called when I start debugging (it use to stop), so my exceptions aren't handled when I get an error.
I initially followed the this blog post to implement it, and it worked fine, but when I added a second interface/endpoint, it stopped working. I changed all of my code according to this SO post, but to no avail. Another thing that started happening is that, when I open the web.config with the Microsoft Service Configuration Editor, I get the error:

The 'TestAppServer, Version 1.0.0.0, Culture=neutral, PublicKeyToken=null' assembly could not be found. Do you want to locate it? If you select 'No', you will not be prompted for the same assembly again. To avoid seeing this message every time the system cannot fund the assembly, please copy the assembly file to the same folder as the configuration file.

The error above started happening after I added the second endpoint/interface, when I lost error handling on my WCF service. Below is my web config:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="ServiceErrorHandler" type="company.Test.appserver.implementation.ServiceErrorHandlerBehaviorExtensionElement, TestAppServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>
    <bindings>
      <basicHttpBinding>
        <binding name="SimpleBinding" />
      </basicHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="DefaultBehavior" name="company.Test.appserver.implementation.TestUpdate">
        <endpoint address="udt" binding="basicHttpBinding" bindingConfiguration="SimpleBinding"
          name="TestUpdate" bindingNamespace="http://company/Test/update/2011/04"
          contract="company.Test.appserver.interfaces.ITestUpdate" />
        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
          name="TestUpdateMex" bindingNamespace="http://company/Test/update/2011/04"
          contract="IMetadataExchange" />
      </service>
      <service behaviorConfiguration="DefaultBehavior" name="company.Test.appserver.implementation.TestTransaction">
        <endpoint address="udt" binding="basicHttpBinding" bindingConfiguration="SimpleBinding"
          name="TestTransacao" bindingNamespace="http://company/Test/transaction/2011/04"
          contract="company.Test.appserver.interfaces.ITestTransacao" />
        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
          name="TestTransacaoMex" bindingNamespace="http://company/Test/transaction/2011/04"
          contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="DefaultBehavior">
          <serviceMetadata httpGetEnabled="true" httpGetBinding="webHttpBinding"
            httpGetBindingConfiguration="" />
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpGetBinding="webHttpBinding"
            httpGetBindingConfiguration="" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

Below is the code for my Error Handler:

namespace company.Test.appserver.implementation
{
  public class ServiceErrorHandlerBehaviorExtensionElement : BehaviorExtensionElement, IServiceBehavior
  {
    public override Type BehaviorType
    {
      get { return typeof(TestErrorHandler); }
    }

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

    private IErrorHandler GetInstance()
    {
      return new TestErrorHandler();
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {

    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
      IErrorHandler errorHandlerInstance = GetInstance();
      foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
      {
        ChannelDispatcher cd = cdb as ChannelDispatcher;

        if (cd != null)
        {
          cd.ErrorHandlers.Add(errorHandlerInstance);
        }
      }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
      foreach (var svcEndpoint in serviceDescription.Endpoints)
      {
        if (svcEndpoint.Contract.Name != "IMetadataExchange")
        {
          foreach (var opDesc in svcEndpoint.Contract.Operations)
          {
            if (opDesc.Faults.Count == 0)
            {
              string msg = string.Format("");
            }
          }
        }
      }
    }
  }

  public class TestErrorHandler : IErrorHandler
  {
    public bool HandleError(Exception error)
    {
      return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
      if (!(error is FaultException))
      {
        ClsTestFaultContract exContract = new ClsTestFaultContract();

        FaultException<ClsTestFaultContract> fex = 
          new FaultException<ClsTestFaultContract>(exContract, exContract.DescricaoErro,
            new FaultCode(Convert.ToString(exContract.CodigoErro)));
        MessageFault msgFault = fex.CreateMessageFault();
        fault = Message.CreateMessage(version, msgFault, ErrorHandlerConstant.FaultAction); 
      }
    }
  }
}

Below is one of my interfaces:

namespace company.Test.appserver.interfaces
{
  [ServiceContract(Namespace = "http://company/Test/transaction/2011/04",
            Name = "ITestTransacao", SessionMode = SessionMode.Allowed)]
  public interface ITestTransacao
  {
    [OperationContract]
    [FaultContract(typeof(ClsTestFaultContract), Action = ErrorHandlerConstant.FaultAction)]
    Int32 RegistrarTransacao(String Param1, String Param2, String Param3, Int32 Param4);
    [OperationContract]
    [FaultContract(typeof(ClsTestFaultContract), Action = ErrorHandlerConstant.FaultAction)]
    void ConfirmarTransacao(String Param1, String Param2, Int32 Param3, String Param4, String Param5);
    [OperationContract]
    [FaultContract(typeof(ClsTestFaultContract), Action = ErrorHandlerConstant.FaultAction)]
    void CancelarTransacao(String Param1, String Param2, String Param3, Int32 Param4, String Param5 = "");
  }
}

I've wasted countless hours, and I can't seem to figure out what happened that broke my error handling, that was working great. Tks SO MUCH for any help

EDIT

I'm posting below the working code, after the help I got. Tks so much again... Below is the correct error handler:

namespace company.Test.appserver.implementation
{
  public class ServiceErrorHandlerBehaviorExtensionElement : BehaviorExtensionElement
  {
    public override Type BehaviorType
    {
      get { return typeof(TestErrorHandler); }
    }

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

  public class TestErrorHandler : ServiceErrorHandlerBehaviorExtensionElement, IErrorHandler, IServiceBehavior
  {
    public bool HandleError(Exception error)
    {
      return true;
    }

    public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
    {
      if (!(error is FaultException))
      {
        ClsTestFaultContract exContract = new ClsTestFaultContract();

        FaultException<ClsTestFaultContract> fex = 
          new FaultException<ClsTestFaultContract>(exContract, exContract.DescricaoErro,
            new FaultCode(Convert.ToString(exContract.CodigoErro)));
        MessageFault msgFault = fex.CreateMessageFault();
        fault = Message.CreateMessage(version, msgFault, ErrorHandlerConstant.FaultAction); 
      }
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
    {

    }

    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
      foreach (ChannelDispatcherBase cdb in serviceHostBase.ChannelDispatchers)
      {
        ChannelDispatcher cd = cdb as ChannelDispatcher;

        if (cd != null)
        {
          cd.ErrorHandlers.Add(this);
        }
      }
    }

    public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
    {
      foreach (var svcEndpoint in serviceDescription.Endpoints)
      {
        if (svcEndpoint.Contract.Name != "IMetadataExchange")
        {
          foreach (var opDesc in svcEndpoint.Contract.Operations)
          {
            if (opDesc.Faults.Count == 0)
            {
              string msg = string.Format("");
            }
          }
        }
      }
    }
  }
}

And below is the correct web.config

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0" />
  </system.web>
  <system.serviceModel>
    <extensions>
      <behaviorExtensions>
        <add name="ServiceErrorHandler" type="company.Test.appserver.implementation.ServiceErrorHandlerBehaviorExtensionElement, TestAppServer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
      </behaviorExtensions>
    </extensions>
    <bindings>
      <basicHttpBinding>
        <binding name="SimpleBinding" />
      </basicHttpBinding>
    </bindings>
    <services>
      <service behaviorConfiguration="DefaultBehavior" name="company.Test.appserver.implementation.TestUpdate">
        <endpoint address="udt" binding="basicHttpBinding" bindingConfiguration="SimpleBinding"
          name="TestUpdate" bindingNamespace="http://company/Test/update/2011/04"
          contract="company.Test.appserver.interfaces.ITestUpdate" />
        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
          name="TestUpdateMex" bindingNamespace="http://company/Test/update/2011/04"
          contract="IMetadataExchange" />
      </service>
      <service behaviorConfiguration="DefaultBehavior" name="company.Test.appserver.implementation.TestTransaction">
        <endpoint address="udt" binding="basicHttpBinding" bindingConfiguration="SimpleBinding"
          name="TestTransacao" bindingNamespace="http://company/Test/transaction/2011/04"
          contract="company.Test.appserver.interfaces.ITestTransacao" />
        <endpoint address="mex" binding="mexHttpBinding" bindingConfiguration=""
          name="TestTransacaoMex" bindingNamespace="http://company/Test/transaction/2011/04"
          contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="DefaultBehavior">
          <serviceMetadata httpGetEnabled="true" httpGetBinding="webHttpBinding"
            httpGetBindingConfiguration="" />
          <serviceDebug includeExceptionDetailInFaults="true" />
          <ServiceErrorHandler />
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" httpGetBinding="webHttpBinding"
            httpGetBindingConfiguration="" />
          <serviceDebug includeExceptionDetailInFaults="false" />
          <ServiceErrorHandler />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
 <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

Solution

  • Your config file has the ServiceErrorHandler behavior extension registered, but it's not actually using it in any of the collections under <serviceBehaviors/>.