Может ли контракт службы WCF иметь нулевой входной параметр?

у меня есть контракт, определенный следующим образом:

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

Я получаю исключение: [InvalidOperationException: операция "GetX" в контракте "IMyGet" имеет переменную запроса с именем " myX "типа" System.Nullable1[System.Int32]', but type 'System.Nullable1[Система.Int32] 'не конвертируется с помощью 'QueryStringConverter'. Переменные для значений запроса UriTemplate должны иметь типы, которые могут быть преобразованы с помощью QueryStringConverter.]

не удалось найти ничего об этой ошибке, кроме следующего ссылка на сайт: http://blog.rolpdog.com/2007/07/webget-and-webinvoke-rock.html, который является немного старые и не является решением.

есть идеи, что делать, кроме как избавиться от параметра nullable?

спасибо.

4 ответов


Да, вы можете иметь nullable параметры с WCF. Я думаю, ваша проблема здесь в том, что QueryStringConverter не работает с параметрами nullable.

Что делать? Вам нужно использовать атрибут UriTemplate? Если вы опубликовали это как "классический веб-сервис", у вас не будет этой проблемы.

другой вариант-следовать совету по ссылке, которую вы предоставили, т. е. получить параметр myX в виде строки, а затем привести его к int?, где (скажем)" n " равно null. Не очень.


есть решение этой проблемы, которое не требует каких-либо хаков. Это может выглядеть как много работы, но это не действительно и имеет большой смысл, если вы прочитаете его. Суть проблемы в том, что действительно существует нерешенные ошибка (начиная с .NET 4) это означает WebServiceHost не использует пользовательские QueryStringConverters. Поэтому вам нужно немного поработать и понять, как работает конфигурация WCF для WebHttpEndpoints. Ниже излагается решение для вас.

во-первых, пользовательские 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, так быть использованным вместо стандартного одного. Обратите внимание, что такое поведение derivces от WebHttpBehavior что важно, чтобы мы унаследовали поведение требуется для конечной точки REST:

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

теперь пользовательского ServiceHost это добавляет пользовательское поведение к WebHttpEndpoint, так что он будет использовать пользовательский querystringconverter, так. Важно отметить, что в этом коде он происходит от ServiceHost, а не WebServiceHost. Это важно, потому что в противном случае ошибка, упомянутая выше, предотвратит пользовательский 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 нам нужно сделать его работа и убеждаться что наша конфигурация правильна для обеспечения обслуживания остальных будет работать. Что-то вроде следующего-это все, что вам нужно. В этой конфигурации у меня также есть настройка конечной точки 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-файлу использовать его, что приведет к использованию всего пользовательского кода. Конечно, вы также можете создать пользовательский элемент, который позволит вам добавить поведение в конфигурации, но я думаю, что для этого поведения лучше использовать подход на основе кода, так как вряд ли вы захотите удалить возможность обработки типов nullable, так как это нарушит вашу службу:

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

измените разметку вашего сервиса.svc файл в следующем:

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

теперь вы можете использовать nullable типы в интерфейсе службы без каких-либо проблем, просто опустив параметр или установив его в пустую строку. Следующие ресурсы могут оказать вам большую помощь:

надеюсь, что это помогает!


на самом деле...вы абсолютно можете иметь nullable параметры или любой другой тип параметра, который не поддерживается QueryStringConverter из коробки. Все, что вам нужно сделать, это продлить QueryStringConverter для поддержки любого типа вам потребуется. См. принятый ответ в этом посте ==>

в модели веб-программирования WCF, как можно написать контракт операции с массивом параметров строки запроса (т. е. с тем же именем)?


Hum, быстрое решение (не очень) - принять параметр nullable в качестве строки в соответствующем интерфейсе WCF и кодах обслуживания.