Note: 这很可能是非常C#
特定语言问题,与以下无关WCF
or web services
at all.
有3方ASMX
Web 服务,用于数据检索。我创建了一个名为的通用方法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.
After ExecuteCommand()
叫做 -channel
已经被处理掉了。之所以channel
对象之所以需要,就是能够提供一个要执行的方法作为参数! IE。() => 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;
}
}
}
所以简短的问题 - 是否可以初始化channel
里面的变量ExecuteCommand
方法本身,同时可以定义在内部执行哪个方法ExecuteCommand
对于给定的频道?
我正在尝试完成这样的事情:
string color = "blue";
var cars = WcfHelper.ExecuteCommand<Car[], CarServiceSoapChannel>(channel => channel.GetCars(color));
or even
string color = "blue";
var cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));
当然,任何其他代码改进建议都是受欢迎的,但不是强制性的。
P.S. ASMX
添加为Service reference
in Visual Studio
。因此,有一些为“CarService”自动生成的实体,例如 -CarServiceSoapChannel
界面,CarServiceSoapClient
类,当然CarService
包含 Web 服务方法的接口。在上面的例子中ChannelFactory
用于创建一个通道CarServiceSoapChannel
接口,因此,这里是问题名称的来源:Passing an interface method as a parameter
。这可能有点误导,但我希望从描述本身可以清楚地看出我想要实现的目标。
更新2018年5月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>();
// 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!