Асинхронные вызовы методов

Просматривая CS-файл, сгенерированный Wsdl.exe, можно заметить, что класс прокси содержит как синхронные, так и асинхронные оболочки методов Web-сервиса. Последние могут быть задействованы для асинхронного вызова Web-методов. Асинхронный вызов возвращает управление немедленно независимо от того, сколько времени нужно Web-сервису на обработку вызова. Чтобы получить результаты асинхронного вызова, позднее нужно выполнить еще один вызов.

Вот пример асинхронного вызова метода Add Web-сервиса Calc.asmx. Клиент вызывает метод прокси BeginAdd для инициирования асинхронного вызова и затем занимается другими делами. Позднее он завершает вызов метода вызовом EndAdd:

CalculatorWebService calc = new CalculatorWebService ();IAsyncResult res = calc.BeginAdd (2, 2, null, null); . . .int sum = calc.EndAdd (res);

Если в момент вызова EndAdd обработка вызова Web-метода еще не завершена, EndAdd блокирует вызывающую программу, пока обработка не завершится. При необходимости клиент может задействовать свойство IsCompleted интерфейса lAsyncResult, возвращенного BeginAdd, чтобы определить, завершился ли Web-метод, и избежать преждевременного обращения к EndAdd:

IAsyncResult res = calc.BeginAdd (2, 2, null, null); . . .if (res.IsCompleted) { int sum = calc.EndAdd (res);}else { // Try again later}

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

AsyncCallback cb = new AsyncCallback (AddCompleted);IAsyncResult res = calc.BeginAdd (2, 2, cb, null); . . .public void AddCompleted (IAsyncResult res){ int sum = calc.EndAdd (res);}

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

Приложение CityView

Web-приложение CityView (рис.11) - это графический клиент Web-сервиса. Используемый им Web-сервис - это Microsoft TerraService (http://terraservice.net/terraservice.asmx), - интерфейс БД Microsoft TerraServer. Подробнее о TerraServer и TerraService см. по адресу http://terraservice.net . TerraServer - одна из самых больших в мире БД, доступных через Интернет, — содержит фотографии и карты большей части земной поверхности, которые стали общедоступны благодаря сотрудничеству Microsoft и U.S. Geological Survey. TerraService реализует Web-методы для доступа к содержимому TerraServer. Всего имеется 16 таких Web-методов с именами вроде ConvertPlaceToLonLatPt и GetTile. Как вы могли ожидать, TerraService написан с помощью Microsoft.NET Framework. Его WSDL-контракт см. по адресу http://terraservice.net/terraservice.asmx?wsdl.

Асинхронные вызовы методов - student2.ru

Рис. 11.CityView: аэрофотоснимок Сан-Франциско

Прежде чем запустить CityView, его нужно установить на Web-сервер.

1.Скопируйте с прилагаемого CityView.aspx и CityView.ashx в выбранный вами виртуальный каталог.

2. В каталоге, куда вы скопировали CityView.aspx и CityView.ashx, создайте подкаталог bin. Затем поместите TerraService.dll в каталог bin. TerraService.dll — это DLL, содержащая прокси-класс TerraService по имени TerraService.

Теперь попробуйте вызвать CityView.aspx в своем браузере. Введите имя города (например, «New York») и выберите штат. Щелкните Show Image. После короткой задержки указанный город появится внизу страницы. Если CityView не может получить запрашиваемое изображение, вместо него выводится сообщение «Image not available». Возможно, вы задали несуществующий город. А может, TerraService временно отключен, или у вас возникли проблемы с соединением с Интернетом.

Вы можете уменьшать и увеличивать размер изображения, выбирая разные масштабы. По умолчанию берется масштаб 8 метров. Выберите меньшее число для более крупного масштаба или большее число для более мелкого. Чтобы получить отличный вид полуострова Сан-Франциско и моста Золотые ворота, введите San Francisco, CA и выберите масштаб 32 метра.

Как работает CityView

CityView состоит из трех файлов:

■ CityView.aspx;

■ CityView, ashx;

■ TerraService.dll.

CityView.aspx — это Web-форма, определяющая пользовательский интерфейс программы (рис.12). Пользовательский интерфейс состоит из элемента управления TextBox для ввода названий городов, элемента управления DropDownList для выбора штатов, элемента управления RadioButtonList для выбора масштаба и элемента управления Button для возврата формы и получения в ответ изображений. Здесь также имеется элемент управления Image, чье свойство ImageUrl программно устанавливается после каждого возврата формы. Вот код, присваивающий URL элементу управления Image:

MyImage.ImageUrl = builder.ToString ();

Если ввести Redmond, WA и оставить масштаб по умолчанию (8 м), то строка, присваиваемая ImageURL, выглядит так:

CityView.ashx?city=Redmond&state=WA&scale=8

что позволяет перейти к обсуждению второго компонента CityView — CityView.ashx.

CityView.ashx — это HTTP-обработчик. Точнее, это HTTP-обработчик, который генерирует и возвращает растровое изображение места, указанного строкой запроса. Когда мы разрабатывали HTTP-обработчик , мы писали его код в CS-файле, компилировали его в DLL и помещали DLL в подкаталог bin корневого каталога приложения. Мы также регистрировали обработчик в файле Web.config.

CityView.ashx демонстрирует другой способ установки HTTP-обработчиков. Вы просто реализуете класс, производный от IHttpHandler, в файле ASHX и указываете директиву @ WebHandler, задающую имя класса и язык, на котором он написан:

<%@ WebHandler Language="C#" Class="CityViewImageGen" %>

Когда клиент запрашивает ASHX-файл, содержащий класс WebHandler, ASP.NET компилирует класс автоматически. Преимущество размещения НТТР-обработчика в ASHX-файле в том, что вам не нужно регистрировать его в файле CONFIG или метабазе IIS; достаточно просто скопировать ASHX-файл на Web-сервер. Недостаток, кончено же, в том, что обработчик нужно тщательно протестировать, чтобы быть уверенным, что ASP.NET сможет его скомпилировать.

CityViewlmageGen в CityView.ashx (рис. 12) генерирует изображения, которые отображает CityView.aspx. Его центром является метод ProcessRequest, вызываемый при каждом запросе. ProcessRequest вызывает для генерации изображения локальный метод GetTiledlmage. Затем он возвращает изображение в НТТР-отклике, вызывая Save для объекта Bitmap, инкапсулирующего изображение:

bitmap.Save (context.Response.OutputStream, format);

В случае ошибки в GetTiledlmage, ProcessRequest возвращает простую картинку, содержащую вместо аэрофотоснимка слова «Image not available». Он также настраивает формат растрового изображения в соответствии с типом возвращаемой информации: JPEG для фотографий и GIF для картинок, содержащих текст.

GetTiledlmage использует три Web-метода TerraService:

■ ConvertPlaceToLonLatPt — преобразует «место» (город, штат, страна) в долготу и широту;

■ GetAreaFromPt — принимает широту, долготу и размер изображения (в пикселах) и возвращает AreaBoundingBox — соответствует границам изображения;

■ GetTile — принимает ID квадрата (полученный от AreaBoundingBox) и возвращает соответствующий квадрат.

«Квадрат» - это квадратное изображение участка земли с размером стороны в 200 пикселов. Для получения изображений большего размера клиент TerraService должен считать несколько квадратов и совместить их. Именно так GetTiledlmage генерирует возвращаемые им изображения размером 600X400. Сначала создается объект Bitmap, представляющий формируемое изображение. Затем с помощью GrapbicsDrawImage на изображение наносятся нужные квадраты. Данный алгоритм не зависит от размера изображения, так что при желании CityView можно заставить показывать изображения большего (или меньшего) размера. Для этого найдите с CityView.ashx оператор:

Bitmap bitmap = GetTiledImage (city, state, res, 600, 400);

и подставьте желаемые ширину и высоту вместо 600 и 400.

Третий и последний компонент CityView — TerraService.dll — содержит прокси-класс TerraService. Метод GetTiledlmage из CityView.ashx создает экземпляр прокси-класса и использует полученный объект для вызова Web-методов TerraService:

TS.TerraService ts = new TS.TerraService ( );

TerraService.dll получена в результате компиляции TerraService.cs, для генерации которого использована команда:

wsdl /namespace:TS http://terraservice.net/terraservice.asmx

Пространство имен необходимо для предотвращения конфликта между некоторыми типами данных, определенными в WSDL-контракте TerraService, и типами

данных FCL. После того как TerraService.cs был создан, следующая команда скомпилировала его в DLL:

esc /t:library terraservice.сs

CityView.aspx

<html>

<body>

<h1>CityView</h1>

<hr>

<form runat="server">

<table cellpadding="8">

<tr>

<td>

City

</td>

<td>

<asp:TextBox ID="City" Width="100%" RunAt="server" />

</td>

<td>

<asp:RequiredFieldValidator

ControlToValidate="City"

ErrorMessage="*"

Display="static"

Color="red"

RunAt="server"

/>

</td>

</tr>

<tr>

<td>

State

</td>

<td>

<asp:DropDownList ID="State" Width="100%" RunAt="server">

<asp:ListItem Text="AL" RunAt="server" />

<asp:ListItem Text="AK" RunAt="server" />

<asp:ListItem Text="AR" RunAt="server" />

<asp:ListItem Text="AZ" RunAt="server" />

<asp:ListItem Text="CA" RunAt="server" />

<asp:ListItem Text="CO" RunAt="server" />

<asp:ListItem Text="CT" RunAt="server" />

<asp:ListItem Text="DC" RunAt="server" />

<asp:ListItem Text="DE" RunAt="server" />

<asp:ListItem Text="FL" RunAt="server" />

<asp:ListItem Text="GA" RunAt="server" />

<asp:ListItem Text="HI" RunAt="server" />

<asp:ListItem Text="IA" RunAt="server" />

<asp:ListItem Text="ID" RunAt="server" />

<asp:ListItem Text="IL" RunAt="server" />

<asp:ListItem Text="IN" RunAt="server" />

<asp:ListItem Text="KS" RunAt="server" />

<asp:ListItem Text="KY" RunAt="server" />

<asp:ListItem Text="LA" RunAt="server" />

<asp:ListItem Text="MA" RunAt="server" />

<asp:ListItem Text="MD" RunAt="server" />

<asp:ListItem Text="ME" RunAt="server" />

<asp:ListItem Text="MI" RunAt="server" />

<asp:ListItem Text="MN" RunAt="server" />

<asp:ListItem Text="MO" RunAt="server" />

<asp:ListItem Text="MS" RunAt="server" />

<asp:ListItem Text="MT" RunAt="server" />

<asp:ListItem Text="NC" RunAt="server" />

<asp:ListItem Text="ND" RunAt="server" />

<asp:ListItem Text="NE" RunAt="server" />

<asp:ListItem Text="NH" RunAt="server" />

<asp:ListItem Text="NJ" RunAt="server" />

<asp:ListItem Text="NM" RunAt="server" />

<asp:ListItem Text="NV" RunAt="server" />

<asp:ListItem Text="NY" RunAt="server" />

<asp:ListItem Text="OH" RunAt="server" />

<asp:ListItem Text="OK" RunAt="server" />

<asp:ListItem Text="OR" RunAt="server" />

<asp:ListItem Text="PA" RunAt="server" />

<asp:ListItem Text="RI" RunAt="server" />

<asp:ListItem Text="SC" RunAt="server" />

<asp:ListItem Text="SD" RunAt="server" />

<asp:ListItem Text="TN" RunAt="server" />

<asp:ListItem Text="TX" RunAt="server" />

<asp:ListItem Text="UT" RunAt="server" />

<asp:ListItem Text="VA" RunAt="server" />

<asp:ListItem Text="VT" RunAt="server" />

<asp:ListItem Text="WA" RunAt="server" />

<asp:ListItem Text="WI" RunAt="server" />

<asp:ListItem Text="WV" RunAt="server" />

<asp:ListItem Text="WY" RunAt="server" />

</asp:DropDownList>

</td>

<td>

</td>

</tr>

<tr>

<td>

</td>

<td>

<fieldset>

<legend>Scale</legend>

<asp:RadioButtonList ID="Scale" RunAt="server"

RepeatColumns="2" RepeatDirection="Horizontal">

<asp:ListItem Text="1 meter" RunAt="server" />

<asp:ListItem Text="2 meters" RunAt="server" />

<asp:ListItem Text="4 meters" RunAt="server" />

<asp:ListItem Text="8 meters" Selected="true"

RunAt="server" />

<asp:ListItem Text="16 meters" RunAt="server" />

<asp:ListItem Text="32 meters" RunAt="server" />

</asp:RadioButtonList>

</fieldset>

</td>

<td>

</td>

</tr>

<tr>

<td>

</td>

<td>

<asp:Button Text="Show Image" OnClick="OnShowImage"

Width="100%" RunAt="server" />

</td>

<td>

</td>

</tr>

</table>

</form>

<hr>

<asp:Image ID="MyImage" RunAt="server" />

</body>

</html>

<script language="C#" runat="server">

void OnShowImage (Object sender, EventArgs e)

{

StringBuilder builder = new StringBuilder ();

builder.Append ("CityView.ashx?city=");

builder.Append (City.Text);

builder.Append ("&state=");

builder.Append (State.SelectedItem.Text);

builder.Append ("&scale=");

switch (Scale.SelectedIndex) {

case 0:

builder.Append ("1");

break;

case 1:

builder.Append ("2");

break;

case 2:

builder.Append ("4");

break;

case 3:

builder.Append ("8");

break;

case 4:

builder.Append ("16");

break;

case 5:

builder.Append ("32");

break;

}

MyImage.ImageUrl = builder.ToString ();

}

</script>

CityView.ashx

<%@ WebHandler Language="C#" Class="CityViewImageGen" %>

using System;

using System.Web;

using System.Drawing;

using System.Drawing.Imaging;

using System.IO;

public class CityViewImageGen : IHttpHandler

{

public void ProcessRequest (HttpContext context)

{

// Extract user input from the query string

string city = context.Request["City"];

string state = context.Request["State"];

string scale = context.Request["Scale"];

if (city != null && state != null) {

// Determine the scale

TS.Scale res = TS.Scale.Scale8m;

if (scale!= null) {

switch (scale) {

case "1":

res = TS.Scale.Scale1m;

break;

case "2":

res = TS.Scale.Scale2m;

break;

case "4":

res = TS.Scale.Scale4m;

break;

case "8":

res = TS.Scale.Scale8m;

break;

case "16":

res = TS.Scale.Scale16m;

break;

case "32":

res = TS.Scale.Scale32m;

break;

}

}

// Generate the requested image

string type = "image/jpeg";

ImageFormat format = ImageFormat.Jpeg;

Bitmap bitmap = GetTiledImage (city, state, res, 600, 400);

// If GetTiledImage failed, generate an error bitmap

if (bitmap == null) {

bitmap = GetErrorImage ("Image not available");

type = "image/gif";

format = ImageFormat.Gif;

}

// Set the response's content type

context.Response.ContentType = type;

// Write the image to the HTTP response

bitmap.Save (context.Response.OutputStream, format);

// Clean up and return

bitmap.Dispose ();

}

}

public bool IsReusable

{

get { return true; }

}

Bitmap GetTiledImage (string City, string State, TS.Scale Scale,

int cx, int cy)

{

Bitmap bitmap = null;

Graphics g = null;

try {

// Instantiate the TerraService proxy

TS.TerraService ts = new TS.TerraService ();

// Get the latitude and longitude of the requested city

TS.Place place = new TS.Place ();

place.City = City;

place.State = State;

place.Country = "USA";

TS.LonLatPt point = ts.ConvertPlaceToLonLatPt (place);

// Compute the parameters for a bounding box

TS.AreaBoundingBox abb = ts.GetAreaFromPt (point,

TS.Theme.Photo, Scale, cx, cy);

// Create an image to fit the bounding box

bitmap = new Bitmap (cx, cy,

PixelFormat.Format32bppRgb);

g = Graphics.FromImage (bitmap);

int x1 = abb.NorthWest.TileMeta.Id.X;

int y1 = abb.NorthWest.TileMeta.Id.Y;

int x2 = abb.NorthEast.TileMeta.Id.X;

int y2 = abb.SouthWest.TileMeta.Id.Y;

for (int x=x1; x<=x2; x++) {

for (int y=y1; y>=y2; y--) {

TS.TileId tid = abb.NorthWest.TileMeta.Id;

tid.X = x;

tid.Y = y;

Image tile = Image.FromStream

(new MemoryStream (ts.GetTile (tid)));

g.DrawImage (tile,

(x - x1) * tile.Width -

(int) abb.NorthWest.Offset.XOffset,

(y1 - y) * tile.Height -

(int)abb.NorthWest.Offset.YOffset,

tile.Width, tile.Height);

tile.Dispose();

}

}

// Return the image

return bitmap;

}

catch (Exception) {

if (bitmap != null)

bitmap.Dispose ();

return null;

}

finally {

if (g != null)

g.Dispose ();

}

}

Bitmap GetErrorImage (string message)

{

// Determine the width and height of the error message

Bitmap bitmap =

new Bitmap (1, 1, PixelFormat.Format32bppRgb);

Graphics g = Graphics.FromImage (bitmap);

Font font = new Font ("Verdana", 10);

SizeF size = g.MeasureString (message, font);

int cx = (int) size.Width;

int cy = (int) size.Height;

bitmap.Dispose ();

g.Dispose ();

// Generate a bitmap containing the error message

bitmap = new Bitmap (cx, cy, PixelFormat.Format32bppRgb);

g = Graphics.FromImage (bitmap);

SolidBrush redBrush = new SolidBrush (Color.Red);

SolidBrush whiteBrush = new SolidBrush (Color.White);

g.FillRectangle (whiteBrush, 0, 0, 256, 64);

g.DrawString (message, font, redBrush, 0, 0);

whiteBrush.Dispose ();

redBrush.Dispose ();

font.Dispose ();

g.Dispose ();

// Return the image

return bitmap;

}

}

Рис. 12. Текст программы CityView

Системные требования

Для компиляции и исполнения примеров программ на вашем компьютере должен быть установлен .NET Framework Software Develop menu Kit (SDK). SDK работает под Windows NT 4.0/2000/XP, предположительно и на позднейших версиях Windows. .NET Framework SDK версии 1.0, а также его Service Pack 1. По мере появления новых версий вы сможете загрузить их по адресу

http://msdn.microsoft.com/downloads/default.asp?url=/downloads/sample.asp?url=/msdn-files/027/000/976/msdncompositedoc.xml. We all know that URLs change. If you go to this one and find that it’s no longer valid, visit the Microsoft .NET home page at for the latest information on where to find the SDK.

Если вы обратитесь по этому URL и окажется, что он неверен, зайдите на домашнюю страницу Microsoft .NET (http://www.microsoft.com/net), где вы найдете самую свежую информацию о том, как получить SDK.

Помимо .NET Framework SDK, вам понадобится установить на свой компьютер Web-сервер Microsoft: Internet Information Services (IIS). Так как ASP.NET требует Windows 2000 или Windows XP, то и вам понадобится одна из этих ОС. IIS не входит в установку по умолчанию для редакции Professional этих ОС. Чтобы установить IIS, откройте инструмент Add or Remove Programs в Панели управления и выберите Add/Remove Windows Components. Там вы найдете поле флажка для US. Обязательно установите IIS перед установкой SDK для гарантии того, что ASP.NET также установится.

Устанавливать Visual Studio .NET для сборки кода для .NET Framework не нужно — SDK содержит компиляторы командной строки. Однако Visual Studio .NET предоставляет высокоинтегрированную среду разработки, упрощающую написание, тестирование и отладку кода. Подробности см. по адресу http://msdn.microsoft.com/vstudio/howtobuy.

Наши рекомендации