Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
204
Добавлен:
10.05.2015
Размер:
34.76 Mб
Скачать

15.3. Работа с веб-службами в asp.Net ajax

Теперь, когда мы научились работать с языком JavaScript и сумели оптимизировать работу сайта при помощи ASP.NET AJAX стоит оценить преимущества и недостатки текущей реализации Интернет-магазина. Нам удалось сократить время работы серверного кода (хотя и незначительно) за счет того, что мы используем UpdatePanel, и теперь метод Page.Render отрисовывает не всю странице целиком, а только определенные части. Более того, уменьшился и объем данных, передаваемый от сервера клиенту, опять же за счет того, что передается не вся страница. Второе преимущество, которого нам удалось добиться, заключается в том, что пользователь не наблюдает процесс перезагрузки всей страницы при каждой обратной передаче, что делает пользовательский интерфейс страницы более естественным.

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

Один из подходов к решению этих проблем заключается в использовании веб-сервисов. Вместо того чтобы каждый раз ради отправки или получения данных посылать или получать всю страницу целиком, можно сделать так, чтобы клиентский код JavaScript обращался на сервер, получал необходимые данные и сам же их отображал. Этот подход серьезно сократит трафик между сервером и клиентом (в случае с таблицей продуктов, о которой в дальнейшем пойдет речь, вместо 17-18 килобайт данных будет передаваться 1 килобайт) и снизит нагрузку на сервер, так как теперь его основные обязанностями будут заключаться в том, чтобы манипулировать данными.

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

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

15.3.1. Создание веб-службы

Для того чтобы добавить веб-сервис в проект, необходимо кликнуть правой кнопкой мыши по проекту в окне Solution Explorer и выбрать раздел меню Add New Item. В открывшемся диалоговом окне, необходимо выбрать элемент Web Service (рис. 15.2).

Рис. 15.2.  Окно добавления нового веб-сервиса в проект

В результате в проект будет добавлено два файла: WebProductService.asmx и WebProductService.cs, причем последний фал будет помещен в директорию App_code.

Чтобы разрешить вызов веб-служб (ASMX) из клиентского сценария на веб-странице ASP.NET, необходимо добавить на страницу элемент управления ScriptManager. Чтобы определить ссылку на веб-службу, необходимо добавить дочерний элемент asp:ServiceReference к элементу управления ScriptManager. После этого необходимо установить URL-адрес веб-службы в качестве значения атрибута ссылки на сервер path. Объект ServiceReference определяет необходимость создания прокси-класса JavaScript для вызова указанной веб-службы в ASP.NET.

Так как в нашем случае ScriptManager определен на мастере страниц, изменим его код следующим образом:

<asp:ScriptManager ID="Scriptmanager1" runat="server">

<Services>

<asp:ServiceReference Path="~/WebProductService.asmx" />

</Services>

</asp:ScriptManager>

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

Прежде чем приступить к реализации самого сервиса, напишем два вспомогательных класса. Первый класс ProductDTO (DTO – Data Transfer Object) будет представлять собой описание структуры данных продукта, которую сервис будет отправлять клиенту.

[Serializable]

public class ProductDTO

{

public int ProductID { get; set; }

public string ProductNumber { get; set; }

public string Name { get; set; }

public string Color { get; set; }

public decimal ListPrice { get; set; }

public string FullSize { get; set; }

public string Weight { get; set; }

public ProductDTO(Product p)

{

ProductID = p.ProductID;

ProductNumber = p.ProductNumber;

Name = p.Name;

Color = p.Color;

ListPrice = p.ListPrice;

FullSize = p.FullSize;

Weight = p.Weight + p.WeightUnitMeasureCode;

}

public ProductDTO()

{

}

}

Примечание 1: Атрибут [Serializable] в данном случае необязателен. Он показывает, что объекты этого классы должны поддерживать возможность представляться в виде строки, которую можно передать от одного сервиса к другому так, чтобы получающий сервис смог восстановить передаваемый объект. Сериализовать объект можно в xml-файл, json-объект или в бинарный код.

Примечание 2: так как данный класс сериализуем, необходимо чтобы у него был определен публичный конструктор без параметров.

Примечание 3: нам необходимо разработать вспомогательные классы, так как класс Product не сериализуем. Это связано с тем, что сериализации подвергаются все public свойства, среди которых есть ссылки на объекты, которые также рекурсивно сериализуются. Так, например, Product имеет ссылку ProductSubcategory на свою подкатегорию, которая в свою очередь имеет ссылку Products. В результате при сериализации произойдет зацикливание.

Второй класс также будет использоваться для передачи информации клиенту. Он состоит из двух свойств:

  • Result предназначается для передачи коллекции ProductDTO, которая удовлетворяет текущим критериям поиска;

  • TotalCount содержит общее количество продуктов, которые соответствуют критериям поиска.

Этот класс позволит организовать постраничный вывод продуктов. Ниже представлен код этого класса:

public class ResultStructure

{

public object Result{get;set;}

public int TotalCount { get; set; }

}

