ASP.NET应用程序中的WCF Web服务-如何支持可为空的参数?

ASP.NET应用程序中的WCF Web服务-如何支持可为空的参数?,asp.net,wcf,asp.net-4.0,Asp.net,Wcf,Asp.net 4.0,我试图在我的WCF服务中允许灵活的方法参数,但我遇到了一些问题 首先,我将解释一下目标。我希望能够声明web方法,例如: [OperationContract, WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)] public StatisticEventArgs GetStatistic(DateTime? startDate = null, DateTime?

我试图在我的WCF服务中允许灵活的方法参数,但我遇到了一些问题

首先,我将解释一下目标。我希望能够声明web方法,例如:

    [OperationContract, WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)]
    public StatisticEventArgs GetStatistic(DateTime? startDate = null, DateTime? endDate = null)
    {
        return new StatisticEventArgs() { Count = 50; }
    }
并且能够通过jquery$.ajax方法调用它:

$.ajax({
        type: 'GET',
        url: 'service/MyService.svc,
        data: '', // or '?startDate=12/1/2011&endDate=12/10/2011','?startDate=12/1/2011', etc. Any combination of startDate or endDate parameters
        contentType: "application/json",
        dataType: "json",
        processdata: true,
        success: endCallback,
        error: errorCallback 
    });
目标是创建所有参数都是可选的方法,对于未指定的参数,将使用其默认值。在本例中,我希望能够调用可以提供startDate或 或endDate参数,两者都有,或两者都没有

我很快发现默认的QueryStringParameter类(如本问题所示:)不支持可为null的类型,但JsonQueryStringConverter支持。此外,如上面问题中所述,.NET 4.0及以下版本中有一个与此解决方案相关的bug,它阻止在自定义行为中实例化JsonQueryStringConverter类(从未调用GetQueryStringConverter)

经过更多的研究,我发现这提供了一种解决方法,但前提是通过代码实例化服务

我的问题是,如何在ASP.NET WCF Web服务中应用该解决方案?我认为需要在Web.config中进行设置,但我不确定如何实现。以下是我到目前为止所拥有的相关示例代码(我修改了名称,因此可能包含一些拼写错误):

我的ASP.NET Web应用程序项目中包含的WCF类:

namespace Namespace.Services
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MyService
    {

    [OperationContract, WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)]
    public StatisticEventArgs GetStatistic(DateTime? startDate = null, DateTime? endDate = null)
    {
        return new StatisticEventArgs() { Count = 50; }
    }

    public class NullableTypeBehaviorBehaviorExtension :  BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof(NullableTypeBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new NullableTypeBehavior();
        }
    }

    public class NullableTypeBehavior : WebHttpBehavior
    {

        protected override System.ServiceModel.Dispatcher.QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
        {
            return new JsonQueryStringConverter();
        }
    }
}
Javascript:

$.ajax({
        type: 'GET',
        url: 'service/MyService.svc/GetStatistic',
        data: '', // or '?startDate=12/1/2011&endDate=12/10/2011','?startDate=12/1/2011', etc. Any combination of startDate or endDate parameters
        contentType: "application/json",
        dataType: "json",
        processdata: true,
        success: endCallback,
        error: errorCallback 
    });
web.config:

<system.serviceModel>
<extensions>
  <behaviorExtensions>
    <add name="nullableTypeBehavior" type="Namespace.Services.NullableTypeBehaviorBehaviorExtension, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </behaviorExtensions>
</extensions>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"></serviceHostingEnvironment>
<services>
  <service name="MyWcfService">
    <endpoint address="" behaviorConfiguration="MyBehaviorConfig"
      binding="webHttpBinding"  contract="Namespace.Services.MyService" />
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="web">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="true"/>
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
    <behavior name="MyBehaviorConfig">
      <nullableTypeBehavior />
    </behavior>
  </endpointBehaviors>
</behaviors>
</client>
在MyService.svc文件的顶部:

