注意:这可能是非常C#
特定的语言问题,与WCF
或web services
完全无关。
有一个3方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;
}
}
}
所以简短的问题 - 可以在channel
方法本身内初始化ExecuteCommand
变量,同时有可能定义,哪个方法应在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));
当然欢迎任何其他代码改进建议,但不是强制性的。
P.S。 ASMX
在Service reference
中添加Visual Studio
。因此,有一些实体为“CarService”自动生成,例如 - CarServiceSoapChannel
接口,CarServiceSoapClient
类,当然还包含CarService
接口,包含Web服务的方法。在上面的示例中,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 !
答案 0 :(得分:1)
您的方法签名应为:
public static TResult ExecuteCommand<TInterface>(Func<TInterface, TResult> method)
然后在你的WcfHelper(它可能不再是static
,因为它需要一个成员_strategyFactory
),你就像以前一样创建一个频道:
{
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
return method(channel);
}
显然,你需要再次添加所有花哨的try / finally内容。
因为你现在应该有班级作为成员的工厂,你可以将服务合同通用放入你的班级,以方便用户:
public class ConnectionToService<TInterface> : where TInterface : class
{
public TResult ExecuteCommand<TResult>(Func<TInterface, TResult> method)
{
var channel = _strategyFactory.CreateChannel<CarServiceSoapChannel>();
return method(channel);
}
}
用法:
var service = new ConnectionToService<ICarService>();
var color = service.ExecuteCommand(s => s.GetColor());