Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

FaultException.Detail coming back empty

I am trying to catch a given FaultException on a WCF client. I basically need to extract a inner description from the fault class so that I can then package it in another exception for the upper layers to do whatever.

I've done this successfully a number of time, what makes it different this time is that fault is declared as an array, as you can see from the service reference attribute declared on top of the method that throws the exception:

[System.ServiceModel.FaultContractAttribute(typeof(FaultClass[]), Action = "http://whatever/", Name = "whateverBusinessFault")] 

This is my code:

try
{
  // call service here
}
catch (FaultException<FaultClass[]> ex)
{
  if (ex.Detail != null && ex.Detail.Length > 0)
  {
    throw new CustomException(ex.Detail[0].description);
  }
  else
  {
    throw;
  }
}

Problem is Detail (which is an array) is always coming back empty in the code even if I can see the data (description field etc.) in the SOAP response from WCF trace.

So the stuff I need is definitely coming back but for some reason either it doesn't get deserialized or I can't get to it from code.

Any help appreciated!

UPDATE:

Trying with @Darin suggestion but no luck, the string I am extracting from the XmlReader is "/r/n":

var sb = new StringBuilder();

using (XmlReader reader = fault.GetReaderAtDetailContents())
{
  while (reader.Read())
     sb.AppendLine(reader.ReadOuterXml()); 
}

var detail = sb.ToString();

Looks like the detail section is not coming up at all!

like image 385
JohnIdol Avatar asked Sep 08 '10 16:09

JohnIdol


4 Answers

I found the solution on a UPS Forum :

https://developerkitcommunity.ups.com/index.php/Special:AWCforum/st/id371

"The problem was the visual studio didn't quite map out the ErrorDetail objects right. The ErrorDetail node is called "ErrorDetail", but the type generated for it is "ErrorDetailType." I edited the reference.cs class generated for each service I was using and added a TypeName:"

like image 145
ychapados Avatar answered Nov 11 '22 12:11

ychapados


It is difficult to say where the problem is but I suspect the smoking gun is this axis web service not generating standard message. One way to workaround this would be to parse the XML yourself:

try
{
    proxy.CallSomeMethod();
}
catch (FaultException ex)
{
    var fault = ex.CreateMessageFault();
    using (XmlReader reader = fault.GetReaderAtDetailContents())
    {
        // TODO: read the XML fault and extract the necessary information.
    }
}
like image 44
Darin Dimitrov Avatar answered Nov 11 '22 12:11

Darin Dimitrov


It took me ages to figure out how to get the full details message from a FaultException as a string. I eventually figured it out and wrote this extension method:

public static string GetDetail(this FaultException faultException)
{
    if (faultException == null)
        throw new ArgumentNullException(nameof(faultException));

    MessageFault messageFault = faultException.CreateMessageFault();
    if (messageFault.HasDetail) {
        using (XmlDictionaryReader reader = messageFault.GetReaderAtDetailContents()) {
            return reader.ReadContentAsString();
        }
    }
    return null;
}

Originally I was using reader.Value but that only appeared to the return the first line of a multi-line details message. reader.ReadContentAsString() appears to get the whole thing, new lines included, which is what I wanted.

like image 3
Dan Stevens Avatar answered Nov 11 '22 11:11

Dan Stevens


I came up with the simplest test case I could. I hope it will help you. Server side:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    [FaultContract(typeof(FaultClass[]))]
    string Crash();
}

public class Service1 : IService1
{
    public string Crash()
    {
        var exception = new FaultException<FaultClass[]>(new FaultClass[] { new FaultClass { Data = "TEST" } }, new FaultReason("Boom"));

        throw exception;
    }
}

[DataContract]
public class FaultClass
{
    [DataMember]
    public string Data { get; set; }
}

Client side:

try
{
    using (var client = new Service1Client())
    {
        client.Crash();
    }
}
catch(FaultException<FaultClass[]> e)
{
    //Break here
}
like image 2
Johann Blais Avatar answered Nov 11 '22 13:11

Johann Blais