<%@ ServiceHost Language="C#" Debug="true" Service="Namespace.Services.MyService" CodeBehind="MyService.svc.cs" Factory="Namespace.Services.CustomFactory" %>
但这些要求是有效的:

http://localhost/Services/MyService.svc?startDate=null&endDate=null
http://localhost/Services/MyService.svc
似乎这是特定于JsonQueryStringConverter的,所以我将研究它,并在找到解决方案后发回。看起来它应该足够简单,可以调试

更新3:传递日期的解决方案:

问题是我传递的日期不是.NET JSON的预期格式。我路过的地方就像:

"12/01/2011"
NET JSON(包括JsonQueryStringConverter)需要以下格式:

"\/Date(1283219926108)\/"
该日期格式不是javascript的固有格式。为了检索这种格式的日期,我找到了这篇文章。添加此脚本后,我可以在jQuery中执行此操作,日期将被正确解析:

 $.ajax({
        type: 'GET',
        url: 'service/MyService.svc/GetStatistic',
        data: '?startDate=' + JSON.stringifyWCF(new Date('12/1/2011'))
        contentType: "application/json",
        dataType: "json",
        processdata: true,
        success: endCallback,
        error: errorCallback 
    });
一切似乎都按预期进行。我可以忽略任何可选参数,将它们设置为null,或者给它们一个值,所有请求都可以工作


使用解决方法更新了Microsoft错误。解决方案比这里提供的解决方案更优雅。

我不确定您是否需要可空类型。如果要传递多个日期/时间值,为什么不使用签名,例如

public StatisticEventArgs GetStatistic(DateTime[] startDates = null, DateTime[] endDates = null)
CustomQueryStringConverter
中,需要检查参数是否为null/空字符串,然后数组将为null。开始日期和结束日期将通过索引进行关联。您可能无法通过的唯一可能的组合是缺少开始日期的一对和缺少结束日期的另一对。作为一种变通方法,您始终可以使用某个纪元开始日期

编辑
如果您试图在WCF服务中应用MS Connect的变通方法,则需要创建自定义服务主机工厂。比如说,

public class CustomFactory : ServiceHostFactory
{
   public override ServiceHost CreateServiceHost( Type t, Uri[] baseAddresses )
   {
      return new CustomHost( t, baseAddresses )
   }
}

public class CustomHost : ServiceHost
{
   public DerivedHost( Type t, params Uri baseAddresses ) : base( t, baseAddresses ) {}

   public override void OnOpening()
   {
      var nullableTypeBehavior = new NullableTypeBehavior();
      foreach(var ep in this.Description.EndPoints)
      {
         ep.Behaviors.Add(nullableTypeBehavior);
      }
   }
}
最后在svc文件中使用,以插入工厂-例如:

<% @ServiceHost Factory=”CustomFactory” Service=”MyService” %>


谢谢你的建议。实际上,我不希望有多个开始/结束日期,我可能对此不够清楚。当我说“startDate或endDate参数的任意组合”时,我指的是startDate参数,endDate参数,两者都不是,或者两者都不是!唯一的问题是,我仍然必须包括startDate和endDate参数,并在缺少它们时将它们设置为null。我假设这只是因为JsonQueryStringConverter不支持我正在寻找的确切行为,所以我可能不得不自己运行。我会更新我的帖子。
public class CustomFactory : ServiceHostFactory
{
   public override ServiceHost CreateServiceHost( Type t, Uri[] baseAddresses )
   {
      return new CustomHost( t, baseAddresses )
   }
}

public class CustomHost : ServiceHost
{
   public DerivedHost( Type t, params Uri baseAddresses ) : base( t, baseAddresses ) {}

   public override void OnOpening()
   {
      var nullableTypeBehavior = new NullableTypeBehavior();
      foreach(var ep in this.Description.EndPoints)
      {
         ep.Behaviors.Add(nullableTypeBehavior);
      }
   }
}
<% @ServiceHost Factory=”CustomFactory” Service=”MyService” %>