2009-09-08 7 views
12

Mam kontrakt zdefiniowany następująco:Czy kontrakt serwisowy WCF ma zerowy parametr wejściowy?

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

uzyskać wyjątek: [InvalidOperationException: Operacja „GetX” w umowie „IMyGet” ma zmienną kwerendy o nazwie „myX” typu „System.Nullable 1[System.Int32]', but type 'System.Nullable 1 [System.Int32] "nie jest wymienialny przez" QueryStringConverter ". Zmienne o wartości zapytań UriTemplate musi mieć typów, które mogą być zamienione przez „QueryStringConverter”]

nie mógł znaleźć nic na temat tego błędu z wyjątkiem poniższego linku. http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html który jest trochę stary i nie jest rozwiązaniem w każdym razie.

wszelkie pomysły, co zrobić, oprócz pozbycia się parametru zerowego?

dzięki.

Odpowiedz

1

Tak, możesz mieć zerowalne parametry z WCF. Myślę, że twój problem polega na tym, że QueryStringConverter nie działa z parametrami zerowymi.

Co robić? Czy potrzebujesz użyć atrybutu UriTemplate? Jeśli opublikujesz to jako "klasyczna usługa sieciowa", nie będziesz mieć tego problemu.

Inną opcją jest wykonanie polecenia podanego linku - tzn. Otrzymanie parametru myX jako łańcucha znaków, a następnie przesłanie go do int ?, gdzie (powiedzmy) "n" ma wartość null. Nie ładne.

8

W rzeczywistości ... absolutnie można mieć parametry zerowujące lub dowolny inny typ parametru, który nie jest obsługiwany przez QueryStringConverter po wyjęciu z pudełka. Wszystko, co musisz zrobić, to przedłużyć QueryStringConverter, aby obsługiwać każdy typ, jakiego potrzebujesz. Zobacz zaakceptowane odpowiedź w tym poście ==>

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

Występuje błąd w powyższym kodzie, który powoduje, że klasy pochodne QueryStringConverter stają się niezdatne do użytku w frameworku 4. Zanim spróbujesz, sprawdź błąd. Zmarnowałem dużo czasu, zanim odkryłem, że to nie działa w praktyce. – Jim

32

Jest rozwiązanie tego problemu, który nie wymaga żadnych hacków. Może wyglądać jak dużo pracy, ale nie jest tak naprawdę i ma sens, jeśli ją przejrzysz. Istotą problemu jest to, że rzeczywiście istnieje unresolved bug (od .NET 4), co oznacza, że ​​WebServiceHost nie używa niestandardowych QueryStringConverters. Musisz więc trochę więcej pracować i zrozumieć, jak działa konfiguracja WCF dla WebHttpEndpoints. Poniżej znajduje się rozwiązanie dla Ciebie.

Najpierw zwyczaj QueryStringConverter który pozwala null które mają być dostarczone w ciągu kwerendy przez ich pominięcie lub dostarczanie pusty ciąg:

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

Teraz niestandardową WebHttpBehavior, która będzie wyznaczać zwyczaj QueryStringConverter do użycia w miejsce standardowego. Zauważ, że to zachowanie derivces z WebHttpBehavior co jest ważne, abyśmy dziedziczyć zachowanie wymaganej do punktu końcowego REST:

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

Teraz zwyczaj ServiceHost który dodaje niestandardowe zachowania do WebHttpEndpoint tak, że użyje niestandardowego QueryStringConverter.Ważne w tym kodzie jest to, że pochodzi on od ServiceHost oraz NOT WebServiceHost. Jest to ważne, ponieważ w przeciwnym razie błędów wspomniano powyżej uniemożliwi zwyczaj QueryStringConverter przed użyciem:

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

Ponieważ nie są pochodzące z WebServiceHost musimy zrobić to praca i upewnij się nasza konfiguracja jest zgodna z upewnij się, że usługa REST będzie działać. Coś jak poniżej jest wszystkim, czego potrzebujesz. W tej konfiguracji mam również konfigurację punktu końcowego WS HTTP, ponieważ potrzebowałem uzyskać dostęp do tej usługi zarówno z C# (używając WS HTTP jako ładniejszego), jak i urządzeń mobilnych (używając REST). Możesz pominąć konfigurację tego punktu końcowego, jeśli go nie potrzebujesz. Należy pamiętać, że nie potrzebujesz już niestandardowego zachowania punktu końcowego. Dzieje się tak, ponieważ teraz dodajemy własne niestandardowe zachowanie punktu końcowego, które wiąże niestandardowy QueryStringConverter. To wynika z WebHttpBehavior, co jest tym, co dodano do konfiguracji, co czyni go teraz zbędnym.

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

Ostatnią rzeczą do zrobienia jest stworzenie własnego ServiceHostFactory i powiedzieć plik SVC go używać, co spowoduje cały kod niestandardowy do użycia. Oczywiście można również utworzyć niestandardowy element, który pozwoliłby na dodanie zachowania w konfiguracji, ale myślę, że dla tego zachowania podejście oparte na kodzie jest lepsze, ponieważ jest mało prawdopodobne, że będziesz chciał usunąć możliwość przetwarzania typów zerowych, jak będzie przerwa usługę:

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

Zmień znaczniki swojego Service.svc złożyć do następujących:

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

teraz można użyć zerowalne typów w interfejsie usługi bez żadnych problemów, po prostu pomijając parametr lub ustawiając go na pusty ciąg. Następujące zasoby mogą być bardziej pomocne dla Ciebie:

Nadzieja to pomaga!

+1

Podoba mi się to rozwiązanie. – Sawyer

+4

To bardzo dużo pracy dla czegoś, co powinno być nobrainerem do implementacji przez Microsoft. – crush

+2

Co za niespodzianka, Microsoft tworzy coś, co powinno być łatwe w coś boleśnie skomplikowanego ... Dobra odpowiedź przez: – Jim

1

Hum, szybkie rozwiązanie (nie ładne) polega na akceptacji parametru zerującego jako łańcucha znaków w odpowiednich interfejsach i usługach WCF.

Powiązane problemy