Why do WCF Fault Exceptions not retain their details after crossing two boundaries?
When a fault exception is thrown across a boundary, it can take a typ开发者_C百科e parameter, to pass a detail object across the WCF boundary. However, I have noticed that when a fault exception crosses two boundaries (either because it is rethrown, or because the exception just bubbles up the stack), the detail object is lost. Is this by design? If so, why?
I have a code repository that shows this in action if you'd like to see what I'm talking about:
https://bitbucket.org/mckaysalisbury/doublefault/src/
Very interesting, it surely looks like a bug in WCF. If I switch the binding (and the address) from named pipes to use HTTP it actually works. I'll file a bug to the product team. Thank you for reporting this issue!
BTW, here's a stand-alone, console code which reproduces this issue.
public class StackOverflow_6267090
{
static bool useHttp;
const string baseAddressHttp = "http://localhost:8000/Bug/";
const string baseAddressPipe = "net.pipe://localhost/Bug/";
static Binding GetBinding()
{
if (useHttp)
{
return new BasicHttpBinding();
}
else
{
return new NetNamedPipeBinding();
}
}
static string GetBaseAddress()
{
return useHttp ? baseAddressHttp : baseAddressPipe;
}
[ServiceContract]
public interface IInner
{
[OperationContract]
[FaultContract(typeof(Detail))]
int DoStuff();
}
[ServiceContract]
public interface IOuter
{
[OperationContract]
[FaultContract(typeof(Detail))]
int DoStuff();
}
[DataContract]
public class Detail
{
[DataMember]
public string Data { get; set; }
public override string ToString()
{
return string.Format("Detail[Data={0}]", Data);
}
}
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
public class InnerService : IInner
{
public int DoStuff()
{
//return 3;
throw new FaultException<Detail>(new Detail { Data = "Something" }, new FaultReason("My special reason"));
}
}
class OuterService : IOuter
{
public int DoStuff()
{
return Caller.CallInner("In service");
}
}
public static class Caller
{
public static int CallInner(string where)
{
try
{
var factory = new ChannelFactory<IInner>(GetBinding(), new EndpointAddress(GetBaseAddress() + "Inner/"));
var channel = factory.CreateChannel();
int result = channel.DoStuff();
return result;
}
catch (FaultException<Detail> e)
{
Console.WriteLine("[{0} - CallInner] Error, Message={1}, Detail={2}", where, e.Message, e.Detail);
throw;
}
}
public static int CallOuter(string where)
{
try
{
var factory = new ChannelFactory<IOuter>(GetBinding(), new EndpointAddress(GetBaseAddress() + "Outer/"));
var channel = factory.CreateChannel();
int result = channel.DoStuff();
return result;
}
catch (FaultException<Detail> e)
{
Console.WriteLine("[{0} - CallOuter] Error, Message={1}, Detail={2}", where, e.Message, e.Detail);
throw;
}
}
}
public static void TestWith(bool useHttp)
{
StackOverflow_6267090.useHttp = useHttp;
Console.WriteLine("Using address: {0}", GetBaseAddress());
string baseAddress = GetBaseAddress();
ServiceHost innerHost = new ServiceHost(typeof(InnerService), new Uri(baseAddress + "Inner/"));
ServiceHost outerHost = new ServiceHost(typeof(OuterService), new Uri(baseAddress + "Outer/"));
innerHost.AddServiceEndpoint(typeof(IInner), GetBinding(), "");
outerHost.AddServiceEndpoint(typeof(IOuter), GetBinding(), "");
innerHost.Open();
outerHost.Open();
Console.WriteLine("Hosts opened");
Console.WriteLine("Calling inner directly");
try
{
Console.WriteLine(Caller.CallInner("client"));
}
catch (FaultException<Detail> e)
{
Console.WriteLine("In client, after CallInner, Message = {0}, Detail = {1}", e.Message, e.Detail);
}
Console.WriteLine("Calling outer");
try
{
Console.WriteLine(Caller.CallOuter("client"));
}
catch (FaultException<Detail> e)
{
Console.WriteLine("In client, after CallOuter, Message = {0}, Detail = {1}", e.Message, e.Detail);
}
catch (FaultException e)
{
Console.WriteLine("BUG BUG - this should not have arrived here. Exception = {0}", e);
}
}
public static void Test()
{
TestWith(true);
Console.WriteLine();
Console.WriteLine();
Console.WriteLine();
TestWith(false);
}
}
I tried your sample code - it worked fine for me. I ran three instances and hosted inner in one, outer in another and then from the third I called outer and inner. In both cases I saw the detail message.
精彩评论