4. Aplikacja [SimuPaie] – wersja 1 – ASP.NET
4.1. Introduction
Chcemy stworzyć aplikację o nazwie .NET, która umożliwi użytkownikowi przeprowadzanie symulacji obliczeń wynagrodzeń opiekunek dziecięcych ze stowarzyszenia „Maison de la petite enfance” w danej gminie.
formularz ASP.NET służący do obliczania wynagrodzenia będzie wyglądał następująco:

Aplikacja ASP.NET będzie miała następującą architekturę:
![]() |
- Podczas pierwszego żądania skierowanego do aplikacji tworzona jest instancja obiektu typu [Global], wywodzącego się z typu [System.Web.HttpApplication]. To właśnie on wykorzysta plik konfiguracyjny [web.config] aplikacji internetowej i zapisał w pamięci podręcznej niektóre dane z bazy danych (operacja 0 powyżej).
- Podczas pierwszego żądania (operacja 1) skierowanego do strony [Default.aspx], która jest jedyną stroną aplikacji, przetwarzane jest zdarzenie [Load]. W ramach tego zdarzenia realizowane jest wypełnienie listy rozwijanej pracowników. Dane niezbędne do wypełnienia listy rozwijanej są pobierane z obiektu [Global], który je zapisał w pamięci podręcznej. Jest to operacja 2 powyżej. Operacja 4 wysyła tak zainicjowaną stronę do użytkownika.
- Podczas żądania obliczenia wynagrodzenia (operacja 1) skierowanego do strony [Default.aspx] ponownie przetwarzane jest zdarzenie [Load]. Nie podejmuje się żadnych działań, ponieważ jest to żądanie typu POST, a moduł obsługi [Pam_Load] został zaprogramowany tak, aby w tym przypadku nie podejmować żadnych działań (wykorzystanie wartości logicznej IsPostBack). Po przetworzeniu zdarzenia [Load] następnym przetwarzanym zdarzeniem jest zdarzenie [Click] dotyczące przycisku [Salaire]. To zdarzenie wymaga danych, które nie zostały zapisane w pamięci podręcznej w zdarzeniu [Global]. Dlatego też obsługa zdarzenia pobiera je z bazy danych. Jest to operacja nr 3 opisana powyżej. Następnie przeprowadzane jest obliczenie wynagrodzenia, a operacja nr 4 wysyła wyniki do użytkownika.
4.2. Projekt Visual Web Developer 2008
![]() |
- w pliku [1] tworzy się nowy projekt
- w [2], typu [Web / Application Web ASP.NET]
- w [3] nadajemy nazwę projektowi
- w [4] podajemy jego nazwę, a w [5] lokalizację. Dla projektu zostanie utworzony folder [c:\temp\pam-aspnet\pam-v1-adonet].
![]() |
- w [5] projekt Visual Web Developer
- w [6] – właściwości projektu (kliknij prawym przyciskiem myszy na projekcie / Właściwości / Aplikacja).
- na [7], nazwa zestawu, który zostanie wygenerowany podczas kompilacji projektu
- w [8], domyślna przestrzeń nazw, którą chcemy zastosować dla klas projektu. Klasa [_Default] zdefiniowana w plikach [Default.aspx.cs] i [Default.aspx.designer.cs] została utworzona w przestrzeni nazw [pam_v1_adonet], wywodzącej się od nazwy projektu. Przestrzeń nazw tę można zmienić bezpośrednio w kodzie tych dwóch plików:
[Default.aspx.cs]
using System;
....
namespace pam_v1
{
public partial class _Default : System.Web.UI.Page
{
[Default.aspx.designer.cs]
//------------------------------------------------------------------------------
// <wygenerowane automatycznie>
// Ten kod został wygenerowany przez narzędzie.
// Wersja środowiska uruchomieniowego: 2.0.50727.3603
//
// Zmiany wprowadzone w tym pliku mogą spowodować nieprawidłowe działanie i zostaną utracone, jeśli
// kod zostanie wygenerowany ponownie.
// </auto-generated>
//------------------------------------------------------------------------------
namespace pam_v1 {
public partial class _Default {
.........
Należy również zmodyfikować znaczniki w pliku [Default.aspx]:
![]() |
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="pam_v1._Default" %>
...
Powyższy atrybut Inherits odnosi się do klasy zdefiniowanej w plikach [Default.aspx.cs] i [Default.aspx.designer.cs]. Wykorzystuje się w nich przestrzeń nazw pam_v1.
4.2.1. Formularz [Default.aspx]
Wygląd formularza [Default.aspx] jest następujący:
![]() |
Składniki są następujące:
Nr | Typ | Nazwa | Rola |
DropDownList | ComboBoxEmployes | Zawiera listę nazwisk pracowników | |
TextBox | TextBoxHeures | Liczba przepracowanych godzin – rzeczywista | |
TextBox | TextBoxJours | Liczba przepracowanych dni – liczba całkowita | |
Przycisk | ButtonSalaire | Żądanie obliczenia wynagrodzenia | |
TextBox | TextBoxErreur | Komunikat informacyjny dla użytkownika ReadOnly=true, TextMode=MultiLine | |
Etykieta | LabelNom | Nazwisko wybranego pracownika w (1) | |
Etykieta | LabelPrénom | Imię wybranego pracownika (1) | |
Etykieta | LabelAdresse | Adres | |
Etykieta | LabelVille | Miasto | |
Etykieta | LabelCP | Kod pocztowy | |
Etykieta | LabelIndice | Indeks | |
Etykieta | LabelCSGRDS | Stawka składki CSGRDS | |
Etykieta | LabelCSGD | Stawka składki CSGD | |
Etykieta | LabelRetraite | Stawka składki emerytalnej | |
Etykieta | LabelSS | Stawka składki na ubezpieczenie społeczne | |
Etykieta | LabelSH | Podstawowa stawka godzinowa dla wskaźnika wskazanego w polu (11) | |
Etykieta | LabelEJ | Dzienna dieta na utrzymanie dla wskaźnika wskazanego w polu (11) | |
Etykieta | LabelRJ | Dzienna dieta na posiłki dla wskaźnika wskazanego w polu (11) | |
Etykieta | LabelCongés | Stawka dodatku za płatny urlop, którą należy zastosować do wynagrodzenia podstawowego | |
Etykieta | LabelSB | Kwota wynagrodzenia podstawowego | |
Etykieta | LabelCS | Kwota należnych składek na ubezpieczenie społeczne | |
Etykieta | LabelIE | Kwota zasiłków na utrzymanie dziecka pozostającego pod opieką | |
Etykieta | LabelIR | Kwota dodatku na posiłki dla dziecka przebywającego w placówce opiekuńczej | |
Etykieta | LabelSN | Wynagrodzenie netto należne pracownikowi |
Formularz zawiera ponadto dwa pola typu [Panel]:
zawiera komponent (5) TextBoxErreur | |
zawiera komponenty od (6) do (24) |
Komponent typu [Panel] można programowo ustawić jako widoczny lub niewidoczny za pomocą jego właściwości boolowskiej [Panel].Visible.
4.2.2. Weryfikacja danych wprowadzonych
Aby obliczyć wynagrodzenie, użytkownik:
- wybiera pracownika w polu [1]
- wprowadza liczbę przepracowanych godzin w polu [2]. Liczba ta może być ułamkowa, np. 2,5 dla 2 godz. 30 min.
- wprowadza liczbę przepracowanych dni w polu [3]. Liczba ta jest liczbą całkowitą.
- żądaj wypłaty, klikając przycisk [4]
![]() |
Gdy użytkownik wprowadzi błędne dane w polach [2] i [3], są one sprawdzane natychmiast po zmianie pola wprowadzania danych przez użytkownika. W związku z tym poniższy zrzut ekranu został wykonany jeszcze przed kliknięciem przez użytkownika przycisku [Salaire]:
![]() |
Aby uzyskać powyższe zachowanie, muszą być spełnione dwa warunki:
- komponenty walidacyjne muszą mieć właściwość EnableClientScript ustawioną na wartość „prawda”:
![]() |
- przeglądarka wyświetlająca stronę musi obsługiwać kod JavaScript osadzony na stronie HTML.
Jeśli przeglądarka klienta nie sprawdza samodzielnie poprawności danych, zostaną one zweryfikowane dopiero w momencie, gdy przeglądarka prześle dane z formularza na serwer. Wówczas to kod znajdujący się na serwerze, który przetwarza żądanie przeglądarki, sprawdzi poprawność danych. Należy pamiętać, że weryfikacja ta musi być zawsze przeprowadzana, nawet jeśli strona wyświetlana w przeglądarce klienta zawiera kod JavaScript wykonujący tę samą weryfikację. Serwer nie ma bowiem pewności, że przesłane do niego żądanie POST rzeczywiście pochodzi z tej strony, a zatem że weryfikacja danych została przeprowadzona.
Lista walidatorów jest następująca:
Nr | Typ | Nazwa | Rola |
RequiredFieldValidator | RequiredFieldValidatorHeures | sprawdza, czy pole [2] [TextBoxHeures] nie jest puste | |
RangeValidator | RangeValidatorHeures | sprawdza, czy pole [2] [TextBoxHeures] jest liczbą rzeczywistą z przedziału [0, 200] | |
RequiredFieldValidator | RequiredFieldValidatorJours | sprawdza, czy pole [3] [TextBoxJours] nie jest puste | |
RangeValidator | RangeValidatorJours | sprawdza, czy pole [3] [TextBoxJours] jest liczbą całkowitą z przedziału [0,31] |
Zadanie: należy utworzyć stronę [Default.aspx]. Najpierw należy umieścić dwa kontenery [PanelErreurs] i [PanelSalaire], aby następnie móc umieścić w nich komponenty, które mają zawierać.
4.2.3. Elementy aplikacji
![]() |
Po odczytaniu wiersze z tabel [cotisations], [employes], [indemnites] zostaną umieszczone w obiektach typu [Cotisations], [Employe] oraz [Indemnites], zdefiniowanych w następujący sposób:
namespace Pam.Entites
{
public class Cotisations
{
// właściwości automatyczne
public double CsgRds { get; set; }
public double Csgd { get; set; }
public double Secu { get; set; }
public double Retraite { get; set; }
// konstruktory
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
{
// właściwości automatyczne
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; }
// producenci
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
{
// właściwości automatyczne
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; }
// konstruktorzy
public Indemnites()
{
}
public Indemnites(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP)
{
Indice = indice;
BaseHeure = baseHeure;
EntretienJour = entretienJour;
RepasJour = repasJour;
IndemnitesCP = indemnitesCP;
}
// tożsamość
public override string ToString()
{
return string.Format("[{0}, {1}, {2}, {3}, {4}]", Indice, BaseHeure, EntretienJour, RepasJour, IndemnitesCP);
}
}
}
4.2.4. Konfiguracja aplikacji
Plik [Web.config], który konfiguruje aplikację, będzie wyglądał następująco:
<?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>
- wiersz 9: definiuje ciąg połączenia z poprzednią bazą danych serwera SQL
- wiersze 13–16: definiują zapytania SQL wykorzystywane przez aplikację, aby uniknąć ich stałego zapisywania w kodzie.
- wiersz 13: zapytanie SQL jest skonfigurowane. Notacja parametrów jest specyficzna dla serwera SQL.
Zadanie do wykonania: wprowadzić te parametry do pliku [Web.config]. Wiersz 9 należy dostosować do rzeczywistej ścieżki do bazy danych [dbpam.mdf].
4.2.5. Inicjalizacja aplikacji
Inicjalizacja aplikacji ASP.NET odbywa się za pomocą pliku [Global.asax.cs]. Plik ten ma następującą strukturę:
![]() |
- w pliku [1] dodaje się nowy element do projektu
- w pliku [2] dodaje się globalną klasę aplikacji, która domyślnie nosi nazwę [Global.asax] [3]
![]() |
- w [4], plik [Global.asax] i powiązana klasa [Global.asax.cs]
- w [5] wyświetla się znaczniki pliku [Global.asax]
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v1.Global" Language="C#" %>
Klasa [pam_v1.Global] jest zdefiniowana w pliku [Global.asax.cs]. W naszym przypadku zostanie ona zdefiniowana w następujący sposób:
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
{
// --- statyczne dane aplikacji ---
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;
// uruchomienie aplikacji
public void Application_Start(object sender, EventArgs e)
{
...
try
{
// logowanie
ConnectionString = ...
using (SqlConnection connexion = new SqlConnection(ConnectionString))
{
connexion.Open();
// pobieramy listę pracowników i umieszczamy ją w tablicy statycznej [Employes]
...
// pobieranie stawek składek do zmiennej statycznej [Cotisations]
...
// pobieramy dodatki do statycznego słownika [Indemnites]
...
// operacja zakończyła się powodzeniem
Msg = "Base chargée...";
}
}
catch (Exception ex)
{
// odnotowujemy błąd
Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
Erreur = true;
}
}
}
}
- wiersze 20: metoda [Application_Start] jest wykonywana przy uruchomieniu aplikacji internetowej. Jest wykonywana tylko raz.
- wiersze 12–17: pola publiczne i statyczne klasy. Pole statyczne jest wspólne dla wszystkich instancji klasy. Zatem jeśli utworzonych zostanie kilka instancji klasy [Global], wszystkie one będą współdzielić to samo pole statyczne [Employes], dostępne poprzez odwołania [Global.Employes] oraz c.a.d. [NomDeClasse].ChampStatique. [Global] to nazwa klasy. Jest to zatem typ danych. Nazwa ta jest dowolna. Klasa zawsze wywodzi się z klasy [System.Web.HttpApplication].
Wróćmy do naszej klasy [Global]. Można się zastanawiać, czy konieczne jest zadeklarowanie jej pól jako statycznych. W rzeczywistości wydaje się, że w niektórych przypadkach może istnieć wiele instancji klasy [Global], co uzasadnia nadanie charakteru statycznego polom, które mają być współdzielone przez wszystkie te instancje.
Istnieje inny sposób udostępniania danych o zasięgu „aplikacji” między różnymi stronami aplikacji internetowej. W ten sposób tablica pracowników Employes mogłaby zostać zapisana w procedurze Application_Start w następujący sposób:
Application.Add("employes",Employes);
[Application] jest domyślnie w każdej aplikacji ASP.NET odwołaniem do instancji klasy zdefiniowanej przez [Global.asax.cs]. Application jest kontenerem, który może przechowywać obiekty dowolnego typu. Tablice Employes można następnie pobrać w kodzie dowolnej strony aplikacji internetowej w następujący sposób:
Employe[] employes=Application.Get["employes"] as Employe[];
Ponieważ kontener Application przechowuje obiekty dowolnego typu, otrzymujemy typ Object, który następnie trzeba przekonwertować. Metoda ta jest nadal możliwa do zastosowania, jednak udostępnianie danych za pośrednictwem statycznych pól typowanych obiektu [Global] pozwala uniknąć rzutowania typów i umożliwia kompilatorowi przeprowadzanie weryfikacji typów, co stanowi pomoc dla programisty. W tym przypadku zastosujemy właśnie tę metodę.
Dane udostępniane wszystkim użytkownikom są następujące:
- wiersz 12: tablica obiektów typu [Employe], która będzie przechowywać uproszczoną listę (SS, NOM, PRENOM) wszystkich pracowników
- wiersz 13: obiekt typu [Cotisations], w którym będą przechowywane stawki składek
- wiersz 14: słownik, w którym zapisane zostaną dodatki związane z różnymi wskaźnikami pracowników. Będzie on indeksowany według wskaźnika pracownika, a jego wartości będą miały typ [Indemnites]
- wiersz 15: komunikat wskazujący, jak zakończyła się inicjalizacja (pomyślnie lub z błędem)
- wiersz 16: wartość logiczna wskazująca, czy inicjalizacja zakończyła się błędem, czy nie.
- wiersz 17: ciąg znaków służący do połączenia z bazą danych.
Zadanie: uzupełnij kod klasy [Application_Start].
4.3. Zdarzenia formularza [Default.aspx]
4.3.1. Procedura [Page_Load]
Gdy formularz [Default.aspx] zostanie załadowany, wyszukuje on w tablicy [Global.Employes] (wiersz 12) nazwiska pracowników, aby umieścić je w liście rozwijanej [1]:
![]() |
- lista rozwijana [1] została wypełniona
- TextBox [5] wskazuje, że baza danych została poprawnie odczytana
Jeśli podczas uruchamiania aplikacji wystąpiły błędy inicjalizacji, wskazują na to kody TextBox i [5]:

Pytanie: Należy opisać procedurę [Page_Load] ze strony internetowej [Default.aspx], która po uruchomieniu aplikacji zapewnia powyższe działanie.
4.3.2. Obliczanie wynagrodzenia
Kliknięcie przycisku [4] powoduje uruchomienie menedżera:
protected void ButtonSalaire_Click(object sender, System.EventArgs e)
Moduł ten rozpoczyna od sprawdzenia poprawności danych wprowadzonych w [2] i [3]. Jeśli którekolwiek z nich jest nieprawidłowe, błąd jest sygnalizowany tak, jak pokazano powyżej. Po zweryfikowaniu i uznaniu za prawidłowe danych wprowadzonych w [2] i [3] aplikacja musi wyświetlić dodatkowe dane dotyczące użytkownika wybranego w [1], a także jego wynagrodzenie (patrz zrzut ekranu w paragrafie 4.1).
Zadanie: napisać kod procedury [ButtonSalaire_Click].











