Подтвердить что ты не робот

Может ли контракт службы 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, который является немного старым, а не решением в любом случае.

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

спасибо.

4b9b3361

Ответ 1

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

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

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

Ответ 2

Существует решение этой проблемы, которая не требует каких-либо взломов. Это может показаться большой работой, но это не так и имеет большой смысл, если вы прочитаете ее. Ядро проблемы состоит в том, что действительно существует неразрешенная ошибка (начиная с .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 вместо стандартного. Обратите внимание, что это поведение выводится из 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, нам нужно сделать это и убедиться, что наша конфигурация верна, чтобы гарантировать, что служба REST будет работать. Что-то вроде следующего - все, что вам нужно. В этой конфигурации у меня также есть настройка конечной точки HTTP HTTP, потому что мне нужно было получить доступ к этой службе как из С# (используя 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" %>

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

Надеюсь, это поможет!

Ответ 3

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

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

Ответ 4

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