2009-09-08 6 views
12

Ich habe einen Vertrag wie folgt definiert:Kann ein WCF-Dienstvertrag einen nullbaren Eingabeparameter haben?

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

Ich erhalte eine Ausnahme: [InvalidOperationException: Operation 'GetX' im Vertrag 'IMyGet' hat eine Abfrage Variable mit dem Namen 'myX' vom Typ ‚System.Nullable 1[System.Int32]', but type 'System.Nullable 1 [System.Int32] 'kann nicht von' QueryStringConverter 'konvertiert werden. Variablen für UriTemplate Abfragewerte müssen Typen haben, die von ‚Querystringconverter‘ umgewandelt werden kann]

nichts über diesen Fehler außer dem folgenden Link finden kann. http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html die ohnehin ein wenig alt und keine Lösung ist.

irgendwelche Ideen, was zu tun, außer den NULL-Parameter loswerden?

danke.

Antwort

1

Ja, Sie können NULL-Werte mit WCF haben. Ich denke, Ihr Problem hier ist, dass QueryStringConverter nicht mit Nullable-Parametern funktioniert.

Was ist zu tun? Müssen Sie das UriTemplate-Attribut verwenden? Wenn Sie dies als "klassischen Webdienst" veröffentlicht hätten, hätten Sie dieses Problem nicht. Die andere Option besteht darin, dem Ratschlag in dem von Ihnen angegebenen Link zu folgen - d. H. Den myX-Parameter als String zu erhalten und ihn dann auf ein int ?, wo (sagen wir) "n" null ist. Nicht hübsch.

8

Eigentlich ... Sie können absolut nullable Parameter oder jede andere Art von Parameter, der von QueryStringConverter nicht standardmäßig unterstützt wird. Alles, was Sie tun müssen, ist QueryStringConverter zu erweitern, um jeden Typ zu unterstützen, den Sie benötigen würden. Siehe die akzeptierte Antwort in diesem Beitrag ==>

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

Es gibt einen Fehler in der obigen Code-Referenz, der die von QueryStringConverter abgeleiteten Klassen in Framework 4 unbrauchbar macht. Stellen Sie sicher, dass Sie sich den Fehler angesehen haben, bevor Sie dies versuchen. Ich habe viel Zeit verschwendet, bevor ich entdeckt habe, dass es in der Praxis nicht funktioniert. – Jim

32

Es gibt eine Lösung für dieses Problem ist, dass kein Hacks erfordert. Es sieht vielleicht nach viel Arbeit aus, aber es ist nicht wirklich und macht viel Sinn, wenn man es durchliest. Der Kern des Problems besteht darin, dass es tatsächlich eine unresolved bug (ab .NET 4) gibt, die WebServiceHost nicht benutzerdefinierte AbfrageStringConverter verwendet. Sie müssen also etwas mehr arbeiten und verstehen, wie die WCF-Konfiguration für WebHttpEndpoints funktioniert. Das Folgende beschreibt die Lösung für Sie.

Zuerst wird eine benutzerdefinierte Querystringconverter, die NULL-Werte können durch Weglassen sie in die Query-String zur Verfügung gestellt werden, oder eine leere Zeichenfolge bereitstellt:

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); 
    } 
} 

Jetzt eine benutzerdefinierte WebHttpBehavior, die die benutzerdefinierte gesetzt wird QueryStringConverter anstelle des Standard-verwendet werden. Beachten Sie, dass dieses Verhalten derivces von WebHttpBehavior was wichtig ist, so dass wir das Verhalten, die für einen REST-Endpunkt erben:

public class NullableWebHttpBehavior : WebHttpBehavior 
{ 
    protected override QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription) 
    { 
     return new NullableQueryStringConverter(); 
    } 
} 

Jetzt ein benutzerdefinierten Servicehost, die das benutzerdefinierte Verhalten des WebHttpEndpoint fügt, so dass es wird den benutzerdefinierten QueryStringConverter verwenden.Die wichtige Sache in diesem Code zu beachten, ist, dass es von ServiceHost und nicht WebServiceHost abgeleitet ist. Dies ist wichtig, weil sonst die oben genannten Fehler werden die benutzerdefinierten Querystringconverter verhindern, verwendet:

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(); 
    } 
} 

Weil wir Ableitung nicht aus Webservicehost wir es Arbeit tun müssen, und sicherstellen, dass unsere Konfiguration korrekt ist Stellen Sie sicher, dass der REST-Service funktioniert. So etwas wie das Folgende ist alles was Sie brauchen. In dieser Konfiguration habe ich auch eine WS-HTTP-Endpunkt-Einrichtung, weil ich auf diesen Dienst sowohl von C# (unter Verwendung von WS HTTP als dessen netter) als auch von mobilen Geräten (unter Verwendung von REST) ​​zugreifen musste. Sie können die Konfiguration für diesen Endpunkt weglassen, wenn Sie ihn nicht benötigen. Eine wichtige Sache zu beachten ist, dass Sie nicht mehr das benutzerdefinierte Endpunktverhalten benötigen. Dies liegt daran, dass wir jetzt unser eigenes benutzerdefiniertes Endpunktverhalten hinzufügen, das den benutzerdefinierten QueryStringConverter bindet. Es stammt von WebHttpBehavior was ist die Konfiguration hinzugefügt, so dass es jetzt überflüssig.

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

Das letzte, was zu tun ist, eine benutzerdefinierte Servicehost erstellen und die SVC-Datei sagen, es zu benutzen, die alle benutzerdefinierten Code verursacht verwendet werden. Natürlich könnten Sie auch ein benutzerdefiniertes Element erstellen, mit dem Sie das Verhalten in der Konfiguration hinzufügen können, aber ich denke, für dieses Verhalten ist ein code-basierter Ansatz besser, da es unwahrscheinlich ist, dass Sie die Fähigkeit zum Verarbeiten von Nullwerten entfernen möchten. wie es Ihren Dienst brechen:

public sealed class NullableWebServiceHostFactory : ServiceHostFactory 
{ 
    protected override ServiceHost CreateServiceHost(Type serviceType, Uri[] baseAddresses) 
    { 
     return new NullableWebServiceHost(serviceType, baseAddresses); 
    } 
} 

ändern Sie den Markup Ihrer Service.svc auf die folgende Datei:

<%@ ServiceHost Service="MyNamespace..Service1" CodeBehind="Service1.svc.cs" Factory="MyNamespace.NullableWebServiceHostFactory" %> 

Jetzt können Sie Nullable-Typen in Ihrem Service-Schnittstelle ohne Probleme verwenden, einfach durch den Parameter weglassen oder ihn auf eine leere Zeichenfolge setzen. Die folgenden Ressourcen können Sie von mehr Hilfe sein:

hoffe, das hilft!

+1

Ich mag diese Lösung. – Sawyer

+4

Das ist verdammt viel Arbeit für etwas, das ein Nobrainer für Microsoft hätte sein sollen. – crush

+2

Was für eine Überraschung, macht Microsoft etwas, das leicht in etwas schmerzlich über kompliziert sein sollte ... Gute Antwort obwohl – Jim

1

Hum, die schnelle Lösung (nicht hübsch) ist, den Nullable-Parameter als Zeichenfolge in den jeweiligen WCF-Interface- und Service-Codes zu akzeptieren.

Verwandte Themen