- android - RelativeLayout 背景可绘制重叠内容
- android - 如何链接 cpufeatures lib 以获取 native android 库?
- java - OnItemClickListener 不起作用,但 OnLongItemClickListener 在自定义 ListView 中起作用
- java - Android 文件转字符串
我有一个这样定义的契约(Contract):
[OperationContract]
[WebGet(UriTemplate = "/GetX?myStr={myStr}&myX={myX}", BodyStyle = WebMessageBodyStyle.Wrapped)]
string GetX(string myStr, int? myX);
我得到一个异常(exception):[InvalidOperationException:契约(Contract)“IMyGet”中的操作“GetX”有一个名为“myX”的查询变量,类型为“System.Nullable1[System.Int32]”,但类型为“System.Nullable
1[System .Int32]' 不能被 'QueryStringConverter' 转换。 UriTemplate 查询值的变量必须具有可由“QueryStringConverter”转换的类型。]
除了以下链接外,找不到关于此错误的任何信息: http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html这有点旧,无论如何都不是解决方案。
除了去掉可空参数之外还有什么想法吗?
谢谢。
最佳答案
这个问题有一个不需要任何技巧的解决方案。它可能看起来像很多工作,但实际上并非如此,如果您通读它,它会很有意义。问题的核心是确实有一个unresolved bug (从 .NET 4 开始)这意味着 WebServiceHost 不使用自定义 QueryStringConverter。因此,您需要做一些额外的工作并了解 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);
}
}
现在是自定义 WebHttpBehavior,它将设置自定义 QueryStringConverter 以代替标准的使用。请注意,此行为源自 WebHttpBehavior,这很重要,因此我们可以继承 REST 端点所需的行为:
public class NullableWebHttpBehavior : WebHttpBehavior
{
protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
{
return new NullableQueryStringConverter();
}
}
现在是自定义 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();
}
}
因为我们不是从 WebServiceHost 派生的,所以我们需要完成它的工作并确保我们的配置正确以确保 REST 服务能够正常工作。像下面这样的东西就是你所需要的。在此配置中,我还设置了一个 WS HTTP 端点,因为我需要从 C#(使用 WS HTTP 更好)和移动设备(使用 REST)访问该服务。如果不需要,可以省略此端点的配置。需要注意的一件重要事情是您不再需要自定义端点行为。这是因为我们现在要添加我们自己的自定义端点行为来绑定(bind)自定义 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>
最后要做的是创建自定义 ServiceHostFactory 并告诉 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" %>
现在您可以毫无问题地在服务接口(interface)中使用可空类型,只需省略参数或将其设置为空字符串即可。以下资源可能对您更有帮助:
希望这对您有所帮助!
关于c# - WCF 服务协定是否可以具有可为 null 的输入参数?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1393359/
我是一名优秀的程序员,十分优秀!