2009-09-08 25 views
12

Tôi có một hợp đồng được xác định như thế này:Hợp đồng dịch vụ WCF có thể có tham số đầu vào không có giá trị không?

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

tôi nhận được một ngoại lệ: [InvalidOperationException: Operation 'GetX' trong hợp đồng 'IMyGet' có một biến truy vấn có tên là 'Myx' kiểu 'System.Nullable 1[System.Int32]', but type 'System.Nullable 1 [System.Int32] 'không được chuyển đổi bởi' QueryStringConverter '. Các biến cho giá trị truy vấn UriTemplate phải có các loại có thể được chuyển đổi bởi 'QueryStringConverter'.]

không thể tìm thấy bất kỳ điều gì về lỗi này ngoại trừ liên kết sau: http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html.

bất kỳ ý tưởng nào cần làm ngoại trừ việc loại bỏ thông số không thể thực hiện được?

cảm ơn.

Trả lời

1

Có, bạn có thể có tham số nullable với WCF. Tôi nghĩ rằng vấn đề của bạn ở đây là QueryStringConverter không làm việc với các tham số nullable.

Việc cần làm? Bạn có cần sử dụng thuộc tính UriTemplate không? Nếu bạn xuất bản này như là một 'dịch vụ web cổ điển' thì bạn sẽ không gặp phải vấn đề này.

Tùy chọn khác là thực hiện theo lời khuyên trong liên kết bạn cung cấp - tức là nhận tham số myX dưới dạng chuỗi và sau đó truyền tham số vào int ?, trong đó (nói) "n" là rỗng. Không đẹp.

8

Thực ra ... bạn hoàn toàn có thể có tham số có thể vô hiệu hoặc bất kỳ loại thông số nào khác không được hỗ trợ bởi QueryStringConverter ngoài hộp. Tất cả những gì bạn cần làm là mở rộng QueryStringConverter để hỗ trợ bất kỳ loại nào bạn cần. Xem câu trả lời được chấp nhận trong bài này ==>

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ó lỗi trong tham chiếu mã ở trên làm cho các lớp dẫn xuất QueryStringConverter không thể sử dụng được trong khung 4. Đảm bảo rằng bạn đã xem lỗi trước khi thử. Tôi lãng phí rất nhiều thời gian trước khi phát hiện ra rằng nó không hoạt động trong thực tế. – Jim

32

Có một giải pháp cho vấn đề này mà không đòi hỏi bất kỳ hacks. Nó có thể trông giống như rất nhiều công việc nhưng nó không thực sự và làm cho rất nhiều ý nghĩa nếu bạn đọc qua nó. Cốt lõi của vấn đề là có thực sự là một unresolved bug (như của .NET 4) có nghĩa là WebServiceHost không sử dụng QueryStringConverters tùy chỉnh. Vì vậy, bạn cần thêm một chút công việc và hiểu cách cấu hình WCF cho WebHttpEndpoints hoạt động như thế nào. Bên dưới đưa ra giải pháp cho bạn.

Đầu tiên, một phong tục QueryStringConverter cho phép null để được cung cấp trong chuỗi truy vấn bằng cách bỏ qua chúng, hoặc cung cấp một chuỗi trống:

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

Bây giờ một tùy chỉnh WebHttpBehavior rằng sẽ thiết lập các tùy chỉnh QueryStringConverter được sử dụng thay cho tiêu chuẩn. Lưu ý rằng hành vi này derivces từ WebHttpBehavior đó là rất quan trọng để chúng ta được thừa hưởng những hành vi cần thiết cho một thiết bị đầu cuối REST:

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

Bây giờ một tùy chỉnh ServiceHost có thêm các hành vi tùy chỉnh để các WebHttpEndpoint để nó sẽ sử dụng tùy chỉnh QueryStringConverter.Điều quan trọng cần lưu ý trong mã này, là nó xuất phát từ ServiceHost và KHÔNG WebServiceHost. Đây là quan trọng bởi vì nếu không các lỗi nêu trên sẽ ngăn chặn các tùy chỉnh QueryStringConverter từ đang được sử dụng:

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

Bởi vì chúng ta không bắt nguồn từ WebServiceHost chúng ta cần phải làm điều đó là công việc và đảm bảo cấu hình của chúng tôi là chính xác để đảm bảo dịch vụ REST sẽ hoạt động. Một cái gì đó như sau là tất cả những gì bạn cần. Trong cấu hình này, tôi cũng có thiết lập điểm cuối WS HTTP vì tôi cần truy cập dịch vụ này từ cả C# (sử dụng WS HTTP như thiết bị đẹp hơn) và các thiết bị di động (sử dụng REST). Bạn có thể bỏ qua cấu hình cho điểm cuối này nếu bạn không cần đến nó. Một điều quan trọng cần lưu ý là bạn KHÔNG cần hành vi điểm cuối tùy chỉnh nữa. Điều này là do chúng tôi hiện đang thêm hành vi điểm cuối tùy chỉnh của riêng mình liên kết tùy chỉnh QueryStringConverter. Nó xuất phát từ WebHttpBehavior đó là cấu hình được thêm vào, làm cho nó bây giờ dư thừa.

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

Điều cuối cùng cần làm là tạo ra một tùy chỉnh ServiceHostFactory và nói với các tập tin svc để sử dụng nó, mà sẽ gây ra tất cả các mã tùy chỉnh để được sử dụng. Tất nhiên bạn cũng có thể tạo phần tử tùy chỉnh cho phép bạn thêm hành vi trong cấu hình, nhưng tôi nghĩ hành vi này dựa trên mã là tốt hơn, vì bạn không muốn loại bỏ khả năng xử lý các loại không có giá trị, vì nó sẽ phá vỡ dịch vụ của bạn:

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

Thay đổi đánh dấu của Service.svc bạn nộp như sau:

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

Bây giờ bạn có thể sử dụng các loại nullable trong giao diện dịch vụ của bạn mà không cần bất kỳ vấn đề, chỉ đơn giản bằng bỏ qua tham số hoặc đặt nó vào một chuỗi rỗng. Các tài liệu sau đây có thể hỗ trợ nhiều hơn nữa cho bạn:

Hope this helps!

+1

Tôi thích giải pháp này. – Sawyer

+4

Đó là một địa ngục của rất nhiều công việc cho một cái gì đó nên đã là một nobrainer để thực hiện bởi Microsoft. – crush

+2

Thật bất ngờ, Microsoft đã tạo ra một thứ gì đó dễ dàng vào một cái gì đó đau đớn hơn phức tạp ... Câu trả lời hay mặc dù – Jim

1

Hum, giải pháp nhanh (không đẹp) là chấp nhận thông số nullable như một chuỗi trong mã giao diện và dịch vụ tương ứng WCF.

Các vấn đề liên quan