Hay una solución a este problema que no requiere ningún hacks. Puede parecer mucho trabajo, pero no tiene mucho sentido si lo lees. El núcleo del problema es que efectivamente existe un unresolved bug (a partir de .NET 4) que significa que WebServiceHost no utiliza QueryStringConverters personalizados. Por lo tanto, necesita un poco de trabajo adicional y comprender cómo funciona la configuración de WCF para WebHttpEndpoints. A continuación se presenta la solución para usted.
En primer lugar, una costumbre QueryStringConverter que permite a los nulos que se proporcionan en la cadena de consulta por su omisión, o proporcionar una cadena en blanco:
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);
}
}
ahora una costumbre WebHttpBehavior que establecerá la costumbre QueryStringConverter para ser utilizado en lugar del estándar. Observe que este comportamiento derivces de WebHttpBehavior lo cual es importante para que nosotros heredamos el comportamiento requerido para un extremo REST:
public class NullableWebHttpBehavior : WebHttpBehavior
{
protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
{
return new NullableQueryStringConverter();
}
}
ahora una costumbre ServiceHost que se suma el comportamiento personalizado a la WebHttpEndpoint para que usará el QueryStringConverter personalizado.Lo importante a tener en cuenta en este código es que deriva de ServiceHost y NO WebServiceHost. Esto es importante porque de lo contrario el error mencionado anteriormente evitará la costumbre QueryStringConverter se utilice:
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();
}
}
Debido a que no se derivan de WebServiceHost que tenemos que hacer es un trabajo y asegurarse de que nuestra configuración es correcto asegúrese de que el servicio REST funcione. Algo como lo siguiente es todo lo que necesitas. En esta configuración, también tengo una configuración de punto final WS HTTP porque necesitaba acceder a este servicio tanto desde C# (usando WS HTTP como mejor) como desde dispositivos móviles (usando REST). Puede omitir la configuración para este punto final si no lo necesita. Una cosa importante a tener en cuenta es que NO NECESITA más el comportamiento del punto final personalizado. Esto se debe a que ahora estamos agregando nuestro propio comportamiento de punto final personalizado que vincula el QueryStringConverter personalizado. Se deriva de WebHttpBehavior, que es lo que la configuración agregó, por lo que ahora es redundante.
<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>
La última cosa que hacer es crear una costumbre ServiceHostFactory y decirle al archivo SVC para usarlo, lo que hará que todo el código personalizado que se utilizará. Por supuesto, también puede crear un elemento personalizado que le permita agregar el comportamiento en la configuración, pero creo que para este comportamiento es mejor un enfoque basado en código, ya que es poco probable que desee eliminar la capacidad de procesar tipos que aceptan nulos. ya que va a romper su servicio:
public sealed class NullableWebServiceHostFactory : ServiceHostFactory
{
protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses)
{
return new NullableWebServiceHost(serviceType, baseAddresses);
}
}
Cambiar el marcado de su Service.svc archivo a la siguiente:
<%@ ServiceHost Service="MyNamespace..Service1" CodeBehind="Service1.svc.cs" Factory="MyNamespace.NullableWebServiceHostFactory" %>
Ahora puede utilizar tipos anulables en su interfaz de servicio sin ningún problema, simplemente omitiendo el parámetro o configurándolo en una cadena vacía. Los siguientes recursos pueden ser de más ayuda para usted:
Espero que esto ayude!
Hay un error en la referencia de código anterior que hace que las clases derivadas QueryStringConverter no se puedan utilizar en el marco 4. Asegúrese de echar un vistazo al error antes de intentar esto. Perdí mucho tiempo antes de descubrir que no funciona en la práctica. – Jim