Skip to content

4. Додаток [SimuPaie] — версія 1 — ASP.NET

4.1. Introduction

Ми хочемо написати додаток .NET, який дозволить користувачеві проводити симуляції розрахунку заробітної плати вихователів дитячого садка асоціації «Дім раннього дитинства» в певній громаді.

Image

Форма для розрахунку заробітної плати на сайті матиме такий вигляд:

Додаток ASP.NET матиме таку архітектуру:

  • під час першого запиту до додатка створюється екземпляр об’єкта типу [Global], похідного від типу [System.Web.HttpApplication]. Саме він буде обробляти файл конфігурації [web.config] веб-додатка та заносити в кеш певні дані з бази даних (операція 0 вище).
  • під час першого запиту (операція 1) до сторінки [Default.aspx], яка є єдиною сторінкою додатка, обробляється подія [Load]. У ній здійснюється заповнення комбінованого списку співробітників. Дані, необхідні для списку, запитуються в об’єкті [Global], який їх закешував. Це операція 2 вище. Операція 4 надсилає користувачеві сторінку, ініціалізовану таким чином.
  • Під час запиту на розрахунок заробітної плати (операція 1), зробленого на сторінці [Default.aspx], знову обробляється подія [Load]. Нічого не відбувається, оскільки це запит типу POST, а обробник [Pam_Load] написано так, щоб у цьому випадку нічого не робити (використання логічного значення IsPostBack). Після обробки події [Load] обробляється подія [Click] на кнопці [Salaire]. Ця подія потребує даних, які не були збережені в кеші в [Global]. Тому обробник події запитує їх у базі даних. Це операція 3, зазначена вище. Потім виконується розрахунок заробітної плати, а операція 4 надсилає результати користувачеві.

4.2. Проєкт Visual Web Developer 2008

  • у [1] створюється новий проєкт
  • у [2], типу [Web / Application Web ASP.NET]
  • у [3], присвоюємо назву проекту
  • у [4] уточнюємо його назву, а в [5] — його розташування. Для проекту буде створено папку [c:\temp\pam-aspnet\pam-v1-adonet].
  • у [5] проект Visual Web Developer
  • у [6] — властивості проекту (клацнути правою кнопкою миші на проекті / Властивості / Додаток).
  • в [7] — ім’я збірки, яка буде створена під час генерації проекту
  • у [8] — простір імен за замовчуванням, який ми хочемо використовувати для класів проекту. Клас [_Default], визначений у файлах [Default.aspx.cs] та [Default.aspx.designer.cs], було створено в просторі імен [pam_v1_adonet], похідному від назви проєкту. Цей простір імен можна змінити безпосередньо в коді цих двох файлів:

[Default.aspx.cs]


using System;
....

namespace pam_v1
{
  public partial class _Default : System.Web.UI.Page
  {

[Default.aspx.designer.cs]


//------------------------------------------------------------------------------
// <автоматично згенеровано>
//      Цей код було згенеровано за допомогою інструменту.
//      Версія середовища виконання: 2.0.50727.3603
//
//      Зміни, внесені до цього файлу, можуть спричинити некоректну роботу і будуть втрачені, якщо
//      код буде згенеровано заново.
// </auto-generated>
//------------------------------------------------------------------------------

namespace pam_v1 {
    
    
    public partial class _Default {
        
.........

Розмітку файлу [Default.aspx] також потрібно змінити:

  

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="pam_v1._Default" %>
...

Атрибут Inherits, наведений вище, позначає клас, визначений у файлах [Default.aspx.cs] та [Default.aspx.designer.cs]. У ньому використовується простір імен pam_v1.

4.2.1. Форма [Default.aspx]

Візуальний вигляд форми [Default.aspx] такий:

Компоненти такі:

Тип
Назва
Роль
1
DropDownList
ComboBoxEmployes
Містить список імен співробітників
2
TextBox
TextBoxHeures
Кількість відпрацьованих годин — фактична кількість
3
TextBox
TextBoxJours
Кількість відпрацьованих днів – ціле число
4
Кнопка
ButtonSalaire
Запит на розрахунок заробітної плати
5
TextBox
TextBoxErreur
Інформаційне повідомлення для користувача
ReadOnly=true, TextMode=MultiLine
6
Мітка
LabelNom
Ім'я співробітника, обраного в пункті (1)
7
Мітка
LabelPrénom
Ім'я обраного працівника (1)
8
Мітка
LabelAdresse
Адреса
9
Етикетка
LabelVille
Місто
10
Етикетка
LabelCP
Поштовий індекс
11
Етикетка
LabelIndice
Індекс
12
Етикетка
LabelCSGRDS
Ставка внеску CSGRDS
13
Етикетка
LabelCSGD
Ставка внеску CSGD
14
Етикетка
LabelRetraite
Ставка страхового внеску на пенсійне забезпечення
15
Мітка
LabelSS
Ставка внесків на соціальне страхування
16
Етикетка
LabelSH
Базова погодинна заробітна плата для індексу, зазначеного в (11)
17
Мітка
LabelEJ
Добова надбавка на утримання для індексу, зазначеного в (11)
18
Мітка
LabelRJ
Добова надбавка на харчування за індексом, зазначеним у (11)
19
Мітка
LabelCongés
Ставка надбавки за оплачувану відпустку, що застосовується до базової заробітної плати
20
Мітка
LabelSB
Сума базової заробітної плати
21
Мітка
LabelCS
Сума соціальних внесків, що підлягають сплаті
22
Мітка
LabelIE
Сума виплат на утримання дитини, що перебуває під опікою
23
Мітка
LabelIR
Сума дотацій на харчування дитини, що перебуває під опікою
24
Мітка
LabelSN
Чиста заробітна плата, що підлягає виплаті працівнику

Форма також містить два контейнери типу [Panel]:

PanelErreurs
містить компонент (5) TextBoxErreur
PanelSalaire
містить компоненти від (6) до (24)

Компонент типу [Panel] можна зробити видимим або невидимим програмно за допомогою його булевого властивості [Panel].Visible.

4.2.2. Перевірка введених даних

Для розрахунку заробітної плати користувач:

