Создание WCF сервиса с поддержкой Ajax: ч.1 – Контракты

Недавно меня попросили показать пример создания веб-сервиса для взаимодействия со страницей, использующей технологию Ajax. Давайте посмотрим, как можно решить данную задачу с применением Windows Communication Foundation (WCF).

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

Немного теории

Сервис WCF это приложение, предоставляющее для связи набор Конечных точек (Endpoints). Их можно представить как точки связи в внешним миром.

Конечная точка определяется следующими характеристиками:

  • Адрес (Address) – задает её расположение (например: http://site.com/endpoint.svc).
  • Связывание (Binding) – указывает параметры ее взаимодействия с внешним миром (протокол, кодирование и безопасность).
  • Контракт (Contract) – определяет какие операции и данные она предоставляет клиентам.

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

Разработка веб-приложения с использованием WCF

Давайте запустим Visual Studio 2010 и создадим новое пустое веб-приложение (ASP.NET Empty Web Application) под названием WebsitesCatalog. После завершения разработки оно будет отображать список сайтов, показывая дополнительную информацию о выбранном сайте в отдельном блоке.

Чтобы лучше понять процесс создания WCF сервиса не будем использовать помощника для создания классов. Поэтому необходимо самостоятельно добавить в Reference следующие сборки:

  • System.Runtime.Serialization
  • System.ServiceModel

Кроме того, если необходимо поддерживать HTTP GET запросы и ответы в XML формате, то может потребоваться System.ServiceModel.Web. В примере будет использоваться формат Json, поэтому необходимости в данной сборке нет.

Чтобы не сваливать все файлы в кучу, создадим две папки:

  • Code для хранения классов, работающих с данными;
  • Service для WCF файлов сервиса.

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

Создание WCF сервиса начнем с описания контрактов.

Контракты данных (Data сontracts)

Контракт данных определяет какие данные будут передаваться между севером и клиентом. Для его определения необходимо отметить атрибутами:

  1. [DataContract] – класс, который определяет контракт данных.
  2. [DataMember] – каждое поле этого класса, которое будет участвовать в обмене данными.

Обратите внимание, что не обязательно все поля должны быть отмечены как [DataMember]. Это дает возможность исключать их из списка доступных клиенту.

Контракты для стандартных типов, таких как int или string, уже определены в WCF. Поэтому необходимо описывать контракты только для пользовательских типов.

Добавим в папку Code новый класс WebsiteInfo. В нем укажем свойства, содержащие описание сайта: уникальный код, название, ссылку и описание. Отметим их все атрибутом [DataMember], а сам класс – [DataContract]:

namespace WebsitesCatalog
{
    using System;
    using System.Runtime.Serialization;

    [DataContract]
    public class WebsiteInfo
    {
        [DataMember]
        public string Id { get; set; }

        [DataMember]
        public string Title { get; set; }

        [DataMember]
        public string Url { get; set; }

        [DataMember]
        public string Description { get; set; }
    }
}

Контракт данных определен.

Для получения данных добавим новый класс – источник данных. Для упрощения не будем использовать в проекте базу данных, а воспользуемся экземпляром ConcurrentDictionary в качестве хранилища.

Так же в папке Code создадим класс WebsiteDataSource. Реализация очень простая и не нуждается в дополнительных комментариях:

namespace WebsitesCatalog
{
    using System.Collections.Concurrent;
    using System.Collections.Generic;

    public class WebsiteDataSource
    {
        private static readonly ConcurrentDictionary<string, WebsiteInfo> _dataSource =
            new ConcurrentDictionary<string, WebsiteInfo>();

        public IEnumerable<WebsiteInfo> Items
        {
            get { return WebsiteDataSource._dataSource.Values; }
        }

        public void AddOrUpdate(WebsiteInfo websiteInfo)
        {
            WebsiteDataSource._dataSource.AddOrUpdate(
                websiteInfo.Id, websiteInfo, (key, oldValue) => websiteInfo);
        }

        public WebsiteInfo Get(string id)
        {
            WebsiteInfo websiteInfo;
            if (WebsiteDataSource._dataSource.TryGetValue(id, out websiteInfo)) {
                return websiteInfo;
            }

            websiteInfo = new WebsiteInfo() { Id = string.Empty };
            return websiteInfo;
        }

        public void Delete(string id)
        {
            WebsiteInfo websiteInfo;
            WebsiteDataSource._dataSource.TryRemove(id, out websiteInfo);
        }
    }
}

Теперь, когда определен контракт для данных и есть источник данных, можно перейти к описанию взаимодействия сервиса с клиентами.

Сервисные контракты (Service Contracts)

Сервисные контракты определяют операции, которые может доступны клиенту. Для его определения так же, как и в случае с данными, используются атрибуты:

  • [ServiceContract] – указывает на класс или интерфейс, определяющий методы контракта.
  • [OperationContract] – задает методы, которые будет доступны клиентам.

Кроме того, при указании контракта можно задать пространство имен, которому он будет принадлежать.

Описание может быть как сразу в классе, так и задано в виде интерфейса. Последний вариант является более предпочтительной практикой, т.к. не привязывает контракт к реализации. Поэтому добавим в папку Service файл IWebsiteDataService.cs с описанием интерфейса IWebsiteDataService. Обратите внимание на указание своего пространства имен WebsiteCatalogService для контракта в атрибуте ServiceContract.

namespace WebsitesCatalog
{
    using System.ServiceModel;

    [ServiceContract(Namespace = "WebsiteCatalogService")]
    public interface IWebsiteDataService
    {
        [OperationContract]
        void Insert(WebsiteInfo websiteInfo);

        [OperationContract]
        void Delete(string id);

        [OperationContract]
        WebsiteInfo Get(string id);
    }
}

Перейдем к реализации интерфейса. В дальнейшем, можно использовать помощника для добавления "AJAX-enabled WCF service". Он автоматически создаст файл разметки, реализации и добавит нужные записи в Web.сonfig. Но, как и было сказано, в данном примере создадим всё необходимое самостоятельно.

Добавим в папку Service файл WebsiteDataService.svc.cs (наличие .svc обязательно, т.к. в дальнейшем это будет CodeBehind файл) и создадим в нем класс WebsiteDataService, реализующий интерфейс IWebsiteDataService. Нужно отметить, что для поддержки ASP.NET Ajax необходимо у класса реализации установить атрибут [AspNetCompatibilityRequirements]:

namespace WebsitesCatalog
{
    using System.ServiceModel.Activation;

    [AspNetCompatibilityRequirements(
        RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed)]
    public class WebsiteDataService : IWebsiteDataService
    {
        private readonly WebsiteDataSource _dataSource = new WebsiteDataSource();

        public void Insert(WebsiteInfo websiteInfo)
        {
            if (string.IsNullOrWhiteSpace(websiteInfo.Id)) {
                websiteInfo.Id = Guid.NewGuid().ToString();
            }

            this._dataSource.AddOrUpdate(websiteInfo);
        }

        public void Delete(string id)
        {
            this._dataSource.Delete(id);
        }

        public WebsiteInfo Get(string id)
        {
            return this._dataSource.Get(id);
        }
    }
}

Так же надо создать файл разметки, к которому и будет обращаться клиент. Добавим в папку Service файл WebsiteDataService.svc с следующим содержимым:

<%@ ServiceHost Language="C#" Debug="true" Service="WebsitesCatalog.WebsiteDataService"
    CodeBehind="WebsiteDataService.svc.cs" %>

Для полноценной работы осталось определить параметры взаимодействия.

Связываение (Binding)

Как уже было сказано, Связывание определяет параметры общения сервиса с внешним миром. Откроем файл Web.config и добавим внутрь раздела <configuration> следующе строки:

<system.serviceModel>
  
  <behaviors>
    <endpointBehaviors>
      <behavior name="WebsitesCatalog.WebsiteDataServiceBehavior">
        <enableWebScript />
      </behavior>
    </endpointBehaviors>
  </behaviors>

  <serviceHostingEnvironment aspNetCompatibilityEnabled="true" 
                             multipleSiteBindingsEnabled="true" />

  <services>
    <service name="WebsitesCatalog.WebsiteDataService">
      <endpoint address="" 
       behaviorConfiguration="WebsitesCatalog.WebsiteDataServiceBehavior"
       binding="webHttpBinding"
       contract="WebsitesCatalog.IWebsiteDataService" />
    </service>
  </services>
  
</system.serviceModel>

Что определяется в добавленных строках?

Блок <system.serviceModel> указывает что дальше содержится конфигурация WCF.

Раздел <behaviors> служит для описания поведений. Элемент <enableWebScript /> предоставляет возможность обращаться к Конечной точке со страниц ASP.NET, используя технологию Ajax.

В следующем разделе <serviceHostingEnvironment> расположены указания по настройке среды. Атрибут aspNetCompatibilityEnabled задействует режим совместимости WCF c ASP.NET. При этом разрешаются запросы к WCF сервисам только по HTTP каналам через ASP.NET и блокируются любые другие. Атрибут multipleSiteBindingsEnabled разрешает использовать несколько привязок для одного узла IIS.

В разделе <services> указываются службы <service>, которые доступны извне. Каждая служба содержит Конечная точка (<endpoint>), для которой указываются поведение, связывание и поддерживаемый контракт. Связывание webHttpBinding означает, что используется HTTP протокол для обмена сообщениями (формат ответов Json установлен по умолчанию).

Во второй части займемся разработкой клиента, который будет использовать данный WCF сервис.

Комментарии (6) -

Alexandr Zhuravlev 02.12.2010 15:46:31

Познавательно. Спасибо.

ЗЫ. ссылка на вторую часть статьи неверная - вместо for поставьте with

Спасибо, поправил.

Constantine 03.01.2011 20:07:29

"Добавим в папку Service файл WebsiteDataService.svc "
тут непонятно - мы WCFService добавляем или WCFDataService? Так там не только *.svc файл добавляется. Нужно остальное поудалять или? Поподробнее бы немножко.

Добавляем сервис помощников (это оговорено в статье). Т.е. WebsiteDataService.svc добавляется именно как отдельный простой файл, без использования Add -> New Item -> WCF Service. Используйте Add -> New Item -> Code File с указанием нужного имени и расширения. Такой подход применен для того, чтобы лучше было видно "составляющие". Как было сказано – в реальном проект лучше использовать помощника (Add -> New Item –> AJAX-enabled WCF Service), который и создаст все нужные файлы сразу.

Здравствуйте!

Не получается создать файл разметки по предложенному алгоритму: сначала через AddItem->Code добавляю WebsiteDataService.svc.cs, а при попытке таким же образом добавить WebsiteDataService.svc студия ничего не говорит, а просто создаёт в дереве WebsiteDataService.svc, в котором лежит элемент WebsiteDataService.svc.cs.
Как с этим бороться?

@ Reno: Вначале добавьте Add -> New Item -> WCF Service

Pingbacks and trackbacks (2)+

Добавить комментарий