1

好的,我已经搜索了很多关于这个问题的答案,我似乎只能找到 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 管道中是否存在可扩展点,在该点转换此值是可能适当的,以便在调用方法时,参数可以是正确的类型?

4

2 回答 2

4

更新的答案-

看了你的评论,我明白了。因此,您想提供字符串,然后在 OperationInovker 出现之前对其进行转换。所以我弄脏了手,终于做到了。

这是我所做的。一个派生自它的新类WebHttpBehavior将提供一个我们可以扩展或覆盖现有行为的地方WebHttpBidning

public class MyWebHttpBehavior : WebHttpBehavior
{
}

虽然这是一个黑客,但这会奏效。Typed 参数的问题是 URL 模板格式化程序通过检查类型的方法签名来抛出异常,string所以我通过覆盖BindingInformationby overiding 方法来摆脱它GetRequestDispatchFormatter

    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图片。我只是从类型 object 的 input[] 中获取值,将值从Stringto转换为Guid并将其传递回 Invoker。

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; }
    }
}

现在我花了很长时间才弄清楚如何在管道中注入这个自定义操作 inovker。我在这里找到了相关的 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);
    }

现在所有这些黑客和扩展都会使这个合法。

    [WebInvoke(Method = "GET", UriTemplate = "/{id}")]
    string GetValue(Guid id);

免责声明:我在试验这种扩展和应用自定义行为时很开心,但我没有检查受影响的区域。因此,使用它需要您自己承担风险,并且可以随意更改/增强。对不起错别字

更新 2

我创建了一个库来包装这个 web http 行为扩展。该库在方法参数(多个)中提供了对其他值类型的更多支持。看看这个

于 2015-10-08T19:30:16.307 回答
0

您需要提供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();
    }
}
于 2015-10-09T00:41:55.907 回答