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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1393359/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-06 16:01:01  来源:igfitidea点击:

Can a WCF service contract have a nullable input parameter?

c#restwcfnullable

提问by Ami

I have a contract defined like this:

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

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

I get an exception: [InvalidOperationException: Operation 'GetX' in contract 'IMyGet' has a query variable named 'myX' of type 'System.Nullable1[System.Int32]', but type 'System.Nullable1[System.Int32]' is not convertible by 'QueryStringConverter'. Variables for UriTemplate query values must have types that can be converted by 'QueryStringConverter'.]

我得到一个例外: [InvalidOperationException: Operation 'GetX' in contract 'IMyGet' has a query variable named 'myX' of type 'System.Nullable 1[System.Int32]', but type 'System.Nullable1[System.Int32]' is not convertible by 'QueryStringConverter'。UriTemplate 查询值的变量必须具有可由“QueryStringConverter”转换的类型。]

could not find anything about this error except the following link: http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.htmlwhich is a little old and not a solution anyway.

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

any ideas what to do except get rid of the nullable parameter?

除了摆脱可为空的参数之外,还有什么想法可以做吗?

thanks.

谢谢。

采纳答案by Kirk Broadhurst

Yes, you can have nullable parameters with WCF. I think your problem here is that QueryStringConverter does not work with nullable parameters.

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

What to do? Do you need to use the UriTemplate attribute? If you published this as a 'classic web service' then you wouldn't have this issue.

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

The other option is to follow the advise in the link you provided - i.e. receive the myX parameter as string and then cast it to an int?, where (say) "n" is null. Not pretty.

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

回答by WayneC

Actually...you absolutely can have nullable parameters, or any other type of parameter that isn't supported by QueryStringConverterout of the box. All you need to do is extend QueryStringConverterto support any type you would need. See the accepted answer in this post ==>

实际上……您绝对可以拥有可为空的参数,或QueryStringConverter开箱即用不支持的任何其他类型的参数。您需要做的就是扩展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)?

在 WCF web 编程模型中,如何编写带有查询字符串参数数组(即同名)的操作合约?

回答by Xcalibur

There is a solution to this problem that does not require any hacks. It might look like a lot of work but it's not really and makes a lot of sense if you read through it. The core of the problem is that there is indeed an unresolved bug(as of .NET 4) that means the WebServiceHostdoes not use custom QueryStringConverters. So you need to a little extra work and understand how the WCF configuration for WebHttpEndpoints works. The below lays out the solution for you.

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

First, a custom QueryStringConverterthat allows nulls to be provided in the query string by omitting them, or providing a blank string:

首先,自定义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);
    }
}

Now a custom WebHttpBehaviorthat will set the custom QueryStringConverterto be used in place of the standard one. Note that this behavior derivces from WebHttpBehaviorwhich is important so that we inherit the behavior required for a REST endpoint:

现在自定义WebHttpBehavior将设置自定义QueryStringConverter以代替标准的使用。请注意,此行为源自WebHttpBehavior,这很重要,因此我们继承了 REST 端点所需的行为:

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

Now a custom ServiceHostthat adds the custom behavior to the WebHttpEndpointso that it will use the custom QueryStringConverter. The important thing to note in this code, is that it derives from ServiceHostand NOT WebServiceHost. This is important because otherwise the bug mentioned above will prevent the custom QueryStringConverterfrom being used:

现在自定义ServiceHost将自定义行为添加到WebHttpEndpoint以便它将使用自定义QueryStringConverter。在这段代码中需要注意的重要一点是,它派生自ServiceHost而不是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();
    }
}

Because we are not deriving from WebServiceHostwe need to do it's work and make sure our configuration is correct to ensure the REST service will work. Something like the following is all you need. In this configuration, i also have a WS HTTP endpoint setup because i needed to access this service from both C# (using WS HTTP as its nicer) and mobile devices (using REST). You can omit the configuration for this endpoint if you don't need it. One important thing to note is that you DO NOT need the custom endpoint behavior anymore. This is because we are now adding our own custom endpoint behavior that binds the custom QueryStringConverter. It derives from WebHttpBehaviorwhich is what the configuration added, making it now redundant.

因为我们不是从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>

The last thing to do is create a custom ServiceHostFactoryand tell the svc file to use it, which will cause all the custom code to be used. Of course you could also create a custom element that would allow you to add the behavior in configuration, but i think for this behavior a code-based approach is better, since it is unlikely you will want to remove the ability to process nullable types, as it will break your service:

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

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

Change the markup of your Service.svc file to the following:

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

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

Now you can use nullable types in your service interface without any issues, simply by omitting the parameter or setting it to an empty string. The following resources may be of more assistance to you:

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

Hope this helps!

希望这可以帮助!

回答by wolf354

Hum, the quick solution (not pretty) is to accept the nullable parameter as a string in the WCF respective Interface and Service codes.

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