Теперь, когда все вспомогательные структуры готовы, необходимо определить сервис, который будет обрабатывать запросы:

/// <summary>

/// Summary description for WebProductService

/// </summary>

[WebService(Namespace = "http://tempuri.org/")]

[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]

// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.

[System.Web.Script.Services.ScriptService]

public class WebProductService : System.Web.Services.WebService {

Этот код будет автоматически добавлен Visual Studio, при создании сервиса. Нам необходимо разкомментировать атрибут ScriptService, так как он позволяет вызвать сервис из JavaScript.

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

[WebMethod]

[ScriptMethod(ResponseFormat = ResponseFormat.Json)]

public ResultStructure GetProducts(string category, string subcategory, int skip)

{

DataClassesDataContext dcdc =

new DataClassesDataContext(

"Data Source=localhost;Initial Catalog=AdventureWorks;Integrated Security=True");

var query = from p in dcdc.Products select p;

if (!string.IsNullOrEmpty(subcategory))

{

query = query.Where(p => p.ProductSubcategoryID == Convert.ToInt32(subcategory));

}

else

{

if (!string.IsNullOrEmpty(category))

{

query = query.Where(p => p.ProductSubcategory.ProductCategoryID == Convert.ToInt32(category));

}

}

return new ResultStructure()

{

Result = query.Skip(skip).Take(10).Select(p => new ProductDTO(p)).ToList(),

TotalCount = query.Count()

};

}

Код веб-метода идентичен коду, который разрабатывался на предыдущих занятиях для заполнения GridView данными о продуктах, только теперь метод не привязывает данные к какому-либо серверному компоненту, а возвращает сериализованные данные состоящие из коллекции продуктов и их общего количества. В атрибуте ScriptMethod указан параметр ResponseFormat = ResponseFormat.Json, который указывает, что результат необходимо сериализовать в формате JSON. Мы выбрали этот формат, так как он является основным формат представления данных для языка JavaScript.

Если вызвать наш сервис и передать ему параметры, то будет получен ответ, аналогичный приведенному:

{"d":{"__type":"ResultStructure",

"Result":

[{"ProductID":982,"ProductNumber":"BK-M38S-42",

"Name":"Mountain-400-W Silver, 42",

"Color":"Silver","ListPrice":769.4900,

"FullSize":"42 CM ","Weight":"27,13LB "},

{"ProductID":983,"ProductNumber":"BK-M38S-46",

"Name":"Mountain-400-W Silver, 46",

"Color":"Silver","ListPrice":769.4900,

"FullSize":"46 CM ","Weight":"27,42LB "},

{"ProductID":984,"ProductNumber":"BK-M18S-40",

"Name":"Mountain-500 Silver, 40",

"Color":"Silver","ListPrice":564.9900,

"FullSize":"40 CM ","Weight":"27,35LB"},

{"ProductID":985,"ProductNumber":"BK-M18S-42",

"Name":"Mountain-500 Silver, 42",

"Color":"Silver","ListPrice":564.9900,

"FullSize":"42 CM ","Weight":"27,77LB "},

{"ProductID":986,"ProductNumber":"BK-M18S-44",

"Name":"Mountain-500 Silver, 44",

"Color":"Silver","ListPrice":564.9900,

"FullSize":"44 CM ","Weight":"28,13LB "},

{"ProductID":987,"ProductNumber":"BK-M18S-48",

"Name":"Mountain-500 Silver, 48",

"Color":"Silver","ListPrice":564.9900,

"FullSize":"48 CM ","Weight":"28,42LB "},

{"ProductID":988,"ProductNumber":"BK-M18S-52",

"Name":"Mountain-500 Silver,52",

"Color":"Silver","ListPrice":564.9900,

"FullSize":"52 CM ","Weight":"28,68LB "},

{"ProductID":989,"ProductNumber":"BK-M18B-40",

"Name":"Mountain-500 Black, 40",

"Color":"Black","ListPrice":539.9900,

"FullSize":"40 CM ","Weight":"27,35LB "},

{"ProductID":990,"ProductNumber":"BK-M18B-42",

"Name":"Mountain-500 Black, 42",

"Color":"Black","ListPrice":539.9900,

"FullSize":"42 CM ","Weight":"27,77LB "},

{"ProductID":991,"ProductNumber":"BK-M18B-44",

"Name":"Mountain-500 Black, 44",

"Color":"Black","ListPrice":539.9900,

"FullSize":"44 CM ","Weight":"28,13LB "}],

"TotalCount":32}}

При этом для сервиса будут доступны страницы, на которых будут представлены описание методов, которые предоставляет сервис, а также тестовые страницы, на которых можно вызвать метод, передав параметры. Впрочем, в случае использования JSON-формата данных, вызвать методы не получится, так как это приведет к ошибке. Примеры таких страниц представлены на рис. 15.3 и рис. 15.4.

Рис. 15.3.  Страница с описанием сервиса

Рис. 15.4.  Тестовая страница для операции GetProduct сервиса WebProductService