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

发布于 2024-11-14 10:20:08 字数 309 浏览 8 评论 0原文

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

如果您想了解我在说什么,我有一个代码存储库,可以显示此操作:

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

When a fault exception is thrown across a boundary, it can take a type 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/

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

半衾梦 2024-11-21 10:20:08

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

顺便说一句,这是一个独立的控制台代码,可以重现此问题。

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

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);
    }
}
木槿暧夏七纪年 2024-11-21 10:20:08

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

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文