2

When creating a generic fault exception FaultException<MyWebServiceFaultDetail> and passing it back to the message inspector pipeline as a MessageFault, the client will not receive the generic fault in its catch (FaultException<MyWebServiceFaultDetail> ex) block, it is only caught within the catch (FaultException ex) block.

The MyWebServiceFaultDetail and IClientMessageInspector implication both live within the same project as the client WCF web reference in a single project MyProjects.MyWebService.

The WebService is called by another project which has a reference to the MyProjects.MyWebService project.

*Comments have been removed for brevity

The data contract:

[DataContract]
public class MyWebServiceFaultDetail
{
    [DataMember]
    public string MessageDetail { get; set; }

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

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

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

    public override string ToString()
    {
        return string.Format("Detail[MessageDetail={0}] [MessageType={1}] [TransactionComplete={2}] [TransactionSuccess={3}]", MessageDetail,MessageType ,TransactionComplete ,TransactionSuccess );
    }
}

The message inspector. I would just add that reply.Headers.Action is null when this method runs. Setting the action value on the call to CreateMessage() has had no affect. Values I have tried.

.CreateMessage(reply.Version, ex.CreateMessageFault(), "*");

.CreateMessage(reply.Version, ex.CreateMessageFault(), reply.Headers.Action);

.CreateMessage(reply.Version, ex.CreateMessageFault(), ex.Action);

internal class ResponseMessageInspector : System.ServiceModel.Dispatcher.IClientMessageInspector
{
    private static NLog.Logger _logger = NLog.LogManager.GetCurrentClassLogger();

    public void AfterReceiveReply(ref Message reply, object correlationState)
    {
        MessageBuffer bufferedMessage = null;

        try
        {
            bufferedMessage = reply.CreateBufferedCopy(Int32.MaxValue);

            Message replacedMessage = bufferedMessage.CreateMessage();

            if (bufferedMessage.MessageContentType != "application/soap+msbin1" || reply.IsEmpty || reply.IsFault)
            {
                reply = replacedMessage;
                return;
            }

            bool isErrorMessage;

            var messageReader = replacedMessage.GetReaderAtBodyContents();

            isErrorMessage = (messageReader.Name == "TransactionReport");

            if (isErrorMessage)
            {
                string transactionComplete = "",
                    transactionSuccess = "",
                    messageType = "",
                    messageDetail = "",
                    messageBrief = "";

                while (messageReader.Read())
                {
                    if (messageReader.NodeType == XmlNodeType.Element && !messageReader.IsEmptyElement)
                    {
                        switch (messageReader.Name)
                        {
                            case "TransactionComplete":
                                transactionComplete = messageReader.ReadString();
                                break;

                            case "TransactionSuccess":
                                transactionSuccess = messageReader.ReadString();
                                break;

                            case "MessageType":
                                messageType = messageReader.ReadString();
                                break;

                            case "MessageDetail":
                                messageDetail = messageReader.ReadString();
                                break;

                            case "MessageBrief":
                                messageBrief = messageReader.ReadString();
                                break;

                            default:
                                break;
                        }
                    }
                }

                if (string.IsNullOrEmpty(messageBrief))
                {
                    messageBrief = "My response processing fault: {Unable to obtain error message from My response, enable WCF message tracing for more detailed information}";

                    _logger.Warn(messageBrief);
                }

                FaultException ex = new FaultException<MyWebServiceFaultDetail>(
                    new MyWebServiceFaultDetail
                    {
                        TransactionComplete = transactionComplete,
                        TransactionSuccess = transactionSuccess,
                        MessageDetail = messageDetail,
                        MessageType = messageType
                    },
                    new FaultReason(messageBrief));

                Message faultMessage = Message.CreateMessage(reply.Version, ex.CreateMessageFault(), null);

                faultMessage.Headers.CopyHeadersFrom(reply.Headers);
                faultMessage.Properties.CopyProperties(reply.Properties);

                reply = faultMessage;
            }
            else
                reply = bufferedMessage.CreateMessage();
        }
        finally
        {
            if (bufferedMessage != null)
                bufferedMessage.Close();
        }
    }

    public object BeforeSendRequest(ref Message request, System.ServiceModel.IClientChannel channel)
    {
        return null;
    }
}

The client code that is reciving the FaultException but not a FaultException<MyWebServiceFaultDetail>

 internal static T TrySendToMyWebService<T>(
        CallWebServiceDelegate<T> callWebService,
        bool expectResponce,
        out MessageProcessorResult result) where T : class
    {
        T MyWebServiceResponce = null;

        result = new MessageProcessorResult();

        using (ServiceRequestConnectorServiceSoapClient ws =
          new ServiceRequestConnectorServiceSoapClient())
        {
            try
            {
                MyWebServiceWebServiceHelper.LogOn(ws);
                MyWebServiceResponce = callWebService(ws);
                if (expectResponce && MyWebServiceResponce == null)
                {
                    result.ShouldRetry = true;
                    result.RetryReason = "Unexpected MyWebService web service response. The response was null";
                }
            }
            catch (FaultException<MyWebServiceFaultDetail> ex)
            {
                // I never get called :(
                result.Exception = ex;
                result.ShouldRetry = true;
                result.RetryReason = string.Format("An Exception was raised calling the MyWebService web service: Reason:{0}  /r/nDetails: {1}", ex.Reason, ex.Detail.ToString());
                _logger.ErrorException(result.RetryReason, ex);
            }
            catch (FaultException ex)
            {
                result.Exception = ex;
                result.ShouldRetry = true;
                result.RetryReason = string.Format("An Exception was raised calling the MyWebService web service: {0}", ex.Message);
                _logger.ErrorException(ex.Message, ex);
            }
            finally
            {
                MyWebServiceWebServiceHelper.LogOff(ws);
            }
        }

        return MyWebServiceResponce;
    }
4

2 回答 2

0

The problem could be here as you are specifying 2 format arguments and only supplying 1:

public override string ToString()
{
    return string.Format("Detail[MessageDetail={0}] [MessageType={1}] [TransactionComplete={1}] [TransactionSuccess={1}]", MessageDetail);
}

So you actually get a FormatException thrown by string.Format rather than your FaultException<JoBlogsWebServiceFaultDetail>.

于 2012-11-06T14:29:18.667 回答
0

Just throw the new typed FaultException<MyWebServiceFaultDetail> from the AfterReceiveReply of your message inspector.

FaultException ex = new FaultException<MyWebServiceFaultDetail>(
                new MyWebServiceFaultDetail
                {
                    TransactionComplete = transactionComplete,
                    TransactionSuccess = transactionSuccess,
                    MessageDetail = messageDetail,
                    MessageType = messageType
                },
                new FaultReason(messageBrief));
throw ex;

If your detail class is declared on the client side, you don't even need to decorate it with DataContract attribute.

于 2012-11-09T10:30:10.380 回答