WCF服务合同可以有一个可以为空的输入参数吗?

Ami*_*Ami 12 c# rest wcf nullable

我的合同定义如下:

[OperationContract]
[WebGet(UriTemplate = "/GetX?myStr={myStr}&myX={myX}", BodyStyle = WebMessageBodyStyle.Wrapped)]
string GetX(string myStr, int? myX);
Run Code Online (Sandbox Code Playgroud)

我得到一个例外:[InvalidOperationException:合同'IMyGet'中的'GetX'操作有一个名为'myX'的类型为'System.Nullable 1[System.Int32]', but type 'System.Nullable1 [System.Int32]' 的查询变量'不能被'QueryStringConverter'转换.UriTemplate查询值的变量必须具有可由"QueryStringConverter"转换的类型.

找不到任何有关此错误的信息,除了以下链接:http: //blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html有点旧,而不是解决方案.

任何想法除了摆脱可以为空的参数做什么?

谢谢.

Xca*_*bur 34

这个问题有一个解决方案,不需要任何黑客攻击.它可能看起来像很多工作,但如果你仔细阅读它并不是真的很有意义.问题的核心是确实存在未解决的错误(从.NET 4开始),这意味着WebServiceHost不使用自定义QueryStringConverters.因此,您需要做一些额外的工作,并了解WebHttpEndpoints的WCF配置是如何工作的.下面为您提供了解决方案.

首先,自定义QueryStringConverter允许在查询字符串中提供空值,省略它们或提供空字符串:

public class NullableQueryStringConverter : QueryStringConverter
{
    public override bool CanConvert(Type type)
    {
        var underlyingType = Nullable.GetUnderlyingType(type);

        return (underlyingType != null && base.CanConvert(underlyingType)) || base.CanConvert(type);
    }

    public override object ConvertStringToValue(string parameter, Type parameterType)
    {
        var underlyingType = Nullable.GetUnderlyingType(parameterType);

        // Handle nullable types
        if (underlyingType != null)
        {
            // Define a null value as being an empty or missing (null) string passed as the query parameter value
            return String.IsNullOrEmpty(parameter) ? null : base.ConvertStringToValue(parameter, underlyingType);
        }

        return base.ConvertStringToValue(parameter, parameterType);
    }
}
Run Code Online (Sandbox Code Playgroud)

现在是一个自定义WebHttpBehavior,它将设置自定义QueryStringConverter来代替标准的QueryStringConverter.请注意,此行为派生自WebHttpBehavior,这很重要,以便我们继承REST端点所需的行为:

public class NullableWebHttpBehavior : WebHttpBehavior
{
    protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
    {
        return new NullableQueryStringConverter();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在是一个自定义ServiceHost,它将自定义行为添加到WebHttpEndpoint,以便它将使用自定义QueryStringConverter.重要的是要注意在这段代码,是它从派生的ServiceHost和NOT WebServiceHost.这很重要,否则上面提到的错误将阻止使用自定义QueryStringConverter:

public sealed class NullableWebServiceHost : ServiceHost
{
    public NullableWebServiceHost()
    {
    }

    public NullableWebServiceHost(object singletonInstance, params Uri[] baseAddresses) : base(singletonInstance, baseAddresses)
    {
    }

    public NullableWebServiceHost(Type serviceType, params Uri[] baseAddresses) : base(serviceType, baseAddresses)
    {
    }

    protected override void OnOpening()
    {
        if (this.Description != null)
        {
            foreach (var endpoint in this.Description.Endpoints)
            {
                if (endpoint.Binding != null)
                {
                    var webHttpBinding = endpoint.Binding as WebHttpBinding;

                    if (webHttpBinding != null)
                    {
                        endpoint.Behaviors.Add(new NullableWebHttpBehavior());
                    }
                }
            }
        }

        base.OnOpening();
    }
}
Run Code Online (Sandbox Code Playgroud)

因为我们不是从WebServiceHost派生的,所以我们需要这样做,并确保我们的配置正确,以确保REST服务能够正常工作.您需要的就是以下内容.在此配置中,我还有一个WS HTTP端点设置,因为我需要从C#(使用WS HTTP作为其更好的)和移动设备(使用REST)访问此服务.如果不需要,可以省略此端点的配置.需要注意的一件重要事情是,您不再需要自定义端点行为.这是因为我们现在正在添加自己的自定义端点行为,该行为绑定自定义QueryStringConverter.它派生自WebHttpBehavior,这是配置添加的内容,使其现在变得多余.

<system.serviceModel>
  <services>
    <service behaviorConfiguration="ServiceBehavior" name="MyNamespace.Service1">
      <endpoint binding="webHttpBinding" bindingConfiguration="WebHttpBinding" contract="MyNamespace.IService1" />
      <endpoint address="ws" binding="wsHttpBinding" bindingConfiguration="WsHttpBinding" contract="MyNamespace.IService1" />
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
    </service>
  </services>

  <bindings>
    <webHttpBinding>
      <binding name="WebHttpBinding">
        <security mode="Transport">
          <transport clientCredentialType="None" />
        </security>
      </binding>
    </webHttpBinding>

    <wsHttpBinding>
      <binding name="WsHttpBinding">
        <security mode="Transport">
          <transport clientCredentialType="None" />
        </security>
      </binding>
    </wsHttpBinding>
  </bindings>

  <behaviors>
    <serviceBehaviors>
      <behavior name="ServiceBehavior">
        <serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
        <serviceDebug includeExceptionDetailInFaults="true" httpHelpPageEnabled="false" httpsHelpPageEnabled="true" />
        <dataContractSerializer maxItemsInObjectGraph="2147483647" />
      </behavior>
    </serviceBehaviors>
  </behaviors>
</system.serviceModel>
Run Code Online (Sandbox Code Playgroud)

最后要做的是创建一个自定义ServiceHostFactory并告诉svc文件使用它,这将导致使用所有自定义代码.当然你也可以创建一个自定义元素,允许你在配置中添加行为,但我认为对于这种行为,基于代码的方法更好,因为你不太可能想要删除处理可空类型的能力,因为它会破坏你的服务:

public sealed class NullableWebServiceHostFactory : ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
    {
        return new NullableWebServiceHost(serviceType, baseAddresses);
    }
}
Run Code Online (Sandbox Code Playgroud)

将Service.svc文件的标记更改为以下内容:

<%@ ServiceHost Service="MyNamespace..Service1" CodeBehind="Service1.svc.cs" Factory="MyNamespace.NullableWebServiceHostFactory" %>
Run Code Online (Sandbox Code Playgroud)

现在,您可以在服务界面中使用可空类型而不会出现任何问题,只需省略参数或将其设置为空字符串即可.以下资源可能对您有更多帮助:

希望这可以帮助!

  • 对于应该由微软实施的nobrainer而言,这是一项很多工作. (4认同)
  • 令人惊讶的是,微软做出了一件容易让事情变得复杂的事情......但是答案很好 (2认同)

Way*_*neC 8

实际上......你绝对可以拥有可以为空的参数,或者QueryStringConverter开箱即用不支持的任何其他类型的参数.您需要做的就是扩展QueryStringConverter以支持您需要的任何类型.请参阅本文中接受的答案==>

在WCF Web编程模型中,如何使用查询字符串参数数组(即具有相同名称)编写操作契约?


Kir*_*rst 1

是的,您可以在 WCF 中使用可为空的参数。我认为你的问题是 QueryStringConverter 不适用于可为空的参数。

该怎么办?您需要使用 UriTemplate 属性吗?如果您将其发布为“经典 Web 服务”,那么您就不会遇到此问题。

另一种选择是遵循您提供的链接中的建议 - 即接收 myX 参数作为字符串,然后将其转换为 int?,其中(例如)“n”为空。不漂亮。