我正在努力寻找实现WCF重试的最佳方式。我希望让客户体验尽可能干净。我知道有两种方法(见下文)。我的问题是:“有没有我遗漏的第三种方法?也许有一种被普遍接受的方法?”
方法#1:创建实现服务接口的代理。对于对代理的每次调用,实现重试。
public class Proxy : ISomeWcfServiceInterface
{
public int Foo(int snurl)
{
return MakeWcfCall<int>(() => _channel.Foo(snurl));
}
public string Bar(string snuh)
{
return MakeWcfCall<string>(() => _channel.Bar(snuh));
}
private static T MakeWcfCall<T>(Func<T> func)
{
// Invoke the func and implement retries.
}
}
方法#2:将MakeWcfCall() (上面)更改为public,并让消费代码直接发送函数。
我不喜欢方法#1的原因是,每次接口更改时都必须更新代理类。
我不喜欢方法2的原因是客户必须将他们的调用包装在一个函数中。
我是不是错过了更好的方法?
编辑
我已经在这里发布了一个答案(见下文),基于公认的答案,它为我指明了正确的方向。我想我应该在答案中分享我的代码,以帮助某人完成我必须完成的工作。希望能有所帮助。
发布于 2013-04-23 00:58:50
我已经做了这类事情,并且我使用.Net的RealProxy类解决了这个问题。
通过使用RealProxy
,您可以使用提供的接口在运行时创建实际的代理。从调用代码中看,它们似乎在使用IFoo
通道,但实际上它是到代理的IFoo
,然后您有机会拦截对任何方法、属性、构造函数等…的调用
从RealProxy
派生,然后可以覆盖Invoke方法以拦截对WCF方法的调用并处理CommunicationException等。
发布于 2013-04-23 04:12:36
注意:这不应该是被接受的答案,但我想发布这个解决方案,以防它对其他人有帮助。吉姆的回答把我引向了这个方向。
首先是,消费代码,展示它是如何工作的:
static void Main(string[] args)
{
var channelFactory = new WcfChannelFactory<IPrestoService>(new NetTcpBinding());
var endpointAddress = ConfigurationManager.AppSettings["endpointAddress"];
// The call to CreateChannel() actually returns a proxy that can intercept calls to the
// service. This is done so that the proxy can retry on communication failures.
IPrestoService prestoService = channelFactory.CreateChannel(new EndpointAddress(endpointAddress));
Console.WriteLine("Enter some information to echo to the Presto service:");
string message = Console.ReadLine();
string returnMessage = prestoService.Echo(message);
Console.WriteLine("Presto responds: {0}", returnMessage);
Console.WriteLine("Press any key to stop the program.");
Console.ReadKey();
}
The WcfChannelFactory:
public class WcfChannelFactory<T> : ChannelFactory<T> where T : class
{
public WcfChannelFactory(Binding binding) : base(binding) {}
public T CreateBaseChannel()
{
return base.CreateChannel(this.Endpoint.Address, null);
}
public override T CreateChannel(EndpointAddress address, Uri via)
{
// This is where the magic happens. We don't really return a channel here;
// we return WcfClientProxy.GetTransparentProxy(). That class will now
// have the chance to intercept calls to the service.
this.Endpoint.Address = address;
var proxy = new WcfClientProxy<T>(this);
return proxy.GetTransparentProxy() as T;
}
}
WcfClientProxy: (这是我们截取并重试的地方)。
public class WcfClientProxy<T> : RealProxy where T : class
{
private WcfChannelFactory<T> _channelFactory;
public WcfClientProxy(WcfChannelFactory<T> channelFactory) : base(typeof(T))
{
this._channelFactory = channelFactory;
}
public override IMessage Invoke(IMessage msg)
{
// When a service method gets called, we intercept it here and call it below with methodBase.Invoke().
var methodCall = msg as IMethodCallMessage;
var methodBase = methodCall.MethodBase;
// We can't call CreateChannel() because that creates an instance of this class,
// and we'd end up with a stack overflow. So, call CreateBaseChannel() to get the
// actual service.
T wcfService = this._channelFactory.CreateBaseChannel();
try
{
var result = methodBase.Invoke(wcfService, methodCall.Args);
return new ReturnMessage(
result, // Operation result
null, // Out arguments
0, // Out arguments count
methodCall.LogicalCallContext, // Call context
methodCall); // Original message
}
catch (FaultException)
{
// Need to specifically catch and rethrow FaultExceptions to bypass the CommunicationException catch.
// This is needed to distinguish between Faults and underlying communication exceptions.
throw;
}
catch (CommunicationException ex)
{
// Handle CommunicationException and implement retries here.
throw new NotImplementedException();
}
}
}
被代理截取的调用的序列图:
发布于 2013-04-23 00:21:14
您可以实现通用代理,例如使用Castle。这里有一篇很好的文章http://www.planetgeek.ch/2010/10/13/dynamic-proxy-for-wcf-with-castle-dynamicproxy/。这种方法将提供实现接口的user对象,您将拥有一个负责通信的类
https://stackoverflow.com/questions/16151361
复制相似问题