2009-09-08 30 views
12

我有這樣定義的合同:WCF服務合約可以有一個可爲空的輸入參數嗎?

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

我得到一個異常: [出現InvalidOperationException:在承包經營「的getX「IMyGet」有類型的名爲「MYX」查詢變量「System.Nullable 1[System.Int32]', but type 'System.Nullable 1 [System.Int32]'不能被'QueryStringConverter'轉換。爲UriTemplate查詢的值的變量必須具有可以通過「QueryStringConverter」]

轉換找不到任何關於此錯誤的任何東西,除了下面的鏈接類型: http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html這是一老一少反正不是一個解決方案。

任何想法做什麼除了擺脫可空參數?

感謝。

回答

1

是的,你可以有WCF的可爲空的參數。我認爲你的問題在於QueryStringConverter不能使用可爲空的參數。

怎麼辦?你需要使用UriTemplate屬性嗎?如果您將其作爲「經典網絡服務」發佈,那麼您不會遇到此問題。

另一種選擇是遵循您提供的鏈接中的建議 - 即接收myX參數作爲字符串,然後將其轉換爲int ?,其中(比如說)「n」爲空。不漂亮。

8

其實......你絕對可以有可空的參數,或任何其他類型的參數,QueryStringConverter開箱即不支持。所有你需要做的就是擴展QueryStringConverter以支持你需要的任何類型。請參閱這篇文章==>

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

上面的代碼引用中存在一個錯誤,它使得QueryStringConverter派生類在框架4中不可用。請確保在嘗試此操作之前查看錯誤。在發現它在實踐中不起作用之前,我浪費了很多時間。 – Jim 2011-05-27 10:55:41

32

接受的答案是有這個問題,不需要任何黑客的解決方案。它可能看起來像很多工作,但它不是真的,並且如果你仔細閱讀它就會很有意義。問題的核心是確實有一個unresolved bug(從.NET 4起),這意味着WebServiceHost不使用自定義QueryStringConverters。因此,您需要做一些額外的工作並瞭解WebHttpEndpoints的WCF配置是如何工作的。以下爲您找出解決方案。

首先,自定義QueryStringConverter,允許通過省略它們,或者提供一個空字符串中的查詢字符串提供空值:

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

現在定製WebHttpBehavior將設置自定義QueryStringConverter被用來代替標準的。需要注意的是,從WebHttpBehavior這種行爲derivces讓我們繼承了一個REST端點所要求的行爲,這是很重要的:即增加了自定義行爲的WebHttpEndpoint

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

現在定製ServiceHost的,使其將使用自定義QueryStringConverter。此代碼中要注意的重要一點是,它來自ServiceHost而不是WebServiceHost。這是很重要的,否則上面提到的bug會阻止自定義QueryStringConverter被使用:

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

,因爲我們沒有從獲得WebServiceHost,我們需要做的工作,並確保我們的配置是正確的確保REST服務能夠正常工作。像下面這樣的東西就是你需要的。在這個配置中,我也有一個WS HTTP端點設置,因爲我需要從C#(使用WS HTTP作爲它的更好)和移動設備(使用REST)訪問此服務。如果不需要,可以省略此端點的配置。需要注意的一點是,您不再需要自定義端點行爲。這是因爲我們現在添加了我們自己的自定義端點行爲,該行爲綁定了自定義的QueryStringConverter。它來源於WebHttpBehavior這是什麼配置添加,使其現在是多餘的。

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

最後要做的是創建一個自定義ServiceHostFactory並告訴SVC文件來使用它,這將導致使用的所有的自定義代碼。當然,你也可以創建一個自定義元素,允許你在配置中添加行爲,但我認爲對於這種行爲,基於代碼的方法更好,因爲你不太可能想要去除處理可空類型的能力,因爲它會破壞你的服務:

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

更改您的Service.svc的標記文件到以下幾點:

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

現在你可以使用你的服務接口可空類型沒有任何問題,只要通過省略參數或將其設置爲空字符串。以下資源可能更多的援助,以你:

希望這有助於!

+1

我喜歡這個解決方案。 – Sawyer 2013-08-07 14:06:12

+4

對於本應該成爲微軟實施的行爲者來說,這是一個很多工作的地獄。 – crush 2014-02-06 14:53:46

+2

真是一個驚喜,微軟讓一些東西應該很容易成爲痛苦的複雜的東西......雖然 – Jim 2014-07-23 19:48:21

1

哼,快速解決方案(不太漂亮)是接受可空參數作爲WCF各自接口和服務代碼中的字符串。

相關問題