Веб-служба WCF в приложении ASP.NET - как поддерживать обнуляемые параметры? - PullRequest
0 голосов
/ 22 декабря 2011

Я пытаюсь разрешить гибкие параметры метода внутри моей службы 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 как можно написать контракт операции с массивом параметров строки запроса (т. е. с тем же именем)? ) не поддерживает типы, допускающие обнуление, но 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>

ОБНОВЛЕНИЕ 1: РЕШЕНИЕ

Вот ответ, который я закончил (любезно предоставлено 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 с обходным решением.Обходной путь немного более элегантен, чем решение, представленное здесь.

1 Ответ

1 голос
/ 22 декабря 2011

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

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

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

EDIT
Если вы пытаетесь применить обходной путь из 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” %>
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...