C'è una soluzione a questo problema che non richiede alcun hack. Potrebbe sembrare un sacco di lavoro, ma non è davvero e ha molto senso se lo leggi. Il nocciolo del problema è che esiste effettivamente un unresolved bug (da .NET 4) che significa che il WebServiceHost non utilizza QueryStringConverters personalizzati. Quindi è necessario un piccolo lavoro extra e capire come funziona la configurazione WCF per WebHttpEndpoints. Il sotto stabilisce la soluzione per te.
In primo luogo, una consuetudine QueryStringConverter che consente valori nulli da fornire nella stringa di query da loro omettendo, o la fornitura di una stringa vuota:
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);
}
}
Ora una consuetudine WebHttpBehavior che imposterà l'usanza QueryStringConverter da utilizzare al posto di quello standard. Si noti che questo comportamento derivces da WebHttpBehavior che è importante in modo che ereditiamo il comportamento richiesto per un endpoint REST:
public class NullableWebHttpBehavior : WebHttpBehavior
{
protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
{
return new NullableQueryStringConverter();
}
}
Ora un ServiceHost personalizzato che aggiunge il comportamento personalizzato alla WebHttpEndpoint in modo che utilizzerà l'unità personalizzata QueryStringConverter.La cosa importante da notare in questo codice è che deriva da ServiceHost e NON WebServiceHost. Questo è importante perché altrimenti il bug di cui sopra consentirà di evitare l'usanza QueryStringConverter venga utilizzato:
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();
}
}
perché non siamo derivanti da WebServiceHost abbiamo bisogno di fare è un lavoro e assicurarsi che la nostra configurazione è corretto assicurarsi che il servizio REST funzioni. Qualcosa come il seguente è tutto ciò di cui hai bisogno. In questa configurazione, ho anche una configurazione endpoint WS HTTP perché avevo bisogno di accedere a questo servizio da C# (usando WS HTTP come più bello) e dispositivi mobili (usando REST). È possibile omettere la configurazione per questo endpoint se non è necessario. Una cosa importante da notare è che NON è più necessario il comportamento dell'endpoint personalizzato. Questo perché ora stiamo aggiungendo il nostro comportamento dell'endpoint personalizzato che associa l'unità personalizzata QueryStringConverter. Deriva da WebHttpBehavior che è ciò che la configurazione ha aggiunto, rendendolo ridondante.
<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>
L'ultima cosa da fare è creare un personalizzato ServiceHostFactory e raccontare il file SVC di usarlo, che farà sì che tutto il codice personalizzato da utilizzare. Naturalmente potresti anche creare un elemento personalizzato che ti consentirebbe di aggiungere il comportamento nella configurazione, ma penso che per questo comportamento sia meglio un approccio basato sul codice, dal momento che è improbabile che tu voglia rimuovere la capacità di elaborare i tipi nullable, come si romperà il vostro servizio:
public sealed class NullableWebServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new NullableWebServiceHost(serviceType, baseAddresses);
}
}
Cambiare il markup del vostro Service.svc file al seguente:
<%@ ServiceHost Service="MyNamespace..Service1" CodeBehind="Service1.svc.cs" Factory="MyNamespace.NullableWebServiceHostFactory" %>
Ora è possibile utilizzare tipi nullable nella vostra interfaccia di servizio senza problemi, semplicemente omettendo il parametro o impostandolo su una stringa vuota. Le seguenti risorse possono essere di maggiore aiuto a voi:
Spero che questo aiuti!
C'è un bug nel codice di riferimento sopra che rende le classi derivate da QueryStringConverter inutilizzabili nel framework 4. Assicurati di dare un'occhiata al bug prima di provare questo. Ho perso un sacco di tempo prima di scoprire che non funziona nella pratica. – Jim