2009-09-08 20 views
12

Ho un contratto definito in questo modo:Un contratto di servizio WCF può avere un parametro di input nullable?

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

ottengo un'eccezione: [InvalidOperationException: Operazione 'GetX' a contratto 'IMyGet' ha una variabile query denominata 'myX' di tipo 'System.Nullable 1[System.Int32]', but type 'System.Nullable 1 [System.Int32] 'non è convertibile da' QueryStringConverter '. Le variabili per i valori di query UriTemplate devono avere i tipi che può essere convertito da 'QueryStringConverter']

non riusciva a trovare nulla su questo errore, tranne il seguente link:. http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html che è un po 'vecchio e non una soluzione in ogni caso.

qualsiasi idea cosa fare tranne eliminare il parametro nullable?

grazie.

risposta

1

Sì, è possibile avere parametri nullable con WCF. Penso che il tuo problema qui sia che QueryStringConverter non funziona con parametri nullable.

Cosa fare? Hai bisogno di usare l'attributo UriTemplate? Se hai pubblicato questo come un "servizio web classico", non avresti questo problema.

L'altra opzione è seguire il consiglio nel collegamento che hai fornito - cioè ricevere il parametro myX come stringa e poi lanciarlo su un int ?, dove (say) "n" è nullo. Non carino.

8

In realtà ... è assolutamente possibile avere parametri nullable o qualsiasi altro tipo di parametro non supportato da QueryStringConverter. Tutto ciò che devi fare è estendere QueryStringConverter per supportare qualsiasi tipo di cui avresti bisogno. Vedere la risposta accettata in questo 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

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

32

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!

+1

Mi piace questa soluzione. – Sawyer

+4

Questo è un sacco di lavoro per qualcosa che avrebbe dovuto essere un nobrainer per implementare Microsoft. – crush

+2

Che sorpresa, Microsoft crea qualcosa che dovrebbe essere facile in qualcosa di dolorosamente complicato ... Buona risposta anche se – Jim

1

Hum, la soluzione rapida (non carina) consiste nell'accettare il parametro nullable come stringa nei rispettivi codici di interfaccia e servizio WCF.

Problemi correlati