2009-09-08 24 views
12

tengo un contrato definido así:¿Puede un contrato de servicio WCF tener un parámetro de entrada nullable?

[OperationContract] 
[WebGet(UriTemplate = "/GetX?myStr={myStr}&myX={myX}", BodyStyle = WebMessageBodyStyle.Wrapped)] 
string GetX(string myStr, int? myX); 

me sale una excepción: [InvalidOperationException: Operación 'GetX' en el contrato 'IMyGet' tiene una variable de consulta llamado 'miX' del tipo 'System.Nullable 1[System.Int32]', but type 'System.Nullable 1 [System.Int32] 'no es convertible por' QueryStringConverter '. Las variables de valores de consulta UriTemplate deben tener tipos que se pueden convertir por 'QueryStringConverter']

no hemos podido encontrar nada acerca de este error, excepto el siguiente enlace:. http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html que es un poco viejo y no una solución de todos modos.

¿Alguna idea de qué hacer, excepto deshacerse del parámetro que permite anular?

gracias.

Respuesta

1

Sí, puede tener parámetros anulables con WCF. Creo que su problema aquí es que QueryStringConverter no funciona con parámetros que aceptan nulos.

¿Qué hacer? ¿Necesita usar el atributo UriTemplate? Si publicaste esto como un "servicio web clásico", entonces no tendrías este problema.

La otra opción es seguir el consejo en el enlace que proporcionó, es decir, recibir el parámetro myX como una cadena y luego convertirlo a una int ?, donde (por ejemplo) "n" es nulo. No es bonito.

8

En realidad ... absolutamente puede tener parámetros anulables, o cualquier otro tipo de parámetro que no es compatible con QueryStringConverter fuera de la caja. Todo lo que necesita hacer es extender QueryStringConverter para admitir cualquier tipo que necesite. Ver la respuesta aceptada en este post ==>

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)?

+0

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

32

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!

+1

Me gusta esta solución. – Sawyer

+4

Eso es mucho trabajo para algo que debería haber sido una innovación para implementar por parte de Microsoft. – crush

+2

Qué sorpresa, Microsoft hace algo que debería ser fácil en algo dolorosamente complicado ... Buena respuesta, aunque – Jim

1

Hum, la solución rápida (no bonita) es aceptar el parámetro que se puede anular como una cadena en los respectivos códigos de interfaz y servicio de WCF.

Cuestiones relacionadas