10. Додаток [SimuPaie] — версія 6 — клієнт ASP.NET веб-сервісу
10.1. Архітектура додатка
Ми вдосконалюємо архітектуру «клієнт/сервер» тесту NUnit наступним чином:
![]() |
У [1] тест NUnit замінено на веб-додаток версії 8. Слід пам’ятати, що ця архітектура включає два веб-сервери, які не зображено:
- веб-сервер, на якому виконується веб-служба [S]. Вона працюватиме в першому екземплярі Visual Web Developer.
- веб-сервер, на якому виконується веб-клієнт [1]. Він працюватиме у другому екземплярі Visual Web Developer.
10.2. Проєкт Visual Web Developer для клієнта [web]
Ми створюємо новий веб-проект ASP.NET:
![]() |
- у [1] ми обираємо веб-проект на C#
- у [2] вибираємо «Веб-додаток ASP.NET»
- у [3] ми даємо назву веб-проекту
- у [4] ми вказуємо місце розташування цього проєкту
- у [5] — створений проект
Ми змінюємо деякі властивості проекту:
![]() |
- у [1] — ім’я збірки, що буде згенеровано
- в [2] — простір імен за замовчуванням для класів та інтерфейсів, які будуть створені
Щоб зібрати проект [pam-v6-client-webservice], можна взяти файли [Global.asax] та [Default.aspx] з веб-проекту [pam-v4-3tier-nhibernate-multivues-monopage] і скопіювати їх у проект [pam-v6-client-webservice]. Спочатку це робимо за допомогою провідника Windows:
![]() |
- у [1], папку проекту [pam-v4-3tier-nhibernate-multivues-monopage]
- у [2], папку проекту [pam-v6-client-webservice] після перенесення
- папок [images, pam, ressources]
- файлів [Global.asax, Global.asax.cs]
- файлів [Default.aspx, Default.aspx.cs, Default.aspx.designer.cs]
- у [3], у Visual Studio Express відображаємо всі файли проекту, щоб побачити нещодавно додані файли
- у [4], додаємо до проєкту додані папки та файли
![]() |
- у [5] — новий проект [pam-v6-client-webservice]
На цьому етапі можна вперше згенерувати проект [6]. З’являються такі помилки:
Щоб зрозуміти ці помилки та виправити їх, потрібно повернутися до архітектури веб-проєкту, що створюється:
![]() |
Проєкт [pam-v6-client-webservice] є рівнем [web] [1] на схемі вище. Бачимо, що цей рівень взаємодіє з клієнтом [C] веб-сервісу — клієнтом, якого ми незабаром створимо.
- Помилки 2, 5, 7 пов’язані з тим, що код [pam-v4] посилався на DLL з шару [metier], DLL, який тепер знаходиться на стороні сервера, а не на стороні клієнта.
- Помилки 1 та 3 мають ту саму причину, але цього разу це стосується DLL з шару [dao]
- помилка 4 пов’язана з тим, що код [Global.asax] використовує Spring, а наш проєкт не містить посилання на DLL з Spring. Ми додамо це посилання.
- Помилка 6 пов’язана з тим, що клас [Employe] визначений у шарі [dao], який не існує на стороні клієнта.
Помилки простору імен 1, 2, 4, 5, 6, 7 будуть усунені шляхом генерації клієнта C для веб-сервісу. Помилка 3 усувається шляхом додавання до проєкту посилання на DLL з Spring. Ми можемо вчинити наступним чином:
![]() |
- у [1] додаємо посилання на проект [pam-v6-client-webservice]
- у [4] додано посилання на DLL та [Spring.Core] із папки [lib].
Після додавання цього посилання у Spring під час генерації проекту з’являється на одну помилку менше. Усі інші помилки пов’язані з рядками коду, які посилаються на об’єкти шарів [metier] та [dao], які тепер знаходяться на сервері. Ми побачимо, що ці відсутні об’єкти будуть згенеровані в клієнті [C] веб-сервісу.
![]() |
Перш ніж згенерувати клієнт [C] веб-сервісу, ми змінимо простір імен різних наявних класів. Наразі вони знаходяться в просторі імен [pam-v4]. Ми змінюємо цей простір імен на [pam-v6]. Це стосується таких файлів: Default.aspx.cs, Default.aspx.designer.cs, Global.asax.cs. Наприклад:
....
namespace pam_v6
{
public class Global : System.Web.HttpApplication
{
// --- статичні дані додатка ---
public static Employe[] Employes;
public static IPamMetier PamMetier = null;
....
У рядку 2 простір імен pam_v4 було замінено на простір імен pam_v6.
Крім того, необхідно змінити розмітку файлів: Default.aspx та Global.asax:
![]() |
Розмітка файлу [Default.aspx] стає такою:
<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Default.aspx.cs" Inherits="pam_v6.PagePam" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
.....
У рядку 1 атрибут Inherits вказує на ім’я класу файлу [Default.aspx.cs]. Змінюємо простір імен.
Розмітка [Global.asax] стає такою:
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v6.Global" Language="C#" %>
У наведеному вище прикладі змінюється простір імен атрибута Inherits.
Тепер ми генеруємо клієнт [C] для веб-сервісу.
![]() |
Для виконання наступних операцій необхідно запустити веб-сервіс [S] [pam-v5-webservice] в іншому екземплярі Visual Web Developer.
![]() |
- У [1] додайте до проєкту [pam-v6-client-webservice] посилання на веб-сервіс [pam-v5-webservice]
- у [2] — URI веб-сервісу [pam-v5-webservice]. Це URL файлу WSDL цього веб-сервісу. У описі веб-сервісу [pam-v5-webservice] зазначено, як дізнатися цю URL-адресу.
- У [3] помічник отримує запит на обробку файлу WSDL, вказаного в [2]
- у [4] — веб-сервіс [Service1Soap], який було виявлено, а в [5] — віддалені методи, які він надає.
- у [6] задається простір імен, у якому будуть згенеровані класи та інтерфейси клієнта [C]
- запускається генерація клієнта [C]
![]() |
- у [1] — згенерований клієнт. Двічі клацніть на ньому, щоб отримати доступ до його вмісту.
- У [2], в оглядачі об’єктів, відображаються класи та інтерфейси простору імен pam_v6.WsPam. Це простір імен згенерованого клієнта.
- у [3] — клас, що реалізує клієнт веб-сервісу.
- У [4] — методи, реалізовані клієнтом [Service1SoapClient]. Тут містяться два методи віддаленого веб-сервісу: [5] та [6].
- У файлі [2] містяться зображення об’єктів шарів:
- [metier]: FeuilleSalaire, ElementsSalaire
- [dao]: Employe, Cotisations, Indemnites
Далі слід пам’ятати, що ці зображення віддалених об’єктів знаходяться на стороні клієнта та у просторі імен pam_v6.WsPam.
Розглянемо методи та властивості, що надаються одним із них:
![]() |
- у [1] вибирається локальний клас [Employe]
- у [2] містяться властивості віддаленої сутності [Employe], а також приватні поля, що використовуються для власних потреб локальної сутності.
Розглянемо код [Global.asax.cs], який містить помилки:
![]() |
- у рядках 3 і 4 використовуються простори імен, які існують на сервері, але відсутні на стороні клієнта.
- у рядку 13 використовується клас [Employe], для якого не було заявлено правильний простір імен
- у рядку 14 використовується інтерфейс IPamMetier, невідомий на стороні клієнта.
Ми вже стикалися з подібними проблемами у клієнті на C#, який розглядали раніше.
В архітектурі:
![]() |
- локальний рівень [metier] реалізовано клієнтом [C] типу pam_v6.WsPam.Service1SoapClient, який не реалізує інтерфейс IPamMetier, хоча й має методи з такими самими іменами.
- об’єкти, з якими здійснюється обробка (Employe, Indemnites, Cotisations), якими керує згенерований клієнт [C], знаходяться в просторі імен pam_v6.WsPam
Код [Global.asax.cs] змінюється наступним чином:
using System;
using System.Collections.Generic;
using Pam.Web;
using Spring.Context.Support;
using pam_v6.WsPam;
namespace pam_v6
{
public class Global : System.Web.HttpApplication
{
// --- статичні дані додатка ---
public static Employe[] Employes;
public static Service1SoapClient PamMetier = null;
public static string Msg;
public static bool Erreur = false;
// запуск додатка
public void Application_Start(object sender, EventArgs e)
{
// обробка файлу конфігурації
try
{
// інстанціювання шару [metier]
PamMetier = ContextRegistry.GetContext().GetObject("pammetier") as Service1SoapClient;
// спрощений список співробітників
Employes = PamMetier.GetAllIdentitesEmployes();
// операція виконана успішно
Msg = "Base chargée...";
}
catch (Exception ex)
{
// фіксуємо помилку
Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
Erreur = true;
}
}
public void Session_Start(object sender, EventArgs e)
{
// до сесії додано порожній список симуляцій
List<Simulation> simulations = new List<Simulation>();
Session["simulations"] = simulations;
}
}
}
У рядку 24 за допомогою Spring створюється екземпляр шару [C]. Необхідна для цього конфігурація налаштовується у [web.config]:
<configSections>
<sectionGroup name="system.web.extensions" type="System.Web.Configuration.SystemWebExtensionsSectionGroup, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35">
...
</sectionGroup>
<sectionGroup name="spring">
<section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
<section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
</sectionGroup>
</configSections>
<spring>
<context>
<resource uri="config://spring/objects" />
</context>
<objects xmlns="http://www.springframework.net">
<object id="pammetier" type="pam_v6.WsPam.Service1SoapClient,pam-v6-client-webservice"/>
</objects>
</spring>
У рядку 16 шар [metier] є екземпляром класу [pam_v6.WsPam.Service1SoapClient], який можна знайти в DLL [pam-v6-client-webservice]. Нагадаємо, що ми налаштували проєкт [pam-v6-client-webservice] так, щоб він генерував цей DLL.
У файлі [Default.aspx.cs] все ще залишається кілька помилок:
![]() | ![]() |
- рядки 5, 6: ці простори імен більше не існують. Елементи тепер знаходяться у просторі імен pam_v6 або pam_v6.WsPam.
- рядок 98: згенерований клієнт [C] не успадкував клас [PamException] від шару [dao]. Він не міг цього зробити, оскільки веб-сервіс не надає цього винятку. Вирішено замінити PamException на його батьківський клас Exception.
Код стає таким:
...
using System.Web.UI.WebControls;
using pam_v6.WsPam;
namespace pam_v6
{
public partial class PagePam : Page
{
...
...
try
{
feuillesalaire = Global.PamMetier.GetSalaire(DropDownListEmployes.SelectedValue, HeuresTravaillées, JoursTravaillés);
}
catch (Exception ex)
{
...
Виправивши ці помилки, ми можемо запустити веб-додаток:
![]() |
- в [1], URL веб-клієнта віддаленого веб-сервісу
- у [2], комбінований список співробітників було заповнено. Дані, що містяться в ньому, надходять із веб-сервісу.
Запрошуємо читачів протестувати цю версію 6, яка є копією версії 4.

















