Веб-служба WCF внутри приложения ASP.NET - как поддерживать параметры, допускающие значение NULL?

Я пытаюсь разрешить гибкие параметры метода внутри моей службы WCF, но столкнулся с несколькими проблемами.

Сначала я просто объясню цель. Я хочу иметь возможность объявить веб-метод, например:

    [OperationContract, WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)]
    public StatisticEventArgs GetStatistic(DateTime? startDate = null, DateTime? endDate = null)
    {
        return new StatisticEventArgs() { Count = 50; }
    }

и иметь возможность вызывать его через метод jquery $ .ajax:

$.ajax({
        type: 'GET',
        url: 'service/MyService.svc,
        data: '', // or '?startDate=12/1/2011&endDate=12/10/2011','?startDate=12/1/2011', etc. Any combination of startDate or endDate parameters
        contentType: "application/json",
        dataType: "json",
        processdata: true,
        success: endCallback,
        error: errorCallback 
    });

Цель состоит в том, чтобы создать методы, в которых все параметры являются необязательными, и для любых параметров, которые не указаны, вместо них будет использоваться их значение по умолчанию. В этом примере я хочу иметь возможность вызвать метод, в котором я могу предоставить параметр startDate или endDate, оба или ни один из них.

Я быстро обнаружил, что класс QueryStringParameter по умолчанию (как видно в этом вопросе: В модели веб-программирования WCF, как можно написать контракт операции с массивом параметров строки запроса (то есть с тем же именем)?) не поддерживает типы, допускающие значение NULL, но JsonQueryStringConverter поддерживает. Кроме того, как отмечено в приведенном выше вопросе, в .NET 4.0 и ниже имеется ошибка, связанная с этим решением, которая не позволяет создать экземпляр класса JsonQueryStringConverter в настраиваемом поведении (GetQueryStringConverter никогда не вызывается).

После дополнительных исследований я обнаружил эта ошибка Microsoft, которая предоставляет обходной путь, но только при создании экземпляра службы с помощью кода.

У меня вопрос, как я могу применить это обходное решение в веб-службе ASP.NET WCF? Я считаю, что мне нужно настроить его в web.config, но я не уверен, как это сделать . Вот соответствующий пример кода для того, что у меня есть (я изменил имена, поэтому он может содержать некоторые опечатки):

Класс WCF, содержащийся в моем проекте веб-приложения ASP.NET:

namespace Namespace.Services
{
    [ServiceContract(Namespace = "")]
    [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class MyService
    {

    [OperationContract, WebGet(ResponseFormat = WebMessageFormat.Json, RequestFormat=WebMessageFormat.Json)]
    public StatisticEventArgs GetStatistic(DateTime? startDate = null, DateTime? endDate = null)
    {
        return new StatisticEventArgs() { Count = 50; }
    }

    public class NullableTypeBehaviorBehaviorExtension :  BehaviorExtensionElement
    {
        public override Type BehaviorType
        {
            get { return typeof(NullableTypeBehavior); }
        }

        protected override object CreateBehavior()
        {
            return new NullableTypeBehavior();
        }
    }

    public class NullableTypeBehavior : WebHttpBehavior
    {

        protected override System.ServiceModel.Dispatcher.QueryStringConverter GetQueryStringConverter(OperationDescription operationDescription)
        {
            return new JsonQueryStringConverter();
        }
    }
}

Javascript:

$.ajax({
        type: 'GET',
        url: 'service/MyService.svc/GetStatistic',
        data: '', // or '?startDate=12/1/2011&endDate=12/10/2011','?startDate=12/1/2011', etc. Any combination of startDate or endDate parameters
        contentType: "application/json",
        dataType: "json",
        processdata: true,
        success: endCallback,
        error: errorCallback 
    });

web.config:

<system.serviceModel>
<extensions>
  <behaviorExtensions>
    <add name="nullableTypeBehavior" type="Namespace.Services.NullableTypeBehaviorBehaviorExtension, MyAssembly, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
  </behaviorExtensions>
</extensions>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true"></serviceHostingEnvironment>
<services>
  <service name="MyWcfService">
    <endpoint address="" behaviorConfiguration="MyBehaviorConfig"
      binding="webHttpBinding"  contract="Namespace.Services.MyService" />
  </service>
</services>
<behaviors>
  <serviceBehaviors>
    <behavior name="web">
      <serviceMetadata httpGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="true"/>
    </behavior>
  </serviceBehaviors>
  <endpointBehaviors>
    <behavior name="MyBehaviorConfig">
      <nullableTypeBehavior />
    </behavior>
  </endpointBehaviors>
</behaviors>
</client>

UPDATE 1: SOLUTION

Вот ответ, который я получил (любезно предоставлено VinayC):

public class CustomFactory : System.ServiceModel.Activation.ServiceHostFactory
{
    protected override ServiceHost CreateServiceHost(Type t, Uri[] baseAddresses)
   {
       return new CustomHost(t, baseAddresses);
   }
}

public class CustomHost : ServiceHost
{
    public CustomHost(Type t, params Uri[] baseAddresses) : base(t, baseAddresses) { }

   protected override void OnOpening()
   {
       var nullableTypeBehavior = new NullableTypeBehavior();
      foreach(var ep in this.Description.Endpoints)
      {
         ep.Behaviors.Add(nullableTypeBehavior);
      }
      base.OnOpening();
   }
}

и в верхней части моего файла MyService.svc:

<%@ ServiceHost Language="C#" Debug="true" Service="Namespace.Services.MyService" CodeBehind="MyService.svc.cs" Factory="Namespace.Services.CustomFactory" %>

ОБНОВЛЕНИЕ 2: проблема с датами передачи этого решения

В настоящее время не работает передача дат:

http://localhost/Services/MyService.svc?startDate=11/10/2011&endDate=11/11/2011

но эти запросы работают:

http://localhost/Services/MyService.svc?startDate=null&endDate=null
http://localhost/Services/MyService.svc

Похоже, это специфичный для JsonQueryStringConverter, поэтому я собираюсь изучить его и опубликовать ответ, когда найду решение. Похоже, это должно быть достаточно просто для отладки.

ОБНОВЛЕНИЕ 3. Решение для переноса дат:

Проблема в том, что даты, которые я передавал, не соответствуют ожидаемому формату для .NET JSON. Я проходил что-то вроде:

"12/01/2011"

и .NET JSON (включая JsonQueryStringConverter) ожидает этого формата:

"\/Date(1283219926108)\/"

Этот формат даты не является родным для javascript. Чтобы получить дату в этом формате, я нашел этот пост. После добавления этого скрипта я могу сделать это в jQuery, и дата будет проанализирована соответствующим образом:

 $.ajax({
        type: 'GET',
        url: 'service/MyService.svc/GetStatistic',
        data: '?startDate=' + JSON.stringifyWCF(new Date('12/1/2011'))
        contentType: "application/json",
        dataType: "json",
        processdata: true,
        success: endCallback,
        error: errorCallback 
    });

Вроде все работает как положено. Я могу игнорировать любые необязательные параметры, установить для них значение null или присвоить им значение, и все запросы будут работать.

Обновлена ​​ошибка Microsoft с обходным решением. Обходной путь немного более элегантен, чем решение, представленное здесь.


person user949286    schedule 22.12.2011    source источник


Ответы (1)


Я не уверен, нужны ли вам типы, допускающие значение NULL. Если вы хотите передать несколько значений даты / времени - почему бы не использовать подпись, например

public StatisticEventArgs GetStatistic(DateTime[] startDates = null, DateTime[] endDates = null)

В вашем CustomQueryStringConverter вам нужно проверить, является ли параметр нулевой / пустой строкой, тогда ваш массив будет нулевым. Даты начала и окончания будут коррелированы по индексу. Единственно возможная комбинация, которую вы, возможно, не сможете пройти, будет иметь одну пару, в которой отсутствует дата начала, и другая пара, в которой отсутствует дата окончания. В качестве временного решения вы всегда можете использовать дату начала эпохи.

ИЗМЕНИТЬ:
Если вы пытаетесь применить обходной путь из MS Connect в своей службе WCF, вам необходимо создать настраиваемую фабрику узлов службы. Например,

public class CustomFactory : ServiceHostFactory
{
   public override ServiceHost CreateServiceHost( Type t, Uri[] baseAddresses )
   {
      return new CustomHost( t, baseAddresses )
   }
}

public class CustomHost : ServiceHost
{
   public DerivedHost( Type t, params Uri baseAddresses ) : base( t, baseAddresses ) {}

   public override void OnOpening()
   {
      var nullableTypeBehavior = new NullableTypeBehavior();
      foreach(var ep in this.Description.EndPoints)
      {
         ep.Behaviors.Add(nullableTypeBehavior);
      }
   }
}

И, наконец, используйте директиву @ServiceHost в файле svc для подключения фабрики - Например:

<% @ServiceHost Factory=”CustomFactory” Service=”MyService” %>
person VinayC    schedule 22.12.2011
comment
Спасибо за предложение. На самом деле я не хочу, чтобы несколько дат начала / окончания, я, вероятно, не достаточно ясно понимал это. Когда я говорю «Любая комбинация параметров startDate или endDate», я имею в виду либо параметр startDate, либо параметр endDate, ни один из них или оба сразу. - person user949286; 22.12.2011
comment
Ага, это то, что я искал! Единственное, что мне еще нужно включить параметры startDate и endDate и установить для них значение null, если они отсутствуют. Я предполагаю, что это просто потому, что JsonQueryStringConverter не поддерживает точное поведение, которое я ищу, поэтому мне, возможно, придется свернуть свое собственное. Я обновлю свой пост. - person user949286; 22.12.2011