障害例外が境界を越えてスローされると、タイプパラメータを取り、WCF境界を越えて詳細オブジェクトを渡すことができます。ただし、障害例外が2つの境界を越えると(再スローされるため、または例外がスタックをバブルアップするため)、詳細オブジェクトが失われることに気付きました。これは仕様によるものですか?もしそうなら、なぜですか?
私が話していることを確認したい場合は、これが実際に動作していることを示すコードリポジトリがあります。
障害例外が境界を越えてスローされると、タイプパラメータを取り、WCF境界を越えて詳細オブジェクトを渡すことができます。ただし、障害例外が2つの境界を越えると(再スローされるため、または例外がスタックをバブルアップするため)、詳細オブジェクトが失われることに気付きました。これは仕様によるものですか?もしそうなら、なぜですか?
私が話していることを確認したい場合は、これが実際に動作していることを示すコードリポジトリがあります。
非常に興味深いですが、それは確かに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);
}
}
私はあなたのサンプルコードを試しました-それは私にとってはうまくいきました。私は3つのインスタンスを実行し、1つで内部をホストし、もう1つで外部をホストし、3番目から外部と内部を呼び出しました。どちらの場合も、詳細メッセージが表示されました。