  • вибирає працівника в [1]
  • вводить кількість відпрацьованих годин у полі [2]. Це число може бути десятковим, наприклад 2,5 для 2 годин 30 хвилин.
  • вводить кількість відпрацьованих днів у полі [3]. Це число є цілим.
  • запитуйте заробітну плату за допомогою кнопки [4]

Коли користувач вводить неправильні дані в поля [2] та [3], вони перевіряються, щойно користувач переходить до іншого поля введення. Так, наведений нижче знімок екрана було зроблено ще до того, як користувач натиснув кнопку [Salaire]:

Для забезпечення описаної вище поведінки необхідні дві умови:

  • властивість EnableClientScript компонентів перевірки повинна мати значення «true»:
  
  • браузер, що відображає сторінку, повинен мати можливість виконувати код JavaScript, вбудований у сторінку HTML.

Якщо клієнтський браузер самостійно не перевіряє валідність даних, їх перевірка відбуватиметься лише тоді, коли браузер надішле дані, введені у форму, на сервер. Саме тоді код, розміщений на сервері та призначений для обробки запиту браузера, перевірить валідність даних. Нагадаємо, що ця перевірка має виконуватися завжди, навіть якщо сторінка, що відображається в браузері клієнта, містить код JavaScript, який здійснює ту саму перевірку. Адже сервер не може бути впевнений, що запит POST, який надходить до нього, дійсно походить з цієї сторінки і що, отже, перевірка даних була виконана.

Список валідаторів такий:

Тип
Назва
Роль
25
RequiredFieldValidator
RequiredFieldValidatorHeures
перевіряє, чи поле [2] [TextBoxHeures] не є порожнім
26
RangeValidator
RangeValidatorHeures
перевіряє, чи поле [2] [TextBoxHeures] є дійсним числом в інтервалі [0, 200]
27
RequiredFieldValidator
RequiredFieldValidatorJours
перевіряє, чи поле [3] [TextBoxJours] не є порожнім
28
RangeValidator
RangeValidatorJours
перевіряє, чи поле [3] [TextBoxJours] є цілим числом у проміжку [0,31]

Завдання: створити сторінку [Default.aspx]. Спочатку розмістимо два контейнери [PanelErreurs] та [PanelSalaire], щоб потім розмістити в них компоненти, які вони мають містити.


4.2.3. Елементи додатка

Після зчитування рядки таблиць [cotisations], [employes], [indemnites] будуть поміщені в об’єкти типу [Cotisations], [Employe] та [Indemnites], які визначені таким чином:


namespace Pam.Entites
{
  public class Cotisations
  {
    // автоматичні властивості
    public double CsgRds { get; set; }
    public double Csgd { get; set; }
    public double Secu { get; set; }
    public double Retraite { get; set; }

    // конструктори
    public Cotisations()
    {
    }

    public Cotisations(double csgRds, double csgd, double secu, double retraite)
    {
      CsgRds = csgRds;
      Csgd = csgd;
      Secu = secu;
      Retraite = retraite;
    }

    // ToString
    public override string ToString()
    {
      return string.Format("[{0},{1},{2},{3}]", CsgRds, Csgd, Secu, Retraite);
    }
  }
}

namespace Pam.Entites
{
  public class Employe
  {
    // автоматичні властивості
    public string SS { get; set; }
    public string Nom { get; set; }
    public string Prenom { get; set; }
    public string Adresse { get; set; }
    private string Ville { get; set; }
    private string CodePostal { get; set; }
    private int Indice { get; set; }

    // виробники
    public Employe()
    {

    }

    public Employe(string ss, string nom, string prenom, string adresse, string codePostal, string ville, int indice)
    {
      SS = ss;
      Nom = nom;
      Prenom = prenom;
      Adresse = adresse;
      CodePostal = codePostal;
      Ville = ville;
      Indice = indice;
    }

    // ToString
    public override string ToString()
    {
      return string.Format("[{0},{1},{2},{3},{4},{5},{6}]", SS, Nom, Prenom, Adresse, Ville, CodePostal, Indice);
    }
  }
}

namespace Pam.Entites
{
  public class Indemnites
  {

    // автоматичні властивості
    public int Indice { get; set; }
    public double BaseHeure { get; set; }
    public double EntretienJour { get; set; }
    public double RepasJour { get; set; }
    public double IndemnitesCP { get; set; }

    // конструктори
    public Indemnites()
    {

    }

    public Indemnites(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP)
    {
      Indice = indice;
      BaseHeure = baseHeure;
      EntretienJour = entretienJour;
      RepasJour = repasJour;
      IndemnitesCP = indemnitesCP;
    }

    // ідентичність
    public override string ToString()
    {
      return string.Format("[{0}, {1}, {2}, {3}, {4}]", Indice, BaseHeure, EntretienJour, RepasJour, IndemnitesCP);
    }

  }
}

4.2.4. Налаштування програми

Файл [Web.config], що налаштовує додаток, матиме такий вигляд:


<?xml version="1.0" encoding="utf-8"?>

<configuration>
    <configSections>
...
    </configSections>

  <connectionStrings>
    <add name="dbpamSqlServer2005" connectionString="Data Source=.\SQLEXPRESS;AttachDbFilename=C:\data\...\dbpam.mdf;User Id=sa;Password=msde;Connect Timeout=30;" providerName="System.Data.SqlClient"/>
  </connectionStrings>

  <appSettings>
    <add key="selectEmploye" value="select NOM,PRENOM,ADRESSE,VILLE,CODEPOSTAL,INDICE from EMPLOYES where SS=@SS"/>
    <add key="selectEmployes" value="select PRENOM, NOM, SS from EMPLOYES"/>
    <add key="selectCotisations" value="select CSGRDS,CSGD,SECU,RETRAITE from COTISATIONS"/>
    <add key="SelectIndemnites" value="select INDICE,BASEHEURE,ENTRETIENJOUR,REPASJOUR,INDEMNITESCP from INDEMNITES"/>
  </appSettings>

  <system.web>
        <!-- 
            Définissez compilation debug="true" pour insérer des symboles 
            de débogage dans la page compilée. Comme ceci 
            affecte les performances, définissez cette valeur à true uniquement 
            lors du développement.
        -->
        <compilation debug="false">
...
</configuration>
  • рядок 9: визначає рядок підключення до попередньої бази даних SQL Server
  • рядки 13–16: визначають запити SQL, які використовує додаток, щоб уникнути їх жорсткого вбудовування в код.
  • рядок 13: задано параметри запиту SQL. Формат запису параметрів є специфічним для сервера SQL.

Завдання: внести ці параметри у файл [Web.config]. Рядок 9 слід адаптувати до фактичного шляху до бази даних [dbpam.mdf].


4.2.5. Ініціалізація додатка

Ініціалізація додатка ASP.NET здійснюється за допомогою файлу [Global.asax.cs]. Він побудований наступним чином:

  • у [1] до проекту додається новий елемент
  • у [2] додається глобальний клас додатка, який за замовчуванням називається [Global.asax] [3]
  • у [4] — файл [Global.asax] та пов'язаний з ним клас [Global.asax.cs]
  • у [5] відображається розмітка файлу [Global.asax]

<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v1.Global" Language="C#" %>

Клас [pam_v1.Global] визначено у файлі [Global.asax.cs]. Для нашої задачі його буде визначено так:


using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data.SqlClient;
using Pam.Entites;

namespace pam_v1
{
  public class Global : System.Web.HttpApplication
  {
    // --- статичні дані додатка ---
    public static Employe[] Employes;
    public static Cotisations Cotisations;
    public static Dictionary<int, Indemnites> Indemnites = new Dictionary<int, Indemnites>();
    public static string Msg = string.Empty;
    public static bool Erreur = false;
    public static string ConnectionString = null;

    // запуск додатка
    public void Application_Start(object sender, EventArgs e)
    {
...
      try
      {
        // вхід
        ConnectionString = ...
        using (SqlConnection connexion = new SqlConnection(ConnectionString))
        {
          connexion.Open();
          // отримуємо список співробітників, який поміщаємо у статичний масив [Employes]
...
          // отримано ставки внесків у статичну змінну [Cotisations]
...
          // отримуємо розміри виплат у статичному словнику [Indemnites]
...
          // операція виконана успішно
          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;
      }
    }
  }
}
  • рядок 20: метод [Application_Start] виконується під час запуску веб-додатку. Він виконується лише один раз.
  • рядки 12–17: публічні та статичні поля класу. Статичне поле є спільним для всіх екземплярів класу. Отже, якщо створено кілька екземплярів класу [Global], вони всі спільно використовують одне й те саме статичне поле [Employes], доступ до якого здійснюється через посилання [Global.Employes], c.a.d. [NomDeClasse].ChampStatique. [Global] — це ім’я класу. Отже, це тип даних. Це ім’я є довільним. Клас завжди походить від [System.Web.HttpApplication].

Повернімося до нашого класу [Global]. Можна задатися питанням, чи потрібно оголошувати його поля статичними. Насправді, здається, що в деяких випадках може існувати кілька екземплярів класу [Global], що виправдовує необхідність оголошення статичними тих полів, які мають бути спільними для всіх цих екземплярів.

Існує ще один спосіб спільного використання даних з областю дії «додаток» між різними сторінками веб-додатка. Так, масив Employes зі списком співробітників можна зберегти в процедурі Application_Start наступним чином:


            Application.Add("employes",Employes);

[Application] за замовчуванням у будь-якому додатку ASP.NET є посиланням на екземпляр класу, визначеного [Global.asax.cs]. Application — це контейнер, здатний зберігати об’єкти будь-якого типу. Масив Employes можна потім отримати в коді будь-якої сторінки веб-додатку таким чином:


            Employe[] employes=Application.Get["employes"] as Employe[];

Оскільки контейнер Application зберігає об’єкти будь-якого типу, ми отримуємо тип Object, який потім потрібно перетворити. Цей метод завжди можна використовувати, але обмін даними через статичні типізовані поля об’єкта [Global] дозволяє уникнути перетворення типів і дає змогу компілятору виконувати перевірки типів, що допомагає розробнику. Саме цей метод буде використано в даному випадку.

Дані, спільні для всіх користувачів, такі:

  • рядок 12: масив об’єктів типу [Employe], який зберігатиме спрощений список (SS, NOM, PRENOM) усіх співробітників
  • рядок 13: об’єкт типу [Cotisations], який зберігатиме ставки внесків
  • рядок 14: словник, який зберігатиме надбавки, пов’язані з різними індексами працівників. Він буде індексований за індексом працівника, а його значення матимуть тип [Indemnites]
  • рядок 15: повідомлення, що вказує, як завершилася ініціалізація (успішно чи з помилкою)
  • рядок 16: логічне значення, що вказує, чи завершилася ініціалізація з помилкою чи ні.
  • рядок 17: рядок підключення до бази даних.

Завдання: доповніть код класу [Application_Start].


4.3. Події форми [Default.aspx]

4.3.1. Процедура [Page_Load]

Коли завантажується форма [Default.aspx], вона шукає в масиві [Global.Employes] (рядок 12) імена співробітників, щоб внести їх у випадаючий список [1]:

  • випадаючий список [1] заповнено
  • TextBox [5] вказує, що база даних була прочитана правильно

Якщо під час запуску програми сталися помилки ініціалізації, про це свідчать коди TextBox та [5]:

Image


Завдання: написати процедуру [Page_Load] для веб-сторінки [Default.aspx], яка, виконуючись під час запуску програми, гарантує попередню роботу.


4.3.2. Розрахунок заробітної плати

Натискання кнопки [4] запускає обробник:


    protected void ButtonSalaire_Click(object sender, System.EventArgs e)

Цей менеджер спочатку перевіряє правильність даних, введених у [2] та [3]. Якщо один із них є неправильним, про помилку повідомляється, як було показано раніше. Після перевірки та підтвердження правильності даних, введених у [2] та [3], програма повинна відобразити додаткові дані про користувача, обраного в [1], а також його заробітну плату (див. знімок екрана в параграфі 4.1).


Завдання: написати код процедури [ButtonSalaire_Click].