C#中如何让ExecuteCommand内部创建通道并传递接口方法作为参数?
注意:这很可能是一个特定于C#的语言问题,与WCF或web services完全无关。
我现在对接一个第三方的ASMX Web服务做数据检索,已经写了个通用方法ExecuteCommand()来处理所有针对这个服务的请求——它会负责Cookie会话管理、异常处理这些通用逻辑,而且每次请求都会用新通道,这样释放未使用资源更简单。
但现在有个小麻烦:用ExecuteCommand()的时候,必须先自己初始化通道,再把要执行的方法传进去,调用完通道就被释放了。给你们看个使用示例:
string color = "blue"; var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>(); var cars = WcfHelper.ExecuteCommand(channel, () => channel.GetCars(color)); // 这里channel已经是null了,根据异常类型会被关闭或终止
为了让大家更清楚问题,我把WcfHelper类的内部实现贴出来:
public static class WcfHelper { public static Cookie Cookie { get; set; } public static T ExecuteCommand<T>(IClientChannel channel, Expression<Func<T>> method) { T result = default(T); try { // 初始化操作上下文 using (new OperationContextScope(channel)) { // 把会话Cookie设置到请求头 if (Cookie != null) { HttpRequestMessageProperty request = new HttpRequestMessageProperty(); request.Headers["Cookie"] = cookie.ToString(); OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = request; } // 执行方法 var compiledMethod = method.Compile(); result = compiledMethod.Invoke(); } } // 针对FaultException、CommunicationException、TimeoutException做不同处理 catch (Exception) { throw; } finally { CloseOrAbortServiceChannel(channel); channel = null; } return result; } private static void CloseOrAbortServiceChannel(ICommunicationObject communicationObject) { bool isClosed = false; if (communicationObject == null || communicationObject.State == CommunicationState.Closed) return; try { if (communicationObject.State != CommunicationState.Faulted) { communicationObject.Close(); isClosed = true; } } catch (Exception) { throw; } finally { if (!isClosed) AbortServiceChannel(communicationObject); } } private static void AbortServiceChannel(ICommunicationObject communicationObject) { try { communicationObject.Abort(); } catch (Exception) { throw; } } }
我最关心的核心问题是:能不能把channel的初始化放到ExecuteCommand方法内部去做,同时还能指定要在这个通道上执行的方法?
我理想中的调用方式是这样的:
string color = "blue"; var cars = WcfHelper.ExecuteCommand<Car[], CarServiceSoapChannel>(channel => channel.GetCars(color));
甚至更简洁一点:
string color = "blue"; var cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));
当然如果有其他代码改进的建议也欢迎提,但不是必须的。
补充说明一下:这个ASMX服务是我在Visual Studio里通过添加Service reference引入的,所以自动生成了CarService相关的实体,比如CarServiceSoapChannel接口、CarServiceSoapClient类,还有包含Web服务方法的CarService接口。示例里我用ChannelFactory给CarServiceSoapChannel接口创建通道,这也是问题标题“Passing an interface method as a parameter”的由来,可能有点误导,但希望上面的描述能让大家明白我的需求。
2018.05.25更新:我已经采纳了@nvoigt的建议实现了目标,代码如下:
public static TResult ExecuteCommand<TInterface, TResult>(Func<TInterface, TResult> method) where TInterface : IClientChannel { TResult result = default(TResult); IClientChannel channel = null; try { channel = StrategyFactory.CreateChannel<TInterface>(); // 初始化操作上下文 using (new OperationContextScope(channel)) { // 把会话Cookie设置到请求头 if (Cookie != null) Cookie.SetCookieForSession(); // 执行方法 result = method(
内容的提问来源于stack exchange,提问作者Alex

