1

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

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

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

4

2 回答 2

4

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);
    }
}
于 2011-06-09T16:54:49.907 回答
0

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

于 2011-06-09T16:44:36.733 回答