我正在尝试在我的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 类(如本问题所示: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)?)不支持可空类型,但 JsonQueryStringConverter 支持。此外,如上面的问题所述,.NET 4.0及更低版本中存在与此解决方案相关的错误,该错误会阻止 JsonQueryStringConverter 类在自定义行为中实例化(从不调用GetQueryStringConverter)。 / p>
经过更多研究后,我发现this Microsoft bug提供了一种解决方法,但仅限于通过代码实例化服务。
我的问题是,如何在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>
更新1: 解决方案
以下是我最终得到的答案(由VinayC提供):
public class CustomFactory : System.ServiceModel.Activation.ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type t, Uri[] baseAddresses)
{
return new CustomHost(t, baseAddresses);
}
}
public class CustomHost : ServiceHost
{
public CustomHost(Type t, params Uri[] baseAddresses) : base(t, baseAddresses) { }
protected override void OnOpening()
{
var nullableTypeBehavior = new NullableTypeBehavior();
foreach(var ep in this.Description.Endpoints)
{
ep.Behaviors.Add(nullableTypeBehavior);
}
base.OnOpening();
}
}
并位于MyService.svc文件的顶部:
<%@ ServiceHost Language="C#" Debug="true" Service="Namespace.Services.MyService" CodeBehind="MyService.svc.cs" Factory="Namespace.Services.CustomFactory" %>
更新2:传递此解决方案的日期时出现问题
目前通过日期不起作用:
http://localhost/Services/MyService.svc?startDate=11/10/2011&endDate=11/11/2011
但这些请求有效:
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错误。解决方法比这里提供的解决方案更优雅。
答案 0 :(得分:1)
我不确定你是否需要可空类型。如果要传递多个日期/时间值 - 为什么不使用签名,例如
public StatisticEventArgs GetStatistic(DateTime[] startDates = null, DateTime[] endDates = null)
在CustomQueryStringConverter
中,您需要检查参数是否为空/空字符串,然后您的数组将为空。开始日期和结束日期将按索引相关联。只有您可能无法通过的可能组合才会有一对缺少开始日期,另一对缺少结束日期。作为解决方法,您始终可以使用一些纪元开始日期。
修改强>:
如果您尝试在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 directive来插入工厂 - 例如:
<% @ServiceHost Factory=”CustomFactory” Service=”MyService” %>