клиент не будет перехватывать универсальное FaultException <T>, только FaultException - PullRequest
15 голосов
/ 06 июля 2011

Я прочитал все, что есть, чтобы прочесть об этом, но, возможно, я что-то упустил (ну, определенно, я что-то упускаю, иначе это бы уже работало)

Я выдавал ошибку исключения на бизнес-уровне моего сервера:

public class RfcException :  Exception
{
   public RfcException(string _m, Exception _inner) : base(_m, _inner)
   { }

   public Dictionary<string, string> ExtendedProperties
   {
      get { return extendedProperties; }
      protected set { extendedProperties = value; }
   }

   private Dictionary<string, string> extendedProperties = new Dictionary<string, string>();
}

, который я оставляю необработанным в сервисе, но у меня есть IErrorHandler для отлова и создания FaultMessage:

public class FaultErrorHandler : BehaviorExtensionElement, IErrorHandler, IServiceBehavior
{
   public bool HandleError(Exception error)
   {
      if (!Logger.IsLoggingEnabled()) return true;
      var logEntry = new LogEntry
        {
           EventId = 100,
           Severity = TraceEventType.Error,
           Priority = 1,
           Title = "WCF Failure",
           Message = string.Format("Error occurred: {0}", error)
        };
      logEntry.Categories.Add("MiddleTier");

      Logger.Write(logEntry);
      return true;
   }

   public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault)
   {
      if (error is RfcException)
      {
         RfcException rfcException = error as RfcException;
         var serviceFault = new RfcServiceFault(rfcException);
         var faultException = new FaultException<RfcServiceFault>(serviceFault, new FaultReason(string.Format("System error occurred, exception: {0}", error)));
         var faultMessage = faultException.CreateMessageFault();
         fault = Message.CreateMessage(version, faultMessage, Schema.WebServiceStandard);
      }
      else
      {
         var faultException = new FaultException<Exception>(error, new FaultReason(string.Format("System error occurred, exception: {0}", error)));
         var faultMessage = faultException.CreateMessageFault();
         fault = Message.CreateMessage(version, faultMessage, Schema.WebServiceStandard);
      }
   }

   public void AddBindingParameters(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, System.ServiceModel.Channels.BindingParameterCollection bindingParameters)
   { }

   public void ApplyDispatchBehavior(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
   {
      foreach (ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers)
      {
         chanDisp.ErrorHandlers.Add(this);
      };
   }

   public void Validate(ServiceDescription serviceDescription, System.ServiceModel.ServiceHostBase serviceHostBase)
   { }

   public override Type BehaviorType
   {
      get { return typeof(FaultErrorHandler); }
   }

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

Не нужно спрашивать; Я уже подтвердил ввод отладчиком в части part if (error is RfcException), я прошел этот код, и он без проблем добрался до конца. Этот errorHandler переносит сообщение FaultException<RfcServiceFault>, сообщение RfcServiceFault выглядит так:

[DataContract(Name = "RfcServiceFault", Namespace = "Service.DataTransfer.Rfc")]
public class RfcServiceFault 
{
   public RfcServiceFault(RfcException rfcException) : this( (Exception)rfcException )
   {
      ExtendedProperties = new Dictionary<string, string>(rfcException.ExtendedProperties);
   }

   public RfcServiceFault()
   { }

   public RfcServiceFault(Exception regularException)
   {
      FaultMessage = regularException.Message;
      StackTrace = regularException.StackTrace;
   }

   public Dictionary<string, string> ExtendedProperties
   {
      get { return extendedProperties; }
      protected set { extendedProperties = value; }
   }

   [DataMember]
   private Dictionary<string, string> extendedProperties = new Dictionary<string, string>();

   [DataMember]
   public string FaultMessage { get; set; }

   [DataMember]
   public string StackTrace { get; set; }
}

Служба имеет все аннотации, которые должны иметь службу wcf с faultContract:

[ServiceContract(Name = "MyService", Namespace = Schema.WebServiceStandard, SessionMode = SessionMode.Allowed)]
public interface IMyService 
{
    [OperationContract(Name = "GetStuff")]
    [FaultContract(typeof(RfcServiceFault) , Name="RfcServiceFault", Namespace="Service.DataTransfer.Rfc")]
    LookupResult GetStuff();
}

Теперь: тестирование на клиенте, простой тест вроде этого:

try
{
   var result = myService.GetStuff();
   Assert.IsTrue(!string.IsNullOrEmpty(result));
}
catch (FaultException<RfcServiceFault> rfcEx)
{
   // OMG FOR THE LIFE OF THE PUPPIES ENTER HERE
}
catch (FaultException rr)
{
   // it always falls in here
}
catch (Exception ex)
{ }

Я прочитал много, много сообщений об этой похожей проблеме:

но пока что ничего не помогает, я попытался настроить трассировку WCF в web.config:

<system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing">
        <listeners>
          <add name="log"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="c:\Traces.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>

и я получаю там файл svclog, открываю его с помощью средства просмотра WCF Trace, но вижу только несколько сообщений, желтый показывает исключение, но он только подтверждает то, что клиент уже видит, System.ServiceModel.FaultException получаемый, а не общий

Есть идеи, как это выяснить?

EDIT забыл упомянуть, я включил мой обработчик ошибок в конфигурации, как это:

<behaviorExtensions>
   .....
    <add name="faultErrorHandlerBehavior" 
         type="Service.FaultErrorHandler,Service, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" />
    .....
</behaviorExtensions>
<serviceBehaviors>
   <behavior name="ServiceBehavior">
       <serviceThrottling maxConcurrentCalls="200" maxConcurrentSessions="200"
                          maxConcurrentInstances="200" />
       <serviceMetadata httpGetEnabled="true" />
       <serviceDebug includeExceptionDetailInFaults="true" />
       <faultErrorHandlerBehavior />
    </behavior>
</serviceBehaviors>

1 Ответ

14 голосов
/ 06 июля 2011

(Это небольшой удар в темноте) Я полагаю, что у вас может быть проблема, потому что действие по ошибке не соответствует ожидаемому на клиенте. Можете ли вы заменить ваш CreateMessage следующим:

fault = Message.CreateMessage(version, faultMessage, faultException.Action);
...