为什么WCF故障异常在跨越两个边界后不保留其详细信息

本文关键字:边界 两个 保留 详细信息 异常 故障 WCF 跨越 为什么 | 更新日期: 2023-09-27 17:58:30

当跨边界抛出故障异常时,它可以采用类型参数,以跨WCF边界传递详细信息对象。然而,我注意到,当故障异常跨越两个边界时(要么是因为它被重新抛出,要么是因为异常只是在堆栈中冒泡),详细信息对象就会丢失。这是故意的吗?如果是,为什么?

如果你想看看我在说什么,我有一个代码库可以显示这一点:

https://bitbucket.org/mckaysalisbury/doublefault/src/

为什么WCF故障异常在跨越两个边界后不保留其详细信息

非常有趣,它看起来肯定像WCF中的一个bug。如果我将绑定(和地址)从命名管道切换到使用HTTP,它实际上是有效的。我将向产品团队提交一个bug。感谢您报道此问题!

顺便说一句,这是一个独立的控制台代码,它再现了这个问题。

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);
    }
}

我试过你的示例代码,它对我来说很好。在这两种情况下,我都看到了详细信息。