注意:这可能是一个非常与C#相关的语言问题,与WCF或Web服务无关。
有一个第三方ASMX Web服务,用于数据检索。我创建了一个通用方法叫做ExecuteCommand(),用于对Web服务的每个请求。该方法的目的是处理cookie会话/异常和其他常见逻辑。为了简化未使用资源的处置,每个请求都应使用新的通道。
问题是,为了使用ExecuteCommand()方法,我必须每次初始化通道,以便能够将要执行的方法作为参数传递。如果听起来太复杂,请原谅。下面是一个使用示例:
在调用
那么问题来了 - 在
我想实现这样的功能:
有一个第三方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.
在调用
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
变量并定义在给定通道中执行的方法,这种做法可行吗?我想实现这样的功能:
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));
欢迎提出其他代码优化建议,但这并非必须。
P.S. 在Visual Studio中,ASMX
作为服务引用
添加。因此,“CarService”自动生成了一些实体,例如-CarServiceSoapChannel
接口,CarServiceSoapClient
类和当然包含Web服务方法的CarService
接口。在上面的示例中,使用ChannelFactory
创建了一个CarServiceSoapChannel
接口的通道,因此,这就是问题名称的来源:将接口方法作为参数传递
。这可能有点误导,但我希望从描述本身就可以清楚地了解我试图实现什么。
更新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())
个人而言,我不介意再写一些代码,因为这仍然比我的初始方法实现有很大优势。但是,我想知道新版本能否改进?
例如,当方法中只有一个通用的 TResult
类型时,返回类型 <TResult>
可以被省略。但是在有两个通用类型的情况下,这种省略就不再适用了。无论如何,感谢 @nvoigt!
ExecuteCommand
中创建/初始化通道,并将其作为参数传递给匿名方法。这有什么问题吗? - MakePeaceGreatAgainvar cars = WcfHelper.ExecuteCommand<CarServiceSoapChannel>(channel => channel.GetCars(color));
吗?这是我正在尝试实现的一种首选方式来解决此问题。该方法目前还不能正常工作。 :) 因此,这里有一个问题。 - AlexcompileMethod.Compile()
创建的Func
,假设你的func
是Func<Channel, T>
。 - MakePeaceGreatAgain