【发布时间】:2018-05-24 14:27:29
【问题描述】:
注意:这很可能是 C# 特定语言问题,与 WCF 或 web services 完全无关。
有一个三方ASMX 网络服务,用于数据检索。我创建了一个名为ExecuteCommand() 的通用方法,用于针对Web 服务的每个请求。此方法的目的是处理 cookie 会话/异常和其他常见逻辑。对于每个请求,都应使用一个新通道,以简化未使用资源的处理。
问题是要使用ExecuteCommand() 方法——我每次都必须初始化一个通道,以便能够将要执行的方法作为参数传递。对不起,如果这听起来太复杂。这是一个使用示例:
string color = "blue";
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
var cars = WcfHelper.ExecuteCommand(channel, () => channel.GetCars(color));
// channel is null here. Channel was closed/aborted, depending on Exception type.
在调用 ExecuteCommand() 之后 - channel 已被处理掉。之所以需要channel 对象,是为了能够提供一个方法作为参数来执行!即() => channel.GetCars()。为了进一步支持这些话,这里是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
{
// init operation context
using (new OperationContextScope(channel))
{
// set the session cookie to header
if (Cookie != null) {
HttpRequestMessageProperty request = new HttpRequestMessageProperty();
request.Headers["Cookie"] = cookie.ToString();
OperationContext.Current.OutgoingMessageProperties[HttpRequestMessageProperty.Name] = request;
}
// execute method
var compiledMethod = method.Compile();
result = compiledMethod.Invoke();
}
}
// do different logic for 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;
}
}
}
所以简短的问题 - 是否可以在 ExecuteCommand 方法本身内初始化 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。这可能有点误导,但我希望从描述本身可以清楚地看出我试图完成什么。
25.05.2018 更新 我听从了@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>();
// init operation context
using (new OperationContextScope(channel))
{
// set the session cookie to header
if (Cookie != null)
Cookie.SetCookieForSession();
// execute method
result = method((TInterface)channel);
}
}
catch (Exception)
{
throw;
}
finally
{
CloseOrAbortServiceChannel(channel);
channel = null;
}
return result;
}
这已经达到了最初的目标。然而,这种方法存在一个问题。为了调用该方法 - 您必须明确指定该方法的返回参数。
也就是说,如果你想调用方法——写这个是不够的:
var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.IsBlue())
您必须特别指定,返回类型应为boolean
var result = WcfHelper.ExecuteCommand<CarServiceSoapChannel, bool>(channel => channel.IsBlue())
我个人不介意多写一点代码,因为它仍然比我最初的方法实现有很大的优势,但是,我想知道在新版本中是否可以改进?
例如,当我在方法中只有 1 个通用 TResult 类型时 - 返回类型 <TResult> 可以省略。这不再是 2 个泛型的情况。无论如何,谢谢@nvoigt!
【问题讨论】:
-
第二种方法在我看来相当不错。您在
ExecuteCommand中创建/初始化通道并将其作为参数传递给匿名方法。它有什么问题? -
@HimBromBeere 第二种方法?你可能是说
var cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));?这是我正在努力实现的。面对这个问题的首选方式。该方法还不能那样工作。 :) 因此这是问题所在。 -
您所要做的就是为您由
compileMethod.Compile()创建的Func提供频道 - 假设您的功能是Func<Channel, T>。 -
@HimBromBeere 这很好用!感谢您对这个问题的意见! nvoigt 在回答部分为这个建议做了一个很好的例子。