好吧,我已经搜索了一堆关于这个问题的答案,我似乎只能找到2011年和之后的。net 3.5的文章和文档…所以我希望能在。net 4.5(及更高版本)中找到更多关于WCF的最新信息。
我有一个绑定到webHttpBinding的WCF服务。它使用一个Uri模板,这个Uri模板传递要检索的条目的标识,如下所示:
[WebInvoke(Method="GET", UriTemplate = "/{identity}")]
public ResponseItem Get(string identity)
{
//Convert identity to a Guid which is the correct type for the identity
//Retrieve object
}
我真正想做的是删除值到Guid的转换并将其上移到堆栈以清理代码,并有:
[WebInvoke(Method="GET", UriTemplate = "/{identity}")]
public ResponseItem Get(Guid identity)
{
//Retrieve object
}
我意识到,对于其他类型的绑定,可以使用自定义behavior和QueryStringConverter。我还意识到webHttpBinding默认为字符串的原因是,在地址中传递的固有值在语义上应该是字符串-因为地址是基于字符串的。所以我问的问题可能没有意义。
在我的应用程序的上下文中,字符串在语义上是不正确的,这让我很恼火,这个类与转换代码混乱,不应该是类的关注,但改变绑定不是一个选项,因为现有的客户端正在使用它。
对于当前版本的WCF(例如IParameterInspector, IServiceBehavior),在WCF管道中是否存在一个可扩展点,其中该值的转换是可能的和适当的,以便在调用方法时,参数可以是正确的类型?
看了你的评论后,我明白了。你想要提供字符串,然后在OperationInovker进入画面之前对它进行强制转换。所以我把自己的手弄脏了,最后终于做到了。更新答案 -
这是我所做的。一个从WebHttpBehavior
派生的新类,它将提供一个地方,我们可以扩展或覆盖WebHttpBidning
的现有行为。
public class MyWebHttpBehavior : WebHttpBehavior
{
}
虽然这是一个hack和,但这将工作。键入参数的问题是,URL模板格式化程序通过检查string
类型的方法签名抛出异常,所以我通过覆盖GetRequestDispatchFormatter
方法来覆盖BindingInformation
,从而摆脱了这个问题。
protected override IDispatchMessageFormatter GetRequestDispatchFormatter(OperationDescription operationDescription, ServiceEndpoint endpoint)
{
foreach (var item in operationDescription.Messages[0].Body.Parts)
{
item.Type = typeof(string);
}
return base.GetRequestDispatchFormatter(operationDescription, endpoint);
}
应用此行为后,运行时将不再为字符串参数检查抛出异常。现在我需要改变OperationInvoker,因为如果你运行这个,那么当你从客户端调用操作时,这会抛出异常,说Invalid cast
。
现在是IOperationInvoker
的图片。我只是从类型对象的input[]中获取值,将值从String
转换为Guid
,并将其传递回调用程序。
public class ValueCastInvoker : IOperationInvoker
{
readonly IOperationInvoker _invoker;
public ValueCastInvoker(IOperationInvoker invoker)
{
_invoker = invoker;
}
public ValueCastInvoker(IOperationInvoker invoker, Type type, Object value)
{
_invoker = invoker;
}
public object[] AllocateInputs()
{
return _invoker.AllocateInputs().ToArray();
}
private object[] CastCorrections(object[] inputs)
{
Guid obj;
var value = inputs[0] as string;
if (Guid.TryParse(value, out obj))
{
return new[] { (object)obj }.Concat(inputs.Skip(1)).ToArray();
}
return inputs.ToArray();
}
public object Invoke(object instance, object[] inputs, out object[] outputs)
{
return _invoker.Invoke(instance, CastCorrections(inputs), out outputs);
}
public IAsyncResult InvokeBegin(object instance, object[] inputs, AsyncCallback callback, object state)
{
return _invoker.InvokeBegin(instance, inputs, callback, state);
}
public object InvokeEnd(object instance, out object[] outputs, IAsyncResult result)
{
return _invoker.InvokeEnd(instance, out outputs, result);
}
public bool IsSynchronous
{
get { return _invoker.IsSynchronous; }
}
}
现在我花了很长时间来弄清楚的是如何在管道中注入这个自定义操作调用器。我在这里找到了一个相关的stackoverflow答案。按照他建议的方式来实施,而且很有效。
在这里添加总结:必须实现一个新的IOperationBehavior并将其与DispatcherRuntime附加在一起。
public class MyOperationBehavior : IOperationBehavior
{
public void AddBindingParameters(OperationDescription operationDescription, BindingParameterCollection bindingParameters)
{
}
public void ApplyClientBehavior(OperationDescription operationDescription, ClientOperation clientOperation)
{
}
public void ApplyDispatchBehavior(OperationDescription operationDescription, DispatchOperation dispatchOperation)
{
dispatchOperation.Invoker = new ValueCastInvoker(dispatchOperation.Invoker);
}
public void Validate(OperationDescription operationDescription)
{
}
}
现在在MyWebHttpBehavior中覆盖ApplyDispatchBehavior
并引入上面实现的IOperationBehavior
。
public override void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher)
{
foreach (var operation in endpoint.Contract.Operations)
{
if (operation.Behaviors.Contains(typeof(MyOperationBehavior)))
continue;
operation.Behaviors.Add(new MyOperationBehavior());
}
base.ApplyDispatchBehavior(endpoint, endpointDispatcher);
}
现在所有这些hack和扩展将使这个合法。
[WebInvoke(Method = "GET", UriTemplate = "/{id}")]
string GetValue(Guid id);
免责声明:我很高兴试验这个扩展和应用自定义行为,但我没有检查受影响的区域。因此,使用它要承担自己的风险,并可以随意更改/增强。抱歉,有错别字。
更新2
我已经创建了一个库来包装这个web http行为扩展。该库在方法参数(多个)中提供了对其他值类型的更多支持。看看这个。
您需要提供QueryStringConverter的自定义实现。这是您需要的代码。只需将GuidConverterWebHttpBehavior添加为服务行为,一切都应该正常工作。
class GuidQueryStringConverter : QueryStringConverter
{
public override bool CanConvert(Type type)
{
return type == typeof(Guid) || base.CanConvert(type);
}
public override object ConvertStringToValue(string parameter, Type parameterType)
{
if (parameterType == typeof(Guid))
{
Guid guid;
if(Guid.TryParse(parameter, out guid))
{
return guid;
}
}
return base.ConvertStringToValue(parameter, parameterType);
}
}
public class GuidConverterWebHttpBehavior : WebHttpBehavior
{
protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
{
return new GuidQueryStringConverter();
}
}