所以这是场景:
我有一个 WCF 服务,它为每个操作定义了一堆 FaultContracts。我想安排它,如果在与有效的 FaultContract 不匹配的 WCF 服务线程中引发未处理的异常,它会关闭整个进程而不仅仅是线程。(原因是我想要一个包含异常信息的故障转储,因为它与合同不匹配。)
有没有办法干净地做到这一点?我遇到的主要问题是 WCF 想要将我的所有异常转换为客户端故障,以保持服务运行;我其实是想把整个过程搞下来,这本质上意味着规避 WCF 的正常行为。
所以这是场景:
我有一个 WCF 服务,它为每个操作定义了一堆 FaultContracts。我想安排它,如果在与有效的 FaultContract 不匹配的 WCF 服务线程中引发未处理的异常,它会关闭整个进程而不仅仅是线程。(原因是我想要一个包含异常信息的故障转储,因为它与合同不匹配。)
有没有办法干净地做到这一点?我遇到的主要问题是 WCF 想要将我的所有异常转换为客户端故障,以保持服务运行;我其实是想把整个过程搞下来,这本质上意味着规避 WCF 的正常行为。
Environment.FailFast() 将创建故障转储;它不会运行任何挂起的 try-finally 块,也不会运行任何终结器。
您需要使用IErrorHandler来自定义 WCF 的错误处理行为。在调用 (ServiceHost).Open() 之前“应用行为”。
例如(在 Main() 中查找显示“serviceHost.Description.Behaviors.Add(new FailBehavior());”的行):
class FailBehavior : IServiceBehavior
{
public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters)
{
return;
}
public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
Console.WriteLine("The FailFast behavior has been applied.");
var f = new FailOnError();
foreach(ChannelDispatcher chanDisp in serviceHostBase.ChannelDispatchers)
{
chanDisp.ErrorHandlers.Add(f);
}
}
public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
{
return;
}
}
class FailOnError : IErrorHandler
{
public bool HandleError(Exception error)
{
// this is called for every exception -- even ungraceful disconnects
if( !(error is CommunicationException) )
throw new TargetInvocationException( "WCF operation failed.", error );
else
throw new CommunicationException( "Unexpected communication problem. (see inner exception)", error );
// Unreachable
//return false; // other handlers should be called
}
public void ProvideFault(Exception error, MessageVersion version, ref Message fault)
{
// Can't throw from here (it will be swallowed), and using Environment.FailFast
// will result in all crashes going to the same WER bucket. We could create
// another thread and throw on that, but instead I think throwing from HandleError
// should work.
//Console.WriteLine( "Unhandled exception: {0}", error );
//Environment.FailFast("Unhandled exception thrown -- killing server");
}
}
class Program
{
static void Main( string[] args )
{
Console.WriteLine( "Greetings from the server." );
Uri uri = new Uri( "net.tcp://localhost:5678/ServerThatShouldCrash" );
using( ServiceHost serviceHost = new ServiceHost( typeof( Server ), uri ) )
{
Binding binding = _CreateBinding();
serviceHost.AddServiceEndpoint( typeof( IServer ),
binding,
uri );
serviceHost.Description.Behaviors.Add(new FailBehavior());
serviceHost.Open();
// The service can now be accessed.
Console.WriteLine( "The service is ready." );
Console.WriteLine( "\nPress <ENTER> to terminate service.\n" );
Console.ReadLine();
}
}
private static Binding _CreateBinding()
{
NetTcpBinding netTcp = new NetTcpBinding( SecurityMode.None );
netTcp.ReceiveTimeout = TimeSpan.MaxValue;
netTcp.ReliableSession.InactivityTimeout = TimeSpan.MaxValue;
return netTcp;
} // end _CreateBinding()
}
Application.Exit();
这可能会做到这一点,但用户将丢失他们当时正在处理的任何内容。