H Уроки WCF. Создание простого сервиса в черновиках Tutorial

This topic pertains to a legacy technology. XML Web services and XML Web service clients should now be created using Windows Communication Foundation .

In addition to letting you create Web pages, Microsoft Visual Studio also lets you create Web services that use ASP.NET XML. Creating a Web service in Visual Studio is similar to creating a Web page. You can also use the Microsoft Visual Web Developer Web development tool to reference and use Web services that are in a Visual Web Developer solution, on your local computer or in a local or external UDDI directory. In this walkthrough, you will create the Web service in one solution and use it in another.

Tasks illustrated in this walkthrough include:

    Creating a simple XML Web service in Visual Web Developer.

    Creating a separate Web site that uses the Web service.

Prerequisites

In order to complete this walkthrough, you will need:

    Microsoft Internet Information Services (IIS) installed locally on your computer.

    NET Framework version 3.5 or higher.

Creating a Web Service Under the IIS Root

Create a new Web service and page by following these steps.

You must use an IIS Web site for this walkthrough.

To create a Web service

Open Visual Web Developer.

On the File menu, click New Web Site .

The New Web Site dialog box appears.

Under , click ASP.NET Web Service .

Click Browse .

Click Local IIS .

Click Default Web Site .

Click Create New Web Application .

Type the name TemperatureWebService.

Click Open .

The New Web Site dialog box appears, with the name of the new Web site in the rightmost Location list. The location includes the protocol (http:// ) and location (localhost ). This indicates that you are working with a local IIS Web site.

In the Language

The programming language that you choose will be the default for the Web site. However, you can use more than one language in the same Web application by creating pages and components in different programming languages. For more information about how to create components using different languages, see Shared Code Folders in ASP.NET Web Site Projects .

Click OK .

Visual Web Developer creates the new Web service and opens a new class named Service, which is the default Web service. However, in the following procedure you will create a new Web service with a specified name and you will not use the Service class.

Close the Service class.

Creating the Web Service

You will create a Web service that converts temperature from Fahrenheit to Celsius and vice versa.

To create the Web service

In Solution Explorer, right-click the Web site name (http://localhost/TemperatureWebService), and then click Add New Item .

Under Visual Studio installed templates , click Web Service , and then in the Name box, type Convert.

Make sure that the Place code in separate file check box is selected, and then click Add .

Visual Web Developer creates a new Web service that is made up of two files. The Convert.asmx file is the file that can be invoked to call Web service methods, and it points to the code for the Web service. The code itself is in a class file (Convert.vb, Convert.cs, or Convert.jsl, depending on the programming language) in the App_Code folder. The code file contains a template for a Web service. The code file includes some code for a Web service method.

You will create two methods in the Web service. The first method converts Fahrenheit temperatures to Celsius, and the second method converts Celsius temperatures to Fahrenheit.

To create the conversion methods

Add the following code inside the class, after the HelloWorld method:

_ Public Function FahrenheitToCelsius(ByVal Fahrenheit As Double) _ As Double Return ((Fahrenheit - 32) * 5) / 9 End Function _ Public Function CelsiusToFahrenheit(ByVal Celsius As Double) _ As Double Return ((Celsius * 9) / 5) + 32 End Function public double FahrenheitToCelsius(double Fahrenheit) { return ((Fahrenheit - 32) * 5) / 9; } public double CelsiusToFahrenheit(double Celsius) { return ((Celsius * 9) / 5) + 32; }

Notice that the function names are preceded with an attribute ( or ) as part of the function declaration.

After you have entered the functions, save the file.

Now, you can test the Web service in Visual Web Developer.

To test the Web service

In Solution Explorer, click Convert.asmx, and then press CTRL+F5.

The Web service is invoked and a page appears in the browser that shows the methods that are exposed by the Web service.

Click CelsiusToFahrenheit , which invokes that method.

A page appears that prompts you for parameter values for the CelsiusToFahrenheit method.

In the Celsius box, type 100, and then click Invoke .

A new window appears that displays the XML that the Web service returns when the CelsiusToFahrenheit method is invoked. The value 212 appears in the XML.

Close the browser that contains the method results.

In the original browser, click Back to return to the list of methods.

Click FahrenheitToCelsius and test to make sure that the method is returning the results that you expect.

If you type 212, the FahrenheitToCelsius method will return 100 .

Close the browser.

You have finished creating the Web service; the next step is to use it.

Using the Web Service

Now that you have a Web service, you will create a Web site where you will reference and use the Web service that you created. For the walkthrough, you will create a separate Web site that has a page where you start the Web service methods that you just created.

To create a Web site to use the Web service

On the File menu, click New Web Site .

Under Visual Studio installed templates , click ASP.NET Web Site .

Click Browse .

Click Local IIS .

Click Default Web Site .

Click Create New Web Application .

Visual Web Developer creates a new IIS Web application.

Type the name TemperatureWeb.

Click Open .

In the Language list, click the programming language that you prefer to work in.

Click OK .

Visual Web Developer creates a new local IIS Web site and a new page named Default.aspx.

Adding the Web Service as a Component

The Web service is a component that you can reference in your application. Therefore, you must create a reference to it.

To create a reference to the Web service

If the server name for the Web service contains characters that cannot be used for a class name, such as a hyphen (-), Visual Web Developer converts those characters to an underscore character (_). Therefore, the namespace in Visual Web Developer for the Web service might not match the server name exactly.

You can now use the Web service. In this walkthrough, you will add controls to Default.aspx, and then program the controls to convert a specified temperature to both Fahrenheit and Celsius. When the page is running, it will look something like the following illustration.

Temperature conversion page

To call the Web service methods

Open the Default.aspx page and switch to Design view.

From the Standard group in the Toolbox, drag the following controls onto the page and set their properties as indicated:

Properties

ID : TemperatureTextbox

Text : (empty)

ID : ConvertButton

Text : Convert

ID : FahrenheitLabel

Text : (empty)

ID : CelsiusLabel

Text : (empty)

  • Optionally, add text to the page for captions.

    For this walkthrough, the layout of the page is not important.

    Double-click ConvertButton to create an event handler for its Click event.

    Make sure your event handler code matches the code in the following example.

    Dim wsConvert As New localhost.Convert() Dim temperature As Double temperature = System.Convert.ToDouble(TemperatureTextbox.Text) FahrenheitLabel.Text = "Fahrenheit To Celsius = " & _ wsConvert.FahrenheitToCelsius(temperature).ToString() CelsiusLabel.Text = "Celsius To Fahrenheit = " & _ wsConvert.CelsiusToFahrenheit(temperature).ToString() protected void ConvertButton_Click(object sender, EventArgs e) { localhost.Convert wsConvert = new localhost.Convert(); double temperature = System.Convert.ToDouble(TemperatureTextbox.Text); FahrenheitLabel.Text = "Fahrenheit To Celsius = " + wsConvert.FahrenheitToCelsius(temperature).ToString(); CelsiusLabel.Text = "Celsius To Fahrenheit = " + wsConvert.CelsiusToFahrenheit(temperature).ToString(); }

    Press CTRL+F5 to run the page.

    In the text box, type a value, such as 100, and then click Convert .

    The page displays the result of converting the temperature value into both Fahrenheit and Celsius.

    Debugging the Web Service

    You can debug a Web service in the same way that you debug Web pages.

    Visual Web Developer Express and Visual Studio Standard do not support stepping into a Web service from a page that references it. If you are using Visual Web Developer Express or Visual Studio Standard, skip this section and the ones following. For more information about how to debug Web sites, see Walkthrough: Debugging Web Pages in Visual Web Developer .

    To start, you must configure the Web site that contains the Web service to enable debugging.

    To enable debugging in the Web services Web site

    On the File menu, click Open Web Site .

    Click Local IIS .

    Click TemperatureWebService, and then click Open .

    On the Website menu, click ASP.NET Configuration

    Click Application , and then click Application Configuration .

    Under Debugging and Tracing , click .

    Select the Enable debugging check box .

    The Web Site Administration Tool creates a Web.config file for the Web site and sets a configuration option to enable debugging.

    To see the Web.config file in Solution Explorer, click the Web site name and then, on the Solution Explorer toolbar, click Refresh .

    You must now enable debugging for the Web site that uses the Web service.

    To enable debugging in the Web site

    Open the TemperatureWeb site.

    On the Website menu, click ASP.NET Configuration to open the Web Site Administration Tool.

    Click Application , click Application Configuration , under Debugging and Tracing , click Configure debugging and tracing , and then select the Enable debugging check box .

    Close the Web Site Administration Tool.

    To see the Web.config file in Solution Explorer, select the Web site name and then, on the Solution Explorer toolbar, click Refresh .

    In Solution Explorer, right-click Default.aspx, and then click View Code .

    Visual Web Developer opens the code file for the page.

    Position the pointer in the following line:

    Temperature = System.Convert.ToDouble(TemperatureTextbox.Text) double temperature = System.Convert.ToDouble(TemperatureTextbox.Text);

    Press F9 to set a breakpoint on the line.

    Testing Debugging

    Both the Web site and the Web service are configured for debugging, so that you can now try debugging. You will start in the Default.aspx page and step through the code until the code invokes the Web service. The debugger will switch to the Web service and continue stepping through the code.

    To debug the page and Web service

    Press F5 to run the Default.aspx page with debugging.

    The page appears in the browser.

    In the box, type a value, such as 100, and then click Convert .

    Visual Web Developer starts running the code for the page, but stops and highlights the line with the breakpoint on it.

    Press F11 to step to the next line.

    Press F11 again.

    Because the next line invokes the Web service, the debugger steps into the Web service, stopping on the first line of the FahrenheitToCelsius method.

    Continue pressing F11.

    The debugger steps through the rest of the method, and then returns to the calling page. If you continue stepping, the debugger will step back into the Web service and into the CelsiusToFahrenheit method.

    Close the browser, which also closes the debugger.

    Next Steps

    This walkthrough has illustrated the basic principles of creating a very simple Web service and using it in an ASP.NET application. You might want to experiment with additional, more complex Web service features. For more information, see.

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

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

    Итак, как же выглядит веб сервис для разработчика? Первое – файл web-сервиса имеет расширение asmx. Второе – создание web-сервиса немногим отличается от создания веб-формы в.NET Framework. Третье – файл веб сервиса должен начинаться с директивы WebService. Четвертое – класс web-сервиса может (но не должен) быть потомком класса System.Web.Services.Webservice. Ну и последнее (пока что) – метод, вызываемый через веб, должен имет атрибут WebMethod.

    Основываясь на вышесказанном начнем реализовывать веб сервис (все примеры в этой главе основаны на codebehind и сделаны с помощью Visual Studio.NET).

    Создадим новое приложение в VS.NET и добавим к нему файл веб сервиса nw.asmx (ну или откроем notepad и создадим 2 файла – nw.asmx и nw.asmx.cs).

    Файл nw.asmx содержит единственную строку – директиву WebService, которая утверждает, что этот файл – действительно веб сервис. Этот файл меняться не будет, так что можете взглянуть на него и пока забыть:).

    Весь код веб сервиса будет располагаться в codebehind файле nw.asmx.cs. Изначально этот файл (созданный в Visual Studion.NET) имеет следующий вид:

    Using System; using System.Collections; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Web; using System.Web.Services; namespace WebServicesExample { /// /// Summary description for nw. /// public class nw: System.Web.Services.WebService { public nw() { //CODEGEN: This call is required by the ASP.NET Web Services Designer InitializeComponent(); } #region Component Designer generated code //Required by the Web Services Designer private IContainer components = null; /// /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// private void InitializeComponent() { } /// /// Clean up any resources being used. /// protected override void Dispose(bool disposing) { if(disposing && components != null) { components.Dispose(); } base.Dispose(disposing); } #endregion // WEB SERVICE EXAMPLE // The HelloWorld() example service returns the string Hello World // To build, uncomment the following lines then save and build the project // To test this web service, press F5 // // public string HelloWorld() // { // return "Hello World"; // } } }

    Как видите ребята из Microsoft сразу же позаботились о начинающих программистах и лишили работы многие поколения учителей, жутко любивших начинать свои рассказы с крика «Hello, World!»:). Теперь вы сами не прилагая никаких усилий можете создать этот код просто откомментировав объявление метода HelloWorld() :).

    Ну что ж, сделаем это (зря, что ли ребята старались? :) и запустим, предварительно откомпилировав проект, на выполнение наш веб сервис (откроем в браузере страницу http://localhost/WebServicesExample/nw.asmx). В браузере нарисуется вот такая страница:


    ASP.NET для отображения web-сервиса использует файл шаблона DefaultWsdlHelpGenerator.aspx, расположенный в папке %SYSTEM_ROOT%\Microsoft.NET\Framework\\CONFIG. На выводимой странице веб сервиса есть название веб сервиса (отмечено 1), ссылка на описание сервиса (2) (эта ссылка в дальнейшем будет интересовать нас при создании клиентов к веб сервису) и список веб методов, объявленных в веб сервисе (3). Остальная часть страницы посвящена тому, как плохо использовать неймспейс по умолчанию для веб сервиса, и рекомендациям срочно исправить это упущение:). Нет проблем и пока это свежо у нас в памяти поступим как рекомендуют – добавим атрибут WebService с параметром Namespace к классу веб сервиса:

    Итак, перейдем к странице описания web-метода SayHello (просто кликните по ссылке SayHello на странице описания web-сервиса).


    Как видите на ней также присутствует название веб сервиса, ссылка на первую страницу веб сервиса, название веб метода. Кроме этого на странице расположена форма, предназначеная для вызова web метода через GET запрос (данное правило не выполняется, если web метод не может быть вызван таким образом) и примеры запросов для вызова данного веб метода с помощью SOAP, HTTP POST (если такой вызов возможен) и HTTP GET (если такой вызов возможен). Также представлены примеры ответов вызова web метода.

    При желании вы можете сейчас протестировать метод нажав на кнопку Invoke (у меня уже такого желания нет:)). Лучше перейдем к созданию более полезных веб методов.

    Я долго думал какой же веб метод предложить первым для изучения. Что-то простое, возвращающее единственное значение, или помонструозней да с DataSetами? И решил все таки в первом же примере показать как можно больше возможностей веб сервисов.

    Итак, задача – торговая фирма, имеющая БД со всеми необходимыми ей для работы данными (ну как тут без Northwind обойтись:)), открывает филиал в другом районе/городе/конце земного шара. При этом возникает естесственная потребность работникам филиала обеспечить доступ к БД фирмы. Но вокруг одни ограничения – база централизована и копию создавать нельзя, а в филиале вообще всего пара компьютеров, которые только и могут, что выйти в интернет. Но работать им как-то с базой все таки надо... :). Хотя бы иметь возможность получать заказы по указанным клиентам.

    Задача поставлена, теперь необходимо ее реализовывать. Для начала мы создадим веб сервис, возвращающий DataSet с заказами указанного клиента, а потом напишем 2 клиента для этого веб сервиса, один на DHTML для IE, второй на ASP.NET (через некоторое время филиал все таки разбогател и купил себе компьютер для веб сервера:)).

    Для получения интересующих нас данных мы будем использовать несколько измененную хранимую процедуру CustOrdersOrders:

    CREATE PROCEDURE CustOrdersOrdersDetails @CustomerID nchar(5) AS SELECT OrderID, OrderDate, RequiredDate, ShippedDate FROM Orders WHERE CustomerID = @CustomerID ORDER BY OrderID SELECT Products.ProductName, .UnitPrice, .Quantity, .Discount, .OrderID FROM INNER JOIN Products ON .ProductID = Products.ProductID where OrderID in (SELECT OrderID FROM Orders WHERE CustomerID = @CustomerID)

    Веб метод, возвращающий данные по выбранному клиенту, имеет следующий вид:

    Public DataSet GetCustOrders(string CustomerID) { SqlConnection myConn = new SqlConnection("server=localhost;database=Northwind;uid=sa;pwd=Manowar"); SqlDataAdapter myData = new SqlDataAdapter("CustOrdersOrdersDetails", myConn); myData.SelectCommand.CommandType = CommandType.StoredProcedure; myData.SelectCommand.Parameters.Add(new SqlParameter("@CustomerID", SqlDbType.Char, 5)); myData.SelectCommand.Parameters["@CustomerID"].Value = CustomerID; DataSet ds = new DataSet(); myData.Fill(ds); ds.Tables.TableName = "Orders"; ds.Tables.TableName = "OrderDetails"; ds.Relations.Add(ds.Tables.Columns["OrderID"], ds.Tables.Columns["OrderID"]); return ds; }

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

    Протестируем нашу работу с помощью страницы веб сервиса. Откройте в браузере страницы веб сервиса (у меня это http://localhost/WebServicesExample/nw.asmx), нажмите на GetCustOrders для перехода с странице веб метода, введите в поле ввода ID клиента из базы Northwind (например ALFKI) и нажмите кнопку Invoke. Откроется новая страница, содержащая в себе XML. Это и есть результат вызова нашего веб метода – необходимый нам DataSet со списком заказов клиента ALFKI. Ниже представлены фрагменты страницы с результатами.

    … 10643 1997-08-25T00:00:00.0000000+03:00 1997-09-22T00:00:00.0000000+03:00 1997-09-02T00:00:00.0000000+03:00 10692 1997-10-03T00:00:00.0000000+03:00 1997-10-31T00:00:00.0000000+02:00 1997-10-13T00:00:00.0000000+03:00 … Rössle Sauerkraut 45.6 15 0.25 10643 Chartreuse verte 18 21 0.25 10643 …

    Рассмотрим повнимательней что же нам верноул веб метод.

    Из приведенного выше кода можно сделать следующие выводы:

    • Возвращаемый результат имеет тип DataSet (это видно как из названия корневого узла, так и из строки в описании схемы).
    • Возвращаемый DataSet содержит 2 таблицы – Orders и OrderDetails.
    • В схеме доступна полная онформация о таблицах, а также запись о созданном нами отношении между таблицами.

    Конечно же, если бы нам для вызова web-метода приходилось заходить на страницу его описания, вручную задавать параметры вызова web-метода и вызывать его, толку от этого было бы мало. Как я уже говорил, web-методы можно вызывать с помощью HTTP GET, HTTP POST и SOAP запросов.

    Сейчас мы рассмотрим возможности работы с созданным web-сервисом с помощью HTTP GET (или HTTP POST) из браузера Internet Explorer (тот самый случай, когда работники филиала нашей гипотетической фирмы не имеют ничего кроме компьютера с выходом в интернет), а также SOAP вызов с помощью небольшой программы, написанной на C#.

    Как же это делается? Воспользуемся объектом Microsoft.XMLHTTP, позволяющим передавать и получать данные с помощью запросов GET или POST. Мы создаем запрос к соответствующему web-методу нашего web-сервиса, передаем (если необходимо) параметры и выводим на страницу полученный в ответ XML код, используя небольшой xsl-файл.

    Ниже представлен код orders.xsl (предназначенного для отображения полученного результата) и DhtmlClient.htm (основного файла).

    Order:
    Date:
    Ship:
    Product:Price:Quantity:Discount:
    var xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); var objStyle = new ActiveXObject("MSXML.DOMDocument"); objStyle.async = false; objStyle.load("orders.xsl"); var SOAPRequest = new ActiveXObject("MSXML.DOMDocument"); SOAPRequest.async = false; var SOAPResponse = new ActiveXObject("MSXML.DOMDocument"); SOAPResponse.async = false; function getOrders() { // xmlhttp.Open("GET", "http://localhost/WebServicesExample/nw.asmx/GetCustOrders?CustomerID=" + // document.all.client.value, false); xmlhttp.Open("POST", "http://localhost/WebServicesExample/nw.asmx/GetCustOrders", false); xmlhttp.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); xmlhttp.send("CustomerID=" + document.all.client.value); SOAPResponse.loadXML(xmlhttp.responseXML.xml); document.all.orders.innerHTML = SOAPResponse.transformNode(objStyle.documentElement); }
    Клиент

    Откройте файл DhtmlClient.html в броузере, введите код клиента в поле ввода (ALFKI подойдет просто замечательно) и нажмите кнопку «Заказы». JavaScript метод getOrders() направит запрос через HTTP POST к веб методу и затем преобразует возвращенный результат с помощью таблицы стилей orders.xsl и выведет его на страницу (если вы хотите протестировать вызов веб метода через HTTP GET – откомментируйте первую строку метода и закомментируйте три последующих). При этом у вас получится страница, похожая на вот это:


    Теперь рассмотрим вызов web-метода из.NET-приложения. Для вызова web-сервисов.NET-приложение использует прокси-класс, генерируемый либо с помощью утилиты командной строки wsdl, либо с помощью добавления Web Reference в проект Visual Studio .NET. Прокси-класс, генерируемый таким образом, содержит довольно много кода. Но для нас представляет ценность то, что в нем содержатся методы для вызова интересующих нас web-методов.

    Напишем небольшое Windows Forms приложение, вызывающее метод GetCustOrders нашего веб сервиса. В Visual Studio.NET создадим Windows приложение. Добавим в созданное приложение ссылку на web-сервис (меню Project – Add Web Reference…). В адресную строку открывшегося окна введем адрес нашего web-сервиса (http://localhost/WebServicesExample/nw.asmx):


    После нажатия на кнопку Add Reference Visual Studio.NET добавит в проект ссылку на web-сервис и автоматически сгенерирует прокси-класс для вызова web-методов.

    Тем, кто не пользуется Visual Studio.NET, создание прокси класса также не представляет особого труда. Для этого, как я уже говорил, используется утилита командной строки wsdl. Полное описание использования этой утилиты можно найти в MSDN, здесь же я только покажу вызов этой утилиты для генерации прокси класса для нашего веб сервиса.

    Wsdl /l:cs http://localhost/WebServicesExample/nw.asmx

    При этом будет создан файл nw.cs, содержащий небходимый нам прокси класс.

    Продолжим создание Windows клиента для форума. Разместим на форме поле ввода для ввода кода клиента, DataGrid для отображения полученных данных и, естесственно, кнопку «Заказы». Для того, чтобы в DataGridе отобразились интересующие нас данные, в обработчик OnClick кнопки необходимо вставить всего 2 строки:

    Private void btnOrders_Click(object sender, System.EventArgs e) { localhost.nw orders = new localhost.nw(); dgMain.DataSource = orders.GetCustOrders(txtClient.Text); }

    Запустите созданное приложение, введите ALFKI в поле ввода и нажмите кнопку «Заказы». В DataGridе отобразится полученный в результате вызова DataSet с интересующими нас данными:


    Как видите создание и использование web-сервисов в.NET не такая уж и сложная задача.

    Теперь рассмотрим строение web-сервиса, что называется, шаг за шагом.

    В первой (и единственной, если вы пишете с использованием технологии codebehind) строке asmx файла, как и в случае с aspx-файлом, расположена директива ASP.NET, указывающая на тип данного файла, язык, на котором написан код, и имя класса для файла. Например строка для написанного нами web-сервиса следующая:

    где директива WebService указывает на то, что данный файл является web-сервисом, а атрибуты Language=”c#” и Class=”WebServicesExample.nw” указывают на то, что класс web-сервиса написан на C# и полное имя класса – WebServicesExample.nw.

    Web-сервис может состоять из множества классов. Однако только один класс в web-сервисе может иметь методы, помеченные атрибутом WebMethod (которые можно вызывать через SOAP-запросы).

    Атрибут WebMethod имеет шесть свойств, влияющих на работу web-метода. Рассмотрим их:

    Description

    Данное свойство служит для общего описания web-метода. Вы можете присвоить данному свойству любую текстовую строку. Значение свойства Description выводится на странице описания web-сервиса. Возвращаясь к рассмотренному ранее примеру опишем web-метод getAuthors следующим образом:

    Public DataSet GetCustOrders(string CustomerID)

    Откомпилируем наш проект и взглянем теперь на страницу описания web-сервиса:


    Как видите, теперь под методом GetCustOrders выведено его описание.

    EnableSession

    Данное свойство позволяет включить поддержку сессий. По умолчанию поддержка сессий в web-сервисах отключена. Чтобы включить ее, определите web-метод следующим образом:

    Если при объявлении web-сервиса вы породили его от класса System.Web.Services.WebService, то вы автоматически получаете доступ к открытым свойствам Application, Context, Session, Server и User, имеющим такой же смысл, как и аналогичные свойства ASP.NET веб форм. Если же вы создавали класс web-сервиса как-то иначе – ничего страшного. Вы все равно можете получить доступ к вышеперечисленным свойствам с помощью соответствующих свойств статического HttpContext.Current.

    Рассмотрим работу с объектами Application и Session на примере. Напишем небольшой web-сервис с двумя web-методами – setSessionVar (принимает 2 строковых параметра – имя переменной сессии и ее значение, и устанавливает переменную сессии) и getSessionVar (принимает строковый параметр – имя сессии, и возвращает значение переменной сессии):

    Public void setSessionVar(string name, string val) { Session = val; } public string getSessionVar(string name) { return (string) Session; }

    Не забывайте, что ASP.NET определяет сессию по идентификатору сессии, хранящемуся в cookie. Так что приведенный выше пример будет успешно работать из страницы web-сервиса, но если попробовать вызывать методы этого web-сервиса из.NET приложения через прокси-класс, то вы с удивлением обнаружите, что вызов метода getSessionVar не возвращает никакого значения, так как использование web-сервисов с помощью проки-класса по умолчанию не использует куки для сохранения информации.

    Чтобы.NET приложение могло работать с сессией с помощью web-сервиса, необходимо добавить в код, вызывающий методы web-сервиса, работу с cookie. И это не так уж сложно, как кажется на первый взгляд:).

    Создадим Windows приложение, добавим в него Web Reference на созданный ранее web-сервис и добавим интерфейс для установки переменных сессии с помощью метода setSessionVar и получения значения переменной сессии с помощью getSessionVar. Объявим также в класс приватную переменную cookie типа CookieContainer (класс CookieContainer описан в модуле System.Net). В конструкторе класса проиницализируем эту переменную. Теперь для того, чтобы вызовы методов web-сервиса были в одной сессии, необходимо просто перед вызовом web-метода присвоить значение cookie свойству CookieContainer класса web-сервиса.

    Полностью код вызова веб методов с использованием cookie представлен ниже (прокси-класс, сгенерированный для web-сервиса, имеет название localhost1.testService1):

    Private CookieContainer cookie; public Form1() { InitializeComponent(); cookie = new CookieContainer(); } private void btnSetValue_Click(object sender, System.EventArgs e) { localhost.session session = new localhost.session(); session.CookieContainer = cookie; session.setSessionVar(txtSessionName.Text, txtSessionValue.Text); } private void btnGetValue_Click(object sender, System.EventArgs e) { localhost.session session = new localhost.session(); session.CookieContainer = cookie; txtSessionValue.Text = session.getSessionVar(txtSessionName.Text); }

    MessageName

    Свойство MessageName позволяет назначать web-методу имя, отличное от того, которое ему было назначено при написании класса web-сервиса.

    Существует возможность создавать в web-сервисе web-методы с одинаковыми именами. Но при попытке просмотра страницы такого web-сервиса будет сгенерирована ошибка. Свойство MessageName используется именно в этих случаях.

    Допустим кроме веб метода GetCustOrders с одним параметром (код клиента) у нас объявлен еще один метод GetCustOrders, принимающий кроме кода клиента также вилку дат и возвращающий DataSet с информацией о заказах, дата которых находится между указанными датами

    Public DataSet GetCustOrders(string CustomerID) { ... } public DataSet GetCustOrders(string CustomerID, DateTime startDate, DateTime endDate) { ... }

    Теперь, хотя оба web-метода и имеют одинаковое имя, все равно есть возможность использовать страницу web-сервиса. При этом в прокси классе оба метода естесственно будут определены с именем GetCustOrders.

    TransactionOption

    Web-сервисы ограниченно поддерживают транзакции. С помощью свойства TransactionOption можно управлять тем, как ваш метод использует транзакции. Это свойство может принимать следующие значения:

    Слово «ограниченно» означает, что веб сервис может порождать транзакцию, но при этом не может быть участником другой транзакции. Если вызывается веб метод с TransactionOption установленным в Required или RequiresNew, а в нем вызывается другой веб метод с такими же установками, каждый из этих методов инициирует свою транзакцию.

    По умолчанию свойство TransactionOption установлено в Required.

    При нормальном завершении работы метода транзакция считается выполненной. Чтобы прервать выполнение транзакции, сгенерируйте исключение. В веб методе не нужно вызывать методы SetComplete и SetAbort.

    CacheDuration

    Сказать, что кеширование веб сервисов просто означало бы покривить душой. Кешировать веб сервисы не просто, а очень просто. Одним словом, все, что нужно для того, чтобы активизировать кеширование веб сервиса, это использовать параметр CacheDuration атрибута WebMethod с указанием промежутка времени в секундах, на которое кешируется веб сервис.

    Ниже представлен пример обхъявления, кеширующий возвращаемое методом GetCustOrders значение на 10 минут.

    Public DataSet GetCustOrders(string CustomerID)

    BufferResponse

    Свойство BufferResponse позволяет управлять буферизацией ответа web-метода. По умолчанию результат выполнения буферизируется и отправляется клиенту только после того, как он полностью сформирован. Однако если ваш web-метод очень долго выполняется, возможно, имеет смысл отключать буферизацию результата.

    Для отключения буферизации результата используйте следующее объявление web-метода:

    Public DataSet GetCustOrders(string CustomerID)

    Параметры атрибута WebService

    Web-сервис также имеет три свойства, влияющие на его работу. Два из них, Description и Name, работают точно так же, как и подобные им свойства Description и MessageName web-метода. Третье свойство, Namespace, позволяет управлять пространством имен в SOAP-сообщении. Рассмотрим применение всех трех свойств на примере web-сервиса wsauthors.

    Изменим объявление web-сервиса на следующее:

    Public class nw: System.Web.Services.WebService

    после чего откомпилируем web-сервис и посмотрим на страницу описания:


    На страницу было добавлено описание web-сервиса, введенное в свойстве Description, а также изменилось имя web-сервиса. Обратите внимание на тот факт, что теперь обращаться к web-сервису нужно по новому имени (вам необходимо будет заново сгенерировать прокси-класс для web-сервиса, и этот прокси-класс будет иметь новое имя).

    И напоследок рассмотрим вопрос типов данных, которые может принимать в качестве параметров и возвращать веб метод.

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

    Рассмотрим веб метод GetCustomer, возвращающий структуру Customer, содержащую информацию о клиенте. Для начала определим класс Customer

    Public class Customer { private string id; private string companyName; private string address; public Customer() { } public Customer (string id, string companyName, string address) { this.id = id; this.companyName = companyName; this.address = address; } public string ID { get { return id; } set { id = value; } } public string CompanyName { get { return companyName; } set { companyName = value; } } public string Address { get { return address; } set { address = value; } } }

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

    Теперь напишем веб метод, возвращающий переменную данного типа.

    Public Customer GetCustomer(string CustomerID) { SqlConnection myConn = new SqlConnection("server=localhost;database=Northwind;uid=sa;pwd=Manowar"); SqlCommand myCmd = new SqlCommand("select CustomerID, CompanyName, Address from Customers " + "where CustomerID = "" + CustomerID + """, myConn); myConn.Open(); SqlDataReader reader = myCmd.ExecuteReader(); Customer customer; if(reader.Read()) customer = new Customer(reader.GetString(0), reader.GetString(1), reader.GetString(2)); else customer = new Customer(); reader.Close(); myConn.Close(); return customer; }

    В результате вызова этого веб метода для многострадального клиента ALFKI будет возвращен следующий XML.

    ALFKI Alfreds Futterkiste Obere Str. 57

    Еще один вопрос, связанный с возвратом данных из web-сервиса, касается вида XML-кода, возвращаемого web-методом. Как видно из предыдущго примера, web-метод возвращает данные в виде XML-элементов с названиями, соответствующими тем названиям, которые были описаны в описании класса. Вы можете изменить как названия XML-элементов, так и возвращать некоторые (или все) данные в виде XML-атрибутов.

    Изменим немного описание структуры Customer для иллюстрации сказанного. Теперь поле CustomerID, будет возвращаться как атрибут ID, а поле CompanyName поменяет свое название на Company.

    Public class Customer { ... public string ID ... public string CompanyName ... }

    Теперь в результате выполнения предыдущего web-метода через веб интерфейс будет получен следующий XML:

    Alfreds Futterkiste Obere Str. 57

    Заключение

    Веб сервисы представляют собой новый вид веб приложений для создания уровня бизнес логики и связи разнородных приложений с помощью использования общих стандартов – HTTP, XML и SOAP. Практически не существует ограничений на области применения веб сервисов: вы можете создавать веб сервисы для взаимодействия между различными приложениями или для предоставления данных вашим клиентам. Возможности использования веб сервисов теоретически ограничены только вашим воображением.

    Текст примеров данной статьи можно выкачать

    Данная статья открывает курс уроков по технологии WCF и рассчитана в первую очередь на новичков в программировании веб-сервисов.

    В данном уроке мы создадим простой WCF-сервис «калькулятор», реализующий четыре базовые арифметические операции, научимся размещать сервис на IIS и публиковать на сервере. Кроме того, расскажем о том, как подключиться к сервису из другого приложения на C# или с помощью бесплатной программы SoapUI , а также добавим функционал для вызова сервиса через веб (например, из браузера или Ajax).

    Урок рассчитан на начинающих программистов C#, решивших освоить технологию WCF. Опытным разработчикам может быть полезна информация из раздела , посвященного созданию конечных точек для Ajax.

    2. Создание проекта сервиса

    В работе используется Visual Studio 2013 Professional, версия фреймворка 4.5.1. Для заметки: за некоторыми небольшими исключениями, разницы между версиями 4.0. и 4.5.1 нет, но использование последней предпочтительней, т.к. добавляет полезную функцию генерации WSDL одним файлом, но об этом ниже.

    Итак, запускаем студию и создаем проект типа WCF Service Application в соответствующем разделе, называем его SampleService .

    После создания проекта рассмотрим структуру решения в обозревателе решений:

    1. IService1.cs содержит описание интерфейса сервиса, т.е. набор методов, которые наш сервис предоставляет.
    2. Service1.svc состоит из двух частей — реализация сервиса (Service1.svc.cs) и разметка (Markup), доступная в меню по клику правой кнопкой мыши.
    3. Web.config — конфигурация сервиса.
    4. Папка App_Data нам пока не нужна — удаляем.

    Для начала переименуем сервис и его интерфейс, придав им имена Calculator и ICalculator соответственно, а также изменим строчку в разметке сервиса (файл Calculator.svc):

    Теперь опишем сам интерфейс нашего калькулятора. Объявим стандартные арифметические операции, такие как сложение (Addition), вычитание (Subtraction), умножение (Multiplication) и деление (Division). Также добавим дополнительный метод TestConnection, возвращающий строку. С помощью этого метода клиенты смогут проверить, что сервис функционирует, т.к. стандартного «пинга» WCF не предоставляет. Все методы интерфейса должны быть помечены атрибутом , иначе они не будут видны клиентам.

    В итоге, интерфейс примет следующий вид:

    ICalculator.cs

    using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace SampleService { public interface ICalculator { #region Common Methods /// /// OK string TestConnection(); #endregion #region Arithmetic /// /// сложение /// /// Слагаемое 1 /// Слагаемое 2 /// сумма double Addition(double a, double b); /// /// вычитание /// /// Уменьшаемое /// Вычитаемое /// разность double Subtraction(double a, double b); /// /// умножение /// /// Множитель 1 /// Множитель 2 /// произведение double Multiplication(double a, double b); /// /// деление /// /// Делимое /// Делитель /// частное double Division(double a, double b); #endregion } }

    Теперь реализуем интерфейс в классе сервиса:

    Calculator.svc.cs

    using System; using System.Collections.Generic; using System.Linq; using System.Runtime.Serialization; using System.ServiceModel; using System.ServiceModel.Web; using System.Text; namespace SampleService { public class Calculator: ICalculator { #region Common Methods /// /// проверка соединения /// /// OK public string TestConnection() { return "OK"; } #endregion #region Arithmetic /// /// сложение /// /// Слагаемое 1 /// Слагаемое 2 /// сумма public double Addition(double a, double b) { return a + b; } /// /// вычитание /// /// Уменьшаемое /// Вычитаемое /// разность public double Subtraction(double a, double b) { return a - b; } /// /// умножение /// /// Множитель 1 /// Множитель 2 /// произведение public double Multiplication(double a, double b) { return a * b; } /// /// деление /// /// Делимое /// Делитель /// частное public double Division(double a, double b) { return a / b; } #endregion } }

    Логика сервиса описана, теперь необходимо его разместить на хостинге.

    Сам по себе сервис представляет библиотеку (в нашем случае файл SampleService.dll), и для ее запуска в виде сервиса необходимо воспользоваться одним из предоставляемых WCF методов:

    • 1. Хостинг на IIS.
    • 2. Запуск в виде службы Windows.
    • 3. Self hosting (сервис выполнен в виде консольного приложения, запускающего сервис).

    Принципиального различия в этих методах нет, поэтому выберем простейший вариант — размещение на IIS. Для этого откроем конфигурацию проекта и выберем вкладку «Web». Параметры по умолчанию вы можете наблюдать у себя, а вот так выглядят настройки для нашего сервиса:

    Раздел Start Action определяет, что произойдет при запуске проекта в студии:

    • Current Page (по умолчанию) – запуск последней открытой страницы в браузере. У нашего сервиса пока нет настроенных url-адресов методов, поэтому этот вариант не подходит.
    • Specific Page – запуск конкретной страницы.
    • Start External program – запуск указанного исполняемого файла, который инициирует сервис, например, вызовет метод, нуждающийся в отладке.
    • Start URL – открытие указанного в поле URL адреса в браузере.
    • Don"t open a page – ничего не открывать, но ждать запроса от стороннего приложения.

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

    Раздел Servers определяет, где будет размещен сервис. По умолчанию выбран IIS Express (строенный в студию вариант IIS), но мы будем продвинутыми и разместим сервис на нормальном IIS. Разумеется, для этого необходимо установить IIS в панели управления и включить необходимые компоненты (ASP .NET, Basic-авторизация и другие).

    Под спойлером указан один из вариантов набора компонентов для работы сервисов.

    Компоненты IIS


    После указания адреса в настройках необходимо нажать кнопку «Create Virtual Directory». Затем можно скомпилировать проект и запустить диспетчер служб IIS . Наше приложение уже должно появиться в списке приложений сайта по умолчанию (Default Web Site), а также ему должен быть назначен пул (DefaultAppPool). Рекомендуется создать отдельный пул для ваших сервисов, указав в качестве платформы.NET 4.0:

    После создания пула, назначьте его вашему приложению (Default Web Site > Lesson1 > Дополнительные параметры > Пул приложений).

    Последний штрих — проверка вашего приложения. Для этого откройте в браузере строку, указанную в конфигурации проекта, добавив в конце имя сервиса, в нашем случае это Calculator.svc: http://localhost/Lesson1/Calculator.svc . Итог должен быть примерно следующим:

    Внимание! Если в качестве платформы вы выбрали.NET 4.0 и при этом на машине, где размещается сервис установлен.NET 4.0, ссылки на WSDL одним файлом (http://localhost/Lesson1/Calculator.svc?singleWsdl) не будет. Поэтому рекомендуется установить на сервер фреймворк версии не ниже 4.5, даже если сам сервис скомпилирован на.NET 4.0. Это достаточно забавная особенность, но за эту ссылку отвечает именно фреймворк на сервере, а не тот, для которого компилировался сервис. Это, пожалуй, единственное существенное отличие.NET 4.0 от.NET 4.5 с точки зрения интерфейса сервиса.

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

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

    Мастер предложит нам несколько вариантов публикации, выберем Custom, профиль назовем Demo.
    Затем выберем метод публикации File System и укажем путь, куда будет сохраняться наша сборка:

    Проект готов к публикации, но желательно еще перейти к следующей вкладке (Settings) и отметить галочку «Delete all existing files prior to publish» (удалять все файлы перед публикацией) и выбрать конфигурацию.

    Выполняем публикацию и смотрим, что получилось в итоге:

    Чтобы разместить сервис на вашем сайте, скопируйте папку на сервер и подключите к IIS как приложение, как если бы это был обычный сайт (Default Web Site > Add Application...)

    Готово! Сервис размещен по адресу http://dszss.proitr.ru/WCFLesson1/Calculator.svc .

    В данном разделе мы рассмотрим три способа использования нашего сервиса: вызов через приложение на C#, обращение через SoapUI и запрос посредством URL (WebInvoke).

    Для начала рассмотрим обращение к нашему сервису через клиент на C#. Так как WCF интегрирован в Visual Studio, создать клиент не составит труда. Все, что нам надо знать — это адрес WSDL сервиса, в которому мы хотим подключиться.

    Создадим в нашем решении еще один проект типа Console Application и назовем его TestClient. Назначим его исполняемым и добавим ссылку на сервис:

    В открывшемся окне введем адрес локальный сервиса, по желанию укажем имя пространства имен и дополнительные настройки. Здесь же можно посмотреть, какие методы предоставляет наш сервис:

    После создания ссылки на сервис, в проекте появится папка «Service References», в которой будет находиться сгенерированный сутидей клиент. Его код при желании вы можете посмотреть самостоятельно, надо только включить отображение скрытых файлов или перейти по F12 внутрь кода клиента.

    Теперь напишем код для подключения к сервису и вызов арифметических операций, предоставляемых им:

    Тестовый клиент

    Program.cs

    using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using System.Threading.Tasks; // ссылка на пространство имен сервиса using TestClient.RemoteService; namespace TestClient { class Program { static void Main(string args) { // помощник вывода в консоль ConsoleWriter writer = new ConsoleWriter(); // создадим клиент сервиса CalculatorClient client = new CalculatorClient("BasicHttpBinding_ICalculator"); try { // проверка соединения writer.Write("Проверка соединения с сервисом... "); if (!string.Equals(client.TestConnection(), "OK", StringComparison.InvariantCultureIgnoreCase)) { throw new Exception("Проверка соединения не удалась"); } writer.WriteLineSuccess(); writer.WriteLine(); // лямбда-функция проверки метода var CheckArithmeticOperation = new Action ((operation, operationName, arg1, arg2, expectedResult) => { writer.Write("Проверка операции ""); writer.Write(ConsoleColor.White, operation.Method.Name); writer.Write("", {0} {1} {2} = ", arg1.ToString(CultureInfo.InvariantCulture), operationName, arg2.ToString(CultureInfo.InvariantCulture)); double result = operation(arg1, arg2); if (result == expectedResult) { // проверка пройдена writer.Write("{0} ", result.ToString(CultureInfo.InvariantCulture)); writer.WriteLineSuccess(); } else { // ошибка throw new Exception(string.Format("Ошибка проверки метода "{0}": {1} {2} {3} != {4}", operation.Method.Name, arg1.ToString(CultureInfo.InvariantCulture), operationName, arg2.ToString(CultureInfo.InvariantCulture), expectedResult.ToString(CultureInfo.InvariantCulture))); } }); // проверка метода Addition CheckArithmeticOperation(client.Addition, "+", 2.5, 5, 2.5 + 5); // проверка метода Subtraction CheckArithmeticOperation(client.Subtraction, "-", 2.5, 5, 2.5 - 5); // проверка метода Multiplication CheckArithmeticOperation(client.Multiplication, "*", 2.5, 5, 2.5 * 5); // проверка метода Division CheckArithmeticOperation(client.Division, "/", 2.5, 5, 2.5 / 5); // в конце работы закрываем клиент client.Close(); } catch (Exception ex) { // в случае ошибки необходимо принудительно закрыть клиент методом Abort() client.Abort(); // выводим информацию об ошибке writer.WriteLine(); writer.WriteLineError("Ошибка: {0}", ex.Message); } Console.WriteLine(); Console.WriteLine("Нажмите любую клавишу для продолжения..."); Console.ReadKey(); } } }


    ConsoleWriter.cs

    using System; namespace TestClient { /// /// консольный писатель /// public class ConsoleWriter { #region Declarations private ConsoleColor _successColor; // цвет сообщений для успешных операций private ConsoleColor _errorColor; // цвет сообщений об ошибках private ConsoleColor _warningColor; // цвет сообщений-предупреждений private string _successText; // текст сообщений для успешных операций private string _errorText; // текст сообщений об ошибках private string _warningText; // текст сообщений-предупреждений #endregion #region Properties /// /// цвет сообщений для успешных операций /// public ConsoleColor SuccessColor { get { return _successColor; } set { _successColor = value; } } /// /// цвет сообщений об ошибках /// public ConsoleColor ErrorColor { get { return _errorColor; } set { _errorColor = value; } } /// /// цвет сообщений-предупреждений /// public ConsoleColor WarningColor { get { return _warningColor; } set { _warningColor = value; } } /// /// текст сообщений для успешных операций /// public string SuccessText { get { return _successText; } set { _successText = value; } } /// /// текст сообщений об ошибках /// public string ErrorText { get { return _errorText; } set { _errorText = value; } } /// /// текст сообщений-предупреждений /// public string WarningText { get { return _warningText; } set { _warningText = value; } } /// /// цвет текста /// public ConsoleColor ForegroundColor { get { return Console.ForegroundColor; } set { Console.ForegroundColor = value; } } /// /// цвет фона /// public ConsoleColor BackgroundColor { get { return Console.BackgroundColor; } set { Console.BackgroundColor = value; } } #endregion #region Constructors /// /// конструктор /// public ConsoleWriter() { _successColor = ConsoleColor.Green; _errorColor = ConsoleColor.Red; _warningColor = ConsoleColor.Blue; _successText = "OK"; _errorText = "ERROR"; _warningText = "WARNING"; } #endregion #region Private methods #endregion #region Protected methods #endregion #region Public methods #region Write | WriteLine /// /// Сообщение public void Write(string value) { Console.Write(value); } /// /// записать сообщение в консоль /// /// Цвет сообщения /// Сообщение public void Write(ConsoleColor color, string value) { ConsoleColor oldColor = Console.ForegroundColor; Console.ForegroundColor = color; Console.Write(value); Console.ForegroundColor = oldColor; } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void Write(string format, params object args) { Console.Write(string.Format(format, args)); } /// /// записать сообщение в консоль /// /// Цвет сообщения /// Строка формата /// Аргументы public void Write(ConsoleColor color, string format, params object args) { ConsoleColor oldColor = Console.ForegroundColor; Console.ForegroundColor = color; Console.Write(string.Format(format, args)); Console.ForegroundColor = oldColor; } /// /// записать перевод строки в консоль /// public void WriteLine() { Console.WriteLine(); } /// /// записать сообщение в консоль /// /// Сообщение public void WriteLine(string value) { Console.WriteLine(value); } /// /// записать сообщение в консоль /// /// Цвет сообщения /// Сообщение public void WriteLine(ConsoleColor color, string value) { ConsoleColor oldColor = Console.ForegroundColor; Console.ForegroundColor = color; Console.WriteLine(value); Console.ForegroundColor = oldColor; } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void WriteLine(string format, params object args) { Console.WriteLine(string.Format(format, args)); } /// /// записать сообщение в консоль /// /// Цвет сообщения /// Строка формата /// Аргументы public void WriteLine(ConsoleColor color, string format, params object args) { ConsoleColor oldColor = Console.ForegroundColor; Console.ForegroundColor = color; Console.WriteLine(string.Format(format, args)); Console.ForegroundColor = oldColor; } #endregion #region WriteSuccess | WriteLineSuccess /// /// записать сообщение в консоль /// public void WriteSuccess() { Write(_successColor, _successText); } /// /// записать сообщение в консоль /// /// Сообщение public void WriteSuccess(string value) { Write(_successColor, value); } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void WriteSuccess(string format, params object args) { Write(_successColor, string.Format(format, args)); } /// /// записать сообщение в консоль /// public void WriteLineSuccess() { WriteLine(_successColor, _successText); } /// /// записать сообщение в консоль /// /// Сообщение public void WriteLineSuccess(string value) { WriteLine(_successColor, value); } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void WriteLineSuccess(string format, params object args) { WriteLine(_successColor, string.Format(format, args)); } #endregion #region WriteError | WriteLineError /// /// записать сообщение в консоль /// public void WriteError() { Write(_errorColor, _errorText); } /// /// записать сообщение в консоль /// /// Сообщение public void WriteError(string value) { Write(_errorColor, value); } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void WriteError(string format, params object args) { Write(_errorColor, string.Format(format, args)); } /// /// записать сообщение в консоль /// public void WriteLineError() { WriteLine(_errorColor, _errorText); } /// /// записать сообщение в консоль /// /// Сообщение public void WriteLineError(string value) { WriteLine(_errorColor, value); } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void WriteLineError(string format, params object args) { WriteLine(_errorColor, string.Format(format, args)); } #endregion #region WriteWarning | WriteLineWarning /// /// записать сообщение в консоль /// public void WriteWarning() { Write(_warningColor, _warningText); } /// /// записать сообщение в консоль /// /// Сообщение public void WriteWarning(string value) { Write(_warningColor, value); } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void WriteWarning(string format, params object args) { Write(_warningColor, string.Format(format, args)); } /// /// записать сообщение в консоль /// public void WriteLineWarning() { WriteLine(_warningColor, _warningText); } /// /// записать сообщение в консоль /// /// Сообщение public void WriteLineWarning(string value) { WriteLine(_warningColor, value); } /// /// записать сообщение в консоль /// /// Строка формата /// Аргументы public void WriteLineWarning(string format, params object args) { WriteLine(_warningColor, string.Format(format, args)); } #endregion #endregion } }


    Результат исполнения:

    В целом, вызов методов сервиса ничем не отличается от вызова методов обычного интерфейса C#, а т.к. клиент и сервис находятся в пределах одного решения, вы можете поставить контрольные точки в методах сервиса, которые сработают при их вызове. Также вы можете запустить отдельный проект сервиса в режиме ожидания запроса от стороннего приложения ().

    Технически, при обращении к сервису клиент формирует Soap сообщение (обычная XML), которое предается по HTTP-каналу и может быть перехвачено сниффером. Разумеется, в арсенале WCF существуют и другие методы передачи, а также возможность шифрования и/или подписи сообщений, но это уже тема другого урока.

    Переходим к другому способу вызова сервиса.

    Эти ограничения не препятствуют использованию функции обратного вызова ASP.NET AJAX по ее прямому назначению - в качестве механизма выполнения страницей задач серверного приложения. Если ранее вам приходилось использовать веб-службы для представления серверной функциональности "толстым" клиентам, сторонним разработчикам и приложениям на платформах, отличных от.NET, вы увидите, что применение веб-служб в ASP.NET AJAX несколько проще.

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

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

    Веб-службы, используемые с AJAX ASP.NET, состоят из двух частей: файла.asmx, который действует в качестве конечной точки веб-службы, и файла.cs, который содержит фактический код C#. Эти файлы необходимо добавить к веб-сайту, содержащему страницу AJAX ASP.NET, которая будет использовать веб-службу.

    Самый быстрый способ создания веб-службы в среде Visual Studio - выбрать пункт меню Website --> Add New Item, указать шаблон Web Service, назначить имя файлу (в следующем примере - TerritoriesService) и щелкнуть на кнопке Add (Добавить). При создании веб-сайта без проекта файл.asmx будет помещен в каталог веб-приложения, а соответствующий файл.cs - в папку App_Code для автоматической компиляции.

    Чтобы веб-службы можно было использовать с ASP.NET AJAX, веб-приложение не обязательно размещать в виртуальном каталоге IIS. Вместо этого для тестирования приложения можно применять встроенный веб-сервер Visual Studio. Это возможно потому, что код сценария, который автоматически вызывает веб-службу, использует относительный путь. В результате, независимо от порта, выбираемого веб-сервером Visual Studio, веб-страница сможет сформировать правильный URL-адрес.

    Файл.asmx не представляет особого интереса - если его открыть, в нем обнаружится единственная строка с директивой WebService, которая определяет язык кода, расположение файла отделенного кода и имя класса:

    В этом примере создается веб-служба TerritoriesService.asmx с файлом отделенного кода TerritoriesService.cs. В файле отделенного кода определен класс TerritoriesService, который выглядит следующим образом:

    Public class TerritoriesService: System.Web.Services.WebService { // ... }

    По умолчанию атрибут ScriptService закомментирован. Чтобы создать веб-службу, которую можно будет вызывать из страницы ASP.NET AJAX, не забудьте удалить символы комментария.

    Этот класс является производным от System.Web.Services.WebService , который служит традиционным базовым классом для веб-служб. Однако этот подход выбран лишь для удобства и не является обязательным. Наследование от WebService предоставляет доступ к ряду встроенных объектов (таких как Application, Server, Session и User) без необходимости обращения к статическому свойству HttpContext.Current.

    Обратите также внимание, что объявление класса веб-службы содержит три атрибута. Два первых - WebService (устанавливает пространство имен XML, используемое в сообщениях веб-службы) и WebServiceBinding (указывает уровень соответствия стандартам, поддерживаемый веб-службой) - применяются только при вызове веб-службы с помощью сообщений SOAP и не имеют значения в страницах ASP.NET AJAX. Однако третий атрибут - ScriptService - значительно важнее. Он конфигурирует веб-службу, разрешая JSON-вызовы из клиентов JavaScript. Без этого веб-службу нельзя было бы применять в странице ASP.NET AJAX.

    Создание веб-метода

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

    Public class TerritoriesService: System.Web.Services.WebService { public string HelloWorld() { return "Hello World"; } }

    He обязательно делать метод общедоступным (как в этом примере), но обычно это делают согласно принятым соглашениям. На веб-методы накладываются определенные ограничения. Типы данных, используемые для значений параметров и возвращаемых значений, должны быть из числа тех, что описаны в таблице ниже:

    Допустимые типы данных параметров и возвращаемых значений веб-службы Тип данных Описание
    Базовые типы

    Базовые типы данных C#, такие как целые числа (short, int, long), целые без знака (ushort, uint, ulong), нецелочисленные числовые типы (float, double, decimal) и ряд других смешанных типов (bool, string, char, byte, DateTime)

    Перечисления

    Типы перечислений (определенные в C# с помощью ключевого слова enum) поддерживаются. Однако, веб-служба использует строковое имя значения перечисления (а не лежащее в основе целое число)

    Специальные объекты

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

    Массивы и коллекции

    Можно использовать массивы любого поддерживаемого типа. Допускается также ArrayList (который просто преобразуется в массив), но более специализированные коллекции, такие как Hashtable, не разрешены. Можно применять базовые коллекции. Во всех этих случаях объекты в коллекции должны быть также сериализуемыми

    XmlNode

    Объекты, основанные на System.Xml.XmlNode, являются представлениями части XML-документа. Их можно применять для отправки произвольного XML-текста

    DataSet и DataTable

    DataSet и DataTable можно использовать для возврата информации из реляционной базы данных. Другие объекты данных ADO.NET, такие как DataColumns и DataRows, не поддерживаются. Применяемый объект DataSet или DataTable автоматически преобразуется в XML-фрагмент, подобно тому, как это происходит при использовании методов GetXml() или WriteXml()

    Состояние сеанса в веб-службе

    Атрибут WebMethod принимает ряд параметров, большинство из которых несет определенную нагрузку в странице ASP.NET. Одним исключением является свойство EnableSession , которое по умолчанию имеет значение false, в результате чего состояние сеанса визуализации веб-службе недоступно. Это значение по умолчанию имеет смысл в традиционной веб-службе, не использующей ASP.NET AJAX, поскольку какая-либо информация о сеансе может не существовать, а клиент может вообще не поддерживать cookie-набор сеанса. Но в случае веб-службы ASP.NET AJAX вызовы веб-службы всегда осуществляются из контекста веб-страницы ASP.NET, которая выполняется в контексте текущего пользователя веб-приложения, и у этого пользователя имеются действующий сеанс и cookie-набор сеанса, автоматически передаваемые вместе с вызовом веб-службы.

    Ниже приведен пример предоставления веб-методу доступа к объекту Session:

    Public void DoSomething() { if (Session["myObject"] != null) { // (Использовать объект в состоянии сеанса.) } else { // (Создать новый объект и сохранить его в состоянии сеанса.) } }

    Для примера с раскрывающимся списком веб-служба должна обеспечить способ получения регионов, расположенных на данной территории. Ниже приведен код веб-службы, содержащей веб-метод GetTerritoriesInRegion(), который извлекает регионы:

    Using System; using System.Collections.Generic; using System.Web; using System.Web.Services; using System.Data; using System.Data.SqlClient; using System.Web.Configuration; public class TerritoriesService: System.Web.Services.WebService { public List GetTerritoriesInRegion(int regionID) { SqlConnection con = new SqlConnection(WebConfigurationManager.ConnectionStrings["Northwind"].ConnectionString); SqlCommand cmd = new SqlCommand("SELECT * FROM Territories WHERE RegionID=@RegionID", con); cmd.Parameters.Add(new SqlParameter("@RegionID", SqlDbType.Int, 4)); cmd.Parameters["@RegionID"].Value = regionID; List territories = new List(); try { con.Open(); SqlDataReader reader = cmd.ExecuteReader(); while (reader.Read()) { territories.Add(new Territory(reader["TerritoryID"].ToString(), reader["TerritoryDescription"].ToString())); } reader.Close(); } catch { // Маскировать ошибки throw new ApplicationException("Ошибка данных"); } finally { con.Close(); } return territories; } }

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

    Класс Territory охватывает две части строковой информации. В нем определены общедоступные переменные-члены, а не свойства, поскольку он призван играть роль исключительно контейнера данных, который транспортирует информацию по сети:

    Public class Territory { public string ID; public string Description; public Territory(string id, string description) { this.ID = id; this.Description = description; } public Territory() { } }

    Это определение класса можно поместить в тот же файл кода, что и веб-служба, или в отдельный файл внутри каталога App_Code.

    Вызов веб-службы

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

    С помощью элементов ServiceReference этого раздела перечисляются все используемые страницей службы и их расположения. Добавление ссылки на ранее приведенный файл TerritoriesService .asmx выполняется следующим образом:

    При визуализации страницы на сервере ScriptManager будет генерировать прокси-объект JavaScript. В клиентском коде этот прокси-объект JavaScript можно применять для выполнения вызовов. Ниже приведен код двух списков, помещенных в веб-форму:

    Первый список заполняется посредством обыкновенной привязки данных ASP.NET с помощью элемента управления источником данных SqlDataSource. Больший интерес представляет то, что он использует атрибут onchange для привязки к клиентскому обработчику события. В результате, когда пользователь выбирает новую территорию, JavaScript-функция GetTerritories() запускается, и текущее значение списка передается в качестве аргумента.

    Формально весь код функции GetTerritories() можно было бы поместить непосредственно в атрибут события onchange, тем самым уменьшив количество создаваемых функций JavaScript. Однако отделение кода, который вызывает веб-службу, улучшает читабельность кода и облегчает его сопровождение.

    Код JavaScript-функции GetTerritories() имеет следующий вид:

    function GetTerritories(regionID) { TerritoriesService.GetTerritoriesInRegion(regionID, OnRequestComplete, OnError); }

    Если вам ранее приходилось программировать с применением веб-служб ASP.NET, вы заметите, что синтаксис клиентского кода вызова веб-службы ASP.NET AJAX отличается от синтаксиса.NET. В приложении.NET сначала должен быть создан прокси-объект, а затем вызвана веб-служба на этом объекте. В странице ASP.NET AJAX используется готовый прокси-объект с тем же именем, что и у класса веб-службы.

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

    Чтобы завершить пример, нужно предоставить клиентскую функцию, которая обрабатывает ответ. В данном примере это функция OnRequestComplete(). Она принимает возвращаемое значение в единственном параметре, а затем добавляет информацию во второй раскрывающийся список на веб-странице:

    Function OnRequestComplete(result) { var lstTerritories = document.getElementById("lstTerritories"); lstTerritories.innerHTML = ""; for (var n = 0; n

    Примечательной особенностью этого кода является то, что он в состоянии работать с результатом, возвращенным из веб-метода, без выполнения каких-либо дополнительных действий по десериализации. Еще больше впечатляет то, что веб-метод возвращает обобщенный список объектов Territory, который, очевидно, не имеет никакого эквивалента в коде JavaScript. Вместо этого ASP.NET AJAX создает определение для объекта Territory и возвращает полный список в массиве. Это позволяет коду JavaScript просматривать в цикле массив и проверять свойства ID и Description каждого элемента.

    В данном случае можно воспользоваться одним небольшим ухищрением. Вместо метода document.getElementById() допускается применять псевдоним $get из ASP.NET AJAX, который выполняет ту же функцию и имеет следующий вид:

    Var lstTerritories = $get("lstTerritories");

    Этот прием - обычное соглашение, применяемое в страницах ASP.NET AJAX.

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

    В качестве последнего штриха можно задать лимит времени и функции обработки ошибок, например:

    Function OnError(result) { var lbl = document.getElementById("lblInfo"); lbl.innerHTML = "" + result.get_message() + "
    "; lbl.innerHTML += result.get_stackTrace(); }

    Функция OnError() получает объект ошибки, содержащий метод get_message() , который извлекает текст ошибки, и метод get_stackTrace() , который возвращает подробный стек вызовов с указанием места возникновения ошибки. На рисунке ниже показано, что происходит, когда веб-методу не удается подключиться к базе данных, и он генерирует стандартное исключение ApplicationException:

    В этом примере продемонстрирована версия ASP.NET AJAX модели клиентского обратного вызова. Хотя она использует те же внутренние механизмы, что и функция клиентского обратного вызова ASP.NET, версия ASP.NET AJAX обеспечивает более прочный фундамент построенный на основе веб-служб. Тем не менее, оба подхода обладают одной общей чертой - независимо от применяемого подхода придется писать собственный код JavaScript для обновления страницы.

    Помещение веб-метода в страницу

    В большинстве случаев для обработки обратных вызовов ASP.NET AJAX целесообразно создавать отдельную веб-службу. Обычно этот подход ведет к созданию более четких страниц и облегчает отладку и совершенствование кода. Тем не менее, в некоторых ситуациях могут существовать один или более веб-методов, которые явно предназначены для использования на единственной странице и действительно не будут применяться в других частях приложения. В этом случае можно создать выделенную веб-службу для каждой страницы или же переместить код веб-службы в страницу.

    Помещение кода веб-метода в страницу не представляет сложности - фактически, для этого достаточно простого перетаскивания. Для начала скопируйте свой веб-метод (дополненный атрибутом WebMethod) в класс отделенного кода страницы. Затем измените его на статический метод и добавьте атрибут System.Web.Script.Services.ScriptMethod . Ниже приведен пример помещения веб-метода (GetTerritoriesInRegion) в веб-страницу:

    Public partial class _Default: System.Web.UI.Page { public static List GetTerritoriesInRegion(int regionID) { // Передать работу классу веб-службы TerritoriesService service = new TerritoriesService(); return service.GetTerritoriesInRegion(regionID); } }

    Установите свойство ScriptManager.EnablePageMethods в true и удалите ссылку в разделе кода ScriptManager (при условии, что не собираетесь использовать какие-либо веб-службы, не встроенные в страницу):

    И, наконец, измените код JavaScript так, чтобы он вызывал метод посредством объекта PageMethods, как показано в следующем примере:

    Function GetTerritories(regionID) { PageMethods.GetTerritoriesInRegion(regionID, OnRequestComplete, OnError); }

    Объект PageMethods представляет все веб-методы, добавленные в текущую веб-страницу.

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

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

    С точки зрения безопасности приложения не имеет никакого значения, размещаются веб-методы в странице или в выделенной веб-службе. Размещение веб-метода в странице может скрыть его от случайных пользователей, но настоящий взломщик начнет с просмотра HTML-кода страницы, который включает в себя ссылку на прокси-объект JavaScript. Злоумышленники могут легко использовать прокси-объект JavaScript для выполнения подложных вызовов веб-метода. Для защиты от подобных угроз веб-методы должны всегда реализовывать те же самые меры безопасности, которые применяются в веб-страницах. Например, любой принимаемый ввод должен быть проверен на достоверность, код должен отказываться возвращать уязвимую информацию не аутентифицированным пользователям, а при доступе к базе данных должны применяться параметризованные команды для предотвращения атак внедрением SQL.