代码之家  ›  专栏  ›  技术社区  ›  Ami

WCF服务约定能否具有可为空的输入参数?

  •  12
  • Ami  · 技术社区  · 15 年前

    我有一个这样定义的合同:

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

    我得到一个例外: [InvalidOperationException:合同“imyget”中的操作“getx”具有类型为“system.nullable”的名为“myx”的查询变量。 1[System.Int32]', but type 'System.Nullable 1[System.Int32]不能由“QueryStringConverter”转换。uritemplate查询值的变量必须具有可由“queryStringConverter”转换的类型。]

    除了以下链接外,找不到有关此错误的任何信息: http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html 这有点老了,反正也不是解决办法。

    除了去掉可以为空的参数,还有什么想法吗?

    谢谢。

    4 回复  |  直到 7 年前
        1
  •  1
  •   Kirk Broadhurst    15 年前

    是的,可以在wcf中使用可以为空的参数。我认为这里的问题是,QueryStringConverter不适用于可以为空的参数。

    怎么办?是否需要使用uritemplate属性?如果将此发布为“经典Web服务”,则不会出现此问题。

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

        2
  •  34
  •   Community user43968    7 年前

    这个问题有一个解决方案,不需要任何黑客攻击。这可能看起来像很多工作,但它不是真的,如果你通读它会有很多意义。问题的核心是 unresolved bug (从.NET 4开始)这意味着 WebService主机 不使用自定义的QueryStringConverters。因此,您需要做一些额外的工作,并了解WebHTTPendpoints的WCF配置是如何工作的。下面为您列出解决方案。

    第一,风俗习惯 查询字符串转换器 它允许在查询字符串中通过省略空值或提供空字符串来提供空值:

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

    现在习惯了 WebHTTP行为 这将设置自定义 查询字符串转换器 代替标准的。注意,这种行为源自 WebHTTP行为 这一点很重要,因此我们继承了REST端点所需的行为:

    public class NullableWebHttpBehavior : WebHttpBehavior
    {
        protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
        {
            return new NullableQueryStringConverter();
        }
    }
    

    现在是一种习俗 服务宿主 将自定义行为添加到 WebHtdPoT点 这样它就可以使用 查询字符串转换器 . 在这段代码中需要注意的一点是,它是从 服务宿主 而不是 WebService主机 .这很重要,因为否则上面提到的错误将阻止自定义 查询字符串转换器 使用中:

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

    因为我们不是从 WebService主机 我们需要完成它的工作,并确保我们的配置是正确的,以确保其余的服务可以工作。你所需要的就是这样的东西。在这个配置中,我还有一个WS-HTTP端点设置,因为我需要从C(使用WS-HTTP作为更好的服务)和移动设备(使用REST)访问这个服务。如果您不需要这个端点,可以省略它的配置。需要注意的一件重要事情是,您不再需要自定义端点行为。这是因为我们现在正在添加自己的自定义端点行为,该行为绑定自定义 查询字符串转换器 . 它来源于 WebHTTP行为 这就是配置添加的内容,使得它现在是多余的。

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

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

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

    将service.svc文件的标记更改为:

    <%@ ServiceHost Service="MyNamespace..Service1" CodeBehind="Service1.svc.cs" Factory="MyNamespace.NullableWebServiceHostFactory" %>
    

    现在,您可以在服务接口中使用可以为空的类型,而无需任何问题,只需省略参数或将其设置为空字符串即可。以下资源可能对您更有帮助:

    希望这有帮助!

        3
  •  8
  •   Community user43968    7 年前

    实际上……您绝对可以拥有可以为空的参数,或者不受支持的任何其他类型的参数。 QueryStringConverter 开箱即用。你需要做的就是延长 查询字符串转换器 以支持您需要的任何类型。请参阅本帖中接受的答案=>

    In the WCF web programming model, how can one write an operation contract with an array of query string parameters (i.e. with the same name)?

        4
  •  1
  •   wolf354    7 年前

    嗯,快速解决方案(不是漂亮的)是接受可空参数作为wcf各自接口和服务代码中的字符串。