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

时间:2011-06-07 14:49:47

标签: c# wcf faultexception

当跨越边界抛出错误异常时,可以采用类型参数来跨WCF边界传递详细信息对象。但是,我注意到当一个错误异常越过两个边界时(或者因为它被重新抛出,或者因为异常只是在堆栈中冒泡),细节对象就会丢失。这是设计的吗?如果是这样,为什么?

我有一个代码存储库,如果你想看看我在说什么,就会显示这个:

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

2 个答案:

答案 0 :(得分:4)

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

BTW,这是一个独立的控制台代码,可以重现这个问题。

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

答案 1 :(得分:0)

我尝试了你的示例代码 - 它对我来说很好。我运行了三个实例,并在一个内部托管,另一个托管在外部,然后从第三个托管内部和内部托管。在这两种情况下,我都看到了详细信息。