我正在消耗一个笨重的WCF服务器,偶尔会抛出各种异常,并且还会将其中的一些错误返回为string
。我根本无法访问服务器代码。
我想覆盖内部WCF客户端请求调用方法并处理服务器返回的所有内部异常和硬编码错误,并在发生错误时引发Fault
事件,伪:
class MyClient : MyServiceSoapClient
{
protected override OnInvoke()
{
object result;
try
{
result = base.OnInvoke();
if(result == "Error")
{
//raise fault event
}
catch
{
//raise fault event
}
}
}
因此,当我调用myClient.GetHelloWorld()
时,它会通过我的重写方法。
如何实现这一目标?
我知道我不必使用生成的客户端,但我不想再次重新实现所有合同,我想使用生成的ClientBase
子类或至少使用它的通道。
我需要的是控制内部请求调用方法。
更新
我读了这个answer,看起来部分是我正在寻找的,但我想知道是否有办法只将IErrorHandler
附加到消费者(客户端)代码,我想以某种方式将它添加到ClientBase<TChannel>
实例。
更新
This文章看起来也很有希望,但它不起作用。应用的属性似乎没有生效。
我找不到将IServiceBehavior
添加到客户端的方法。
更新
我尝试通过IErrorHandler
通过IEndpointBehavior.ApplyClientBehavior
来附加public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime)
{
clientRuntime.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers
.Add(new ErrorHandler());
}
:
clientRuntime
(MyErrorHandler
是一个参数),但仍然会直接跳过ApplyDispatchBehavior
的异常。
根本没有调用BaseClient<TChannel>
。
结论
我需要实现两个方面:
override func viewDidLoad() {
super.viewDidLoad()
self.navigationItem.rightBarButtonItem = self.editButtonItem()
if #available(iOS 9.0, *) {
self.resultSearchController.loadViewIfNeeded()// iOS 9
} else {
// Fallback on earlier versions
let _ = self.resultSearchController.view // iOS 8
}
self.resultSearchController = ({
let controller = UISearchController(searchResultsController: nil)
controller.searchResultsUpdater = self
controller.dimsBackgroundDuringPresentation = false
controller.searchBar.sizeToFit()
self.tableView.tableHeaderView = controller.searchBar
return controller
})()
self.tableView.reloadData()
}
的生命周期内可能发生的所有异常,并决定是处理它们还是抛弃它们。这应该照顾所有操作(我消耗的服务暴露了几十个)答案 0 :(得分:5)
您可以使用和修改Exception Handling WCF Proxy Generator,更具体地说,它可以使用和修改它所使用的基类。它的基本思想(检查this description)是通过捕获连接故障和重试失败的操作来提供连接弹性。可以想象,为此目的,它需要能够捕获抛出的异常,并且还可以检查调用的结果。
主要功能由ExceptionHandlingProxyBase<T>
基类提供,您使用的是基类,而不是ClientBase<T>
。此基类具有如下Invoke
方法,您需要对其进行修改。
简化Invoke
:
protected TResult Invoke<TResult>(string operationName, params object[] parameters)
{
this.Open();
MethodInfo methodInfo = GetMethod(operationName);
TResult result = default(TResult);
try
{
this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait);
result = (TResult)methodInfo.Invoke(m_channel, parameters);
}
catch (TargetInvocationException targetEx) // Invoke() always throws this type
{
CommunicationException commEx = targetEx.InnerException as CommunicationException;
if (commEx == null)
{
throw targetEx.InnerException; // not a communication exception, throw it
}
FaultException faultEx = commEx as FaultException;
if (faultEx != null)
{
throw targetEx.InnerException; // the service threw a fault, throw it
}
//... Retry logic
}
return result;
}
您需要修改throw targetEx.InnerException;
部分以根据需要处理异常,显然还会根据您的需要检查resturn值。除此之外,如果您不期待连接问题,您可以退出重试逻辑或将其丢弃。 Invoke
返回方法的另一种变体是void
。
哦,顺便说一句,它也适用于双工通道,还有另一个基类。
如果您不想使用生成器(它甚至可能不适用于较新版本的VS),那么您可以从here获取基类,并生成实际的实现使用服务界面中的T4课程。
答案 1 :(得分:1)
如果服务没有返回真正的异常,而只是返回消息,那么您可能希望将ClientMessageInspector添加为新的客户端行为。请参阅:https://msdn.microsoft.com/en-us/library/ms733786.aspx
答案 2 :(得分:1)
我最终根据this问题中的答案使用了某些内容。
它坚持生成的客户端代码,并允许一般调用操作。
code不完整,随意分叉编辑。如果您发现任何错误或进行任何更新,请通知我。
它非常笨重,所以我只是分享使用代码:
using (var proxy = new ClientProxy<MyServiceSoapClientChannel, MyServiceSoapChannel>())
{
client.Exception += (sender, eventArgs) =>
{
//All the exceptions will get here, can be customized by overriding ClientProxy.
Console.WriteLine($@"A '{eventArgs.Exception.GetType()}' occurred
during operation '{eventArgs.Operation.Method.Name}'.");
eventArgs.Handled = true;
};
client.Invoke(client.Client.MyOperation, "arg1", "arg2");
}