4. Die Anwendung [SimuPaie] – Version 1 – ASP.NET
4.1. Einleitung
Wir möchten eine .NET-Anwendung erstellen, mit der ein Benutzer die Lohn- und Gehaltsabrechnung für Kinderbetreuer beim Verein „Maison de la petite enfance“ in einer Gemeinde simulieren kann.
Das ASP.NET-Formular zur Gehaltsberechnung wird wie folgt aussehen:

Die ASP.NET-Anwendung wird folgende Architektur aufweisen:
![]() |
- Wenn die erste Anfrage an die Anwendung gestellt wird, wird ein Objekt vom Typ [Global] instanziiert, das vom Typ [System.Web.HttpApplication] abgeleitet ist. Dieses Objekt verarbeitet die Konfigurationsdatei [web.config] der Webanwendung und speichert bestimmte Daten aus der Datenbank im Cache (Vorgang 0 oben).
- Wenn die erste Anfrage (Schritt 1) an die Seite [Default.aspx] gestellt wird, die die einzige Seite der Anwendung ist, wird das [Load]-Ereignis verarbeitet. Hier wird das Kombinationsfeld für Mitarbeiter gefüllt. Die für das Kombinationsfeld erforderlichen Daten werden aus dem [Global]-Objekt abgerufen, das sie zwischengespeichert hat. Dies ist Schritt 2 oben. Schritt 4 sendet die initialisierte Seite an den Benutzer.
- Wenn die Anfrage zur Berechnung des Gehalts (Vorgang 1) an die Seite [Default.aspx] gestellt wird, wird das [Load]-Ereignis erneut verarbeitet. Es wird nichts unternommen, da es sich um eine POST-Anfrage handelt und der [Pam_Load]-Handler so geschrieben wurde, dass er in diesem Fall nichts tut (unter Verwendung des booleschen Werts IsPostBack). Sobald das [Load]-Ereignis verarbeitet wurde, wird als Nächstes das [Click]-Ereignis der Schaltfläche [Salary] verarbeitet. Dieses Ereignis benötigt Daten, die nicht in [Global] zwischengespeichert wurden. Daher ruft der Ereignishandler sie aus der Datenbank ab. Dies ist der oben beschriebene Vorgang 3. Das Gehalt wird dann berechnet, und Vorgang 4 sendet die Ergebnisse an den Benutzer.
4.2. Visual Web Developer 2008
![]() |
- Erstellen Sie in [1] ein neues Projekt
- in [2] vom Typ [Web / ASP.NET-Webanwendung]
- Benennen Sie das Projekt in [3]
- Geben Sie in [4] den Namen und in [5] den Speicherort an. Für das Projekt wird ein Ordner [c:\temp\pam-aspnet\pam-v1-adonet] erstellt.
![]() |
- In [5] das Visual Web Developer-Projekt
- In [6] die Projekteigenschaften (Rechtsklick auf das Projekt / Eigenschaften / Anwendung).
- in [7] der Name der Assembly, die beim Erstellen des Projekts generiert wird
- in [8] der Standard-Namespace, den wir für die Klassen des Projekts verwenden möchten. Die in den Dateien [Default.aspx.cs] und [Default.aspx.designer.cs] definierte Klasse [_Default] wurde im Namespace [pam_v1_adonet] erstellt, der vom Projektnamen abgeleitet ist. Sie können diesen Namespace direkt im Code dieser beiden Dateien ändern:
[Default.aspx.cs]
using System;
....
namespace pam_v1
{
public partial class _Default : System.Web.UI.Page
{
[Default.aspx.designer.cs]
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime version :2.0.50727.3603
//
// Changes made to this file may result in incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace pam_v1 {
public partial class _Default {
.........
Das Markup für die Datei [Default.aspx] muss ebenfalls geändert werden:
![]() |
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" Inherits="pam_v1._Default" %>
...
Das obige Inherits-Attribut bezieht sich auf die Klasse, die in den Dateien [Default.aspx.cs] und [Default.aspx.designer.cs] definiert ist. Dort wird der Namespace pam_v1 verwendet.
4.2.1. Das Formular [ Default.aspx]
Das Formular [ Default.aspx] sieht wie folgt aus:
![]() |
Die Komponenten sind wie folgt:
Nr. | Typ | Name | Funktion |
DropDownList | Mitarbeiter-Kombinationsfeld | Enthält die Liste der Mitarbeiternamen | |
Textfeld | TextfeldStunden | Anzahl der geleisteten Arbeitsstunden – tatsächliche Anzahl | |
Textfeld | TextBoxDays | Anzahl der gearbeiteten Tage – Ganzzahl | |
Schaltfläche | ButtonGehalt | Gehalt berechnen | |
Textfeld | Fehler-Textfeld | Informationsmeldung für den Benutzer ReadOnly=true, TextMode=MultiLine | |
Bezeichnung | LabelName | Name des in (1) ausgewählten Mitarbeiters | |
Bezeichnung | LabelFirstName | Vorname des in (1) ausgewählten Mitarbeiters | |
Bezeichnung | LabelAddress | Adresse | |
Bezeichnung | BezeichnungStadt | Stadt | |
Etikett | Postleitzahl | Postleitzahl | |
Etikett | LabelIndex | Index | |
Bezeichnung | LabelCSGRDS | CSGRDS-Beitragssatz | |
Bezeichnung | CSGD-Bezeichnung | CSGD-Beitragssatz | |
Etikett | Renten-Bezeichnung | Rentenbeitragssatz | |
Bezeichnung | BezeichnungSS | Sozialversicherungsbeitragssatz | |
Bezeichnung | LabelSH | Stundenlohnbasis für den in (11) angegebenen Index | |
Bezeichnung | LabelEJ | Tagespauschale für den in (11) angegebenen Index | |
Bezeichnung | LabelRJ | Tagespauschale für Verpflegung für den in (11) angegebenen Index | |
Bezeichnung | LabelVacation | Auf das Grundgehalt anzuwendender Satz für bezahlten Urlaub | |
Bezeichnung | LabelSB | Grundgehalt | |
Bezeichnung | LabelCS | Höhe der zu zahlenden Sozialversicherungsbeiträge | |
Bezeichnung | LabelIE | Höhe des Kinderbetreuungsgeldes | |
Bezeichnung | LabelIR | Höhe der Verpflegungszulagen für das betreute Kind | |
Bezeichnung | LabelSN | An den Arbeitnehmer zu zahlendes Nettogehalt |
Das Formular enthält außerdem zwei [Panel]-Container:
enthält die (5) TextBoxError-Komponente | |
enthält die Komponenten (6) bis (24) |
Eine [Panel]-Komponente kann programmgesteuert mithilfe ihrer booleschen Eigenschaft [Panel].Visible ein- oder ausgeblendet werden.
4.2.2. Eingabevalidierung
Um ein Gehalt zu berechnen, muss der Benutzer:
- einen Mitarbeiter in [1] auswählen
- die Anzahl der gearbeiteten Stunden in [2] eingeben. Diese Zahl kann eine Dezimalzahl sein, z. B. 2,5 für 2 Stunden und 30 Minuten.
- die Anzahl der gearbeiteten Tage in [3] eingeben. Diese Zahl ist eine ganze Zahl.
- berechnet das Gehalt über die Schaltfläche [4]
![]() |
Wenn der Benutzer in [2] und [3] falsche Daten eingibt, werden diese überprüft, sobald der Benutzer das Eingabefeld wechselt. Der folgende Screenshot wurde daher aufgenommen, bevor der Benutzer überhaupt auf die Schaltfläche [Gehalt] geklickt hat:
![]() |
Für das oben beschriebene Verhalten sind zwei Bedingungen erforderlich:
- Die Eigenschaft „EnableClientScript“ der Validierungskomponenten muss auf „true“ gesetzt sein:
![]() |
- Der Browser, der die Seite anzeigt, muss in der Lage sein, den in eine HTML-Seite eingebetteten JavaScript-Code auszuführen.
Wenn der Client-Browser die Gültigkeit der Daten nicht selbst überprüft, werden die Daten erst dann überprüft, wenn der Browser die Formulareinträge an den Server übermittelt. Es ist dann der Code auf dem Server, der die Anfrage des Browsers verarbeitet, der die Gültigkeit der Daten überprüft. Beachten Sie, dass diese Validierung immer durchgeführt werden muss, auch wenn die im Client-Browser angezeigte Seite JavaScript-Code enthält, der dieselbe Validierung durchführt. Der Grund dafür ist, dass der Server nicht sicher sein kann, dass die empfangene POST-Anfrage tatsächlich von dieser Seite stammt und die Daten somit validiert wurden.
Die Liste der Validatoren lautet wie folgt:
Nr. | Typ | Name | Rolle |
RequiredFieldValidator | RequiredFieldValidatorHours | prüft, ob das Feld [2] [TextBoxHeures] nicht leer ist | |
RangeValidator | RangeValidatorHours | prüft, ob das Feld [2] [TextBoxHours] eine reelle Zahl im Bereich [0, 200] ist | |
RequiredFieldValidator | RequiredFieldValidatorDays | prüft, ob das Feld [3] [TextBoxDays] nicht leer ist | |
RangeValidator | RangeValidatorDays | prüft, ob das Feld [3] [TextBoxDays] eine ganze Zahl im Bereich [0,31] ist |
Aufgabe: Erstellen Sie die Seite [Default.aspx]. Platzieren Sie zunächst die beiden Container [PanelErrors] und [PanelSalary], damit Sie anschließend die Komponenten einfügen können, die sie enthalten sollen.
4.2.3. Anwendungsentitäten
![]() |
Nach dem Einlesen werden die Zeilen aus den Tabellen [contributions], [employees] und [allowances] in Objekten vom Typ [Contributions], [Employee] und [Allowances] gespeichert, die wie folgt definiert sind:
namespace Pam.Entites
{
public class Cotisations
{
// automatic properties
public double CsgRds { get; set; }
public double Csgd { get; set; }
public double Secu { get; set; }
public double Retraite { get; set; }
// manufacturers
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
{
// automatic properties
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; }
// manufacturers
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
{
// automatic properties
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; }
// manufacturers
public Indemnites()
{
}
public Indemnites(int indice, double baseHeure, double entretienJour, double repasJour, double indemnitesCP)
{
Indice = indice;
BaseHeure = baseHeure;
EntretienJour = entretienJour;
RepasJour = repasJour;
IndemnitesCP = indemnitesCP;
}
// identity
public override string ToString()
{
return string.Format("[{0}, {1}, {2}, {3}, {4}]", Indice, BaseHeure, EntretienJour, RepasJour, IndemnitesCP);
}
}
}
4.2.4. Anwendungskonfiguration
Die Datei [Web.config], die die Anwendung konfiguriert, sieht wie folgt aus:
<?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>
- Zeile 9: definiert die Verbindungszeichenfolge zur SQL Server-Datenbank
- Zeilen 13–16: definieren die von der Anwendung verwendeten SQL-Abfragen, um eine feste Einbettung im Code zu vermeiden.
- Zeile 13: Die SQL-Abfrage ist parametrisiert. Die Parameterkonvention ist spezifisch für SQL Server.
Aufgabe: Tragen Sie diese Parameter in die Datei [Web.config] ein. Zeile 9 wird an den tatsächlichen Pfad der Datenbank [dbpam.mdf] angepasst.
4.2.5. Anwendungsinitialisierung
Eine ASP.NET-Anwendung wird durch die Datei [Global.asax.cs] initialisiert. Sie ist wie folgt aufgebaut:
![]() |
- Fügen Sie in [1] dem Projekt ein neues Element hinzu
- Fügen Sie unter [2] die globale Anwendungsklasse hinzu, die standardmäßig [Global.asax] heißt [3]
![]() |
- in [4] die Datei [Global.asax] und die zugehörige Klasse [Global.asax.cs]
- Zeigen Sie in [5] das Markup für [Global.asax] an
<%@ Application Codebehind="Global.asax.cs" Inherits="pam_v1.Global" Language="C#" %>
Die Klasse [pam_v1.Global] ist in der Datei [Global.asax.cs] definiert. Für unsere Zwecke wird sie wie folgt definiert:
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
{
// --- static application data ---
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;
// application startup
public void Application_Start(object sender, EventArgs e)
{
...
try
{
// connection
ConnectionString = ...
using (SqlConnection connexion = new SqlConnection(ConnectionString))
{
connexion.Open();
// retrieve the list of employees and place it in the static array [Employees]
...
// contribution rates are retrieved from the static variable [Contributions]
...
// retrieve allowances from the static dictionary [Allowances]
...
// we succeeded
Msg = "Base chargée...";
}
}
catch (Exception ex)
{
// we note the error
Msg = string.Format("L'erreur suivante s'est produite lors de l'accès à la base de données : {0}", ex);
Erreur = true;
}
}
}
}
- Zeile 20: Die Methode [Application_Start] wird beim Start der Webanwendung ausgeführt. Sie wird nur einmal ausgeführt.
- Zeilen 12–17: öffentliche und statische Felder der Klasse. Ein statisches Feld wird von allen Instanzen der Klasse gemeinsam genutzt. Wenn also mehrere Instanzen der Klasse [Global] erstellt werden, teilen sie sich alle dasselbe statische Feld [Employees], auf das über die Referenz [Global.Employees] zugegriffen werden kann, d. h. [ClassName].StaticField. [Global] ist der Name einer Klasse. Es handelt sich also um einen Datentyp. Dieser Name ist beliebig. Die Klasse leitet sich immer von [System.Web.HttpApplication] ab.
Kehren wir zu unserer Klasse [Global] zurück. Man könnte sich fragen, ob es notwendig ist, ihre Felder als statisch zu deklarieren. Tatsächlich scheint es, dass es in einigen Fällen mehrere Instanzen der Klasse [Global] geben kann, was es rechtfertigt, die Felder, die von all diesen Instanzen gemeinsam genutzt werden müssen, als statisch zu definieren.
Es gibt noch eine andere Möglichkeit, Daten im „Anwendungs“-Bereich zwischen den verschiedenen Seiten einer Webanwendung gemeinsam zu nutzen. So könnte das Employees-Array wie folgt in der Application_Start-Prozedur gespeichert werden:
Application.Add("employes",Employes);
Standardmäßig ist [Application] in jeder ASP.NET-Anwendung ein Verweis auf eine Instanz der in [Global.asax.cs] definierten Klasse. Application ist ein Container, der Objekte beliebigen Typs speichern kann. Das Array „Employees“ könnte dann im Code jeder Seite der Webanwendung wie folgt abgerufen werden:
Employe[] employes=Application.Get["employes"] as Employe[];
Da der Application-Container Objekte beliebigen Typs speichert, erhalten wir einen Object-Typ, der anschließend umgewandelt werden muss. Diese Methode ist zwar immer anwendbar, doch die gemeinsame Nutzung von Daten über typisierte statische Felder des [Global]-Objekts vermeidet die Umwandlung und ermöglicht es dem Compiler, Typüberprüfungen durchzuführen, die den Entwickler unterstützen. Diese Methode wird hier verwendet.
Die von allen Benutzern gemeinsam genutzten Daten lauten wie folgt:
- Zeile 12: das Array von [Employee]-Objekten, das die vereinfachte Liste (SSN, LAST_NAME, FIRST_NAME) aller Mitarbeiter speichert
- Zeile 13: das Objekt vom Typ [Contributions], das die Beitragssätze speichert
- Zeile 14: das Wörterbuch, in dem die mit den verschiedenen Mitarbeiterindizes verknüpften Zulagen gespeichert werden. Es wird nach dem Mitarbeiterindex indiziert, und seine Werte sind vom Typ [Allowances]
- Zeile 15: eine Meldung, die angibt, ob die Initialisierung erfolgreich oder mit einem Fehler abgeschlossen wurde
- Zeile 16: Ein Boolescher Wert, der angibt, ob die Initialisierung mit einem Fehler endete oder nicht
- Zeile 17: die Datenbankverbindungszeichenfolge.
Frage: Vervollständigen Sie den Code für die Klasse [Application_Start].
4.3. Ereignisse des Formulars [Default.aspx]
4.3.1. Die Prozedur [P age_Load]
Wenn das Formular [Default.aspx] geladen wird, ruft es die Namen der Mitarbeiter aus dem Array [Global.Employees] ab (Zeile 12) und füllt die Dropdown-Liste [1] damit:
![]() |
- Die Dropdown-Liste [1] wurde gefüllt
- Das Textfeld [5] zeigt an, dass die Datenbank korrekt gelesen wurde
Wenn beim Start der Anwendung Initialisierungsfehler aufgetreten sind, zeigt das Textfeld [5] dies an:

Frage: Schreiben Sie die Prozedur [Page_Load] für die Webseite [Default.aspx], die bei Ausführung beim Start der Anwendung das oben beschriebene Verhalten gewährleistet.
4.3.2. Gehaltsberechnung
Ein Klick auf die Schaltfläche [4] löst den Handler aus:
protected void ButtonSalaire_Click(object sender, System.EventArgs e)
Dieser Handler überprüft zunächst die Gültigkeit der in [2] und [3] vorgenommenen Eingaben. Ist eine der beiden Eingaben fehlerhaft, wird der Fehler wie zuvor gezeigt gemeldet. Sobald die Eingaben in [2] und [3] überprüft und für gültig befunden wurden, muss die Anwendung zusätzliche Daten zu dem in [1] ausgewählten Benutzer sowie dessen Gehalt anzeigen (siehe Screenshot in Abschnitt 4.1).
Frage: Schreiben Sie den Code für die Prozedur [ButtonSalaire_Click].











