4. Das Modell einer Aktion
Kehren wir zur Architektur einer ASP.NET MVC-Anwendung zurück:
![]() |
Im vorigen Kapitel haben wir uns den Prozess angesehen, der die Anfrage [1] an den Controller und die Aktion [2a] weiterleitet, die sie bearbeiten werden – ein Mechanismus, der als Routing bekannt ist. Außerdem haben wir die verschiedenen Antworten vorgestellt, die eine Aktion an den Browser zurücksenden kann. Bislang haben wir Aktionen vorgestellt, die die an sie gerichtete Anfrage nicht verarbeitet haben. Eine Anfrage [1] enthält verschiedene Informationen, die ASP.NET MVC [2a] der Aktion in Form eines Modells übermittelt. Dieser Begriff ist nicht zu verwechseln mit dem M-Modell einer V-Ansicht [2c], die von der Aktion erzeugt wird:
![]() |
- Die HTTP-Anfrage des Clients trifft bei [1] ein;
- in [2] werden die in der Anfrage enthaltenen Informationen in ein Aktionsmodell [3] umgewandelt, oft, aber nicht unbedingt eine Klasse, das als Eingabe für die Aktion [4] dient;
- an [4] generiert die Aktion auf der Grundlage dieses Modells eine Antwort. Diese Antwort besteht aus zwei Komponenten: einer Ansicht V [6] und dem Modell M dieser Ansicht [5];
- die Ansicht V [6] verwendet ihr Modell M [5], um die für den Client bestimmte HTTP-Antwort zu generieren.
Im MVC-Modell ist die Aktion [4] Teil des C (Controllers), das Ansichtsmodell [5] ist das M und die Ansicht [6] ist das V.
Dieses Kapitel untersucht die Mechanismen zur Verknüpfung der in der Anfrage enthaltenen Informationen – bei denen es sich naturgemäß um Zeichenfolgen handelt – mit dem Aktionsmodell, bei dem es sich um eine Klasse mit Eigenschaften verschiedener Typen handeln kann.
4.1. Initialisierung von Aktionsparametern
Wir fügen [1] der bestehenden Lösung ein neues ASP.NET MVC-Basisprojekt hinzu:
![]() |
![]() |
- in [2] den Namen des neuen Projekts;
- in [3, 4] wählen wir ein einfaches ASP.NET MVC-Projekt aus;
- in [5] das neue Projekt.
Wir machen das neue Projekt zum Startprojekt für die Lösung.
Wie in Abschnitt 3.1 erstellen wir einen Controller mit dem Namen [First] [1]:

In diesem Controller erstellen wir die folgende Aktion [Action01]:
using System.Web.Mvc;
namespace Exemple_02.Controllers
{
public class FirstController : Controller
{
// Action01
public ContentResult Action01(string nom)
{
return Content(string.Format("Contrôleur=First, Action=Action01, nom={0}", nom));
}
}
}
Die Neuerung befindet sich in Zeile 8: Die Methode [Action01] hat einen Parameter. In diesem Kapitel werden wir die verschiedenen Möglichkeiten zur Initialisierung der Parameter einer Aktion untersuchen. Der oben genannte Parameter [name] wird der Reihe nach mit den folgenden Werten initialisiert:
Request.Form["name"] | ein Parameter namens [name], der von einer POST-Anfrage gesendet wurde |
RouteData.Values["name"] | ein URL-Element namens [name] |
Request.QueryString["name"] | ein Parameter namens [name], der von einer GET-Anfrage gesendet wurde |
Request.Files["name"] | eine hochgeladene Datei mit dem Namen [name] |
Betrachten wir diese verschiedenen Fälle. Geben wir die URL [/First/Action01?name=someone] direkt in den Browser ein. Wir erhalten folgende Antwort:

Die HTTP-Anfrage des Browsers lautete wie folgt:
- Zeile 1: Die Anfrage ist eine GET-Anfrage. Die angeforderte URL enthält den Parameter [name]. Auf der Serverseite wird die Anfrage an die Aktion [Action01] weitergeleitet, die folgende Signatur hat:
public ContentResult Action01(string nom)
Um dem Parameter name einen Wert zuzuweisen, überprüft ASP.NET MVC die folgenden Werte der Reihe nach: *Request.Form["name"], RouteData.Values["name"],* *<span style="color: #2323dc">Request.QueryString["name"]</span>**, Request.Files["name"]*. Sobald ein Wert gefunden wird, wird die Suche beendet. Der in der GET-URL eingebettete Parameter [name] wurde vom Framework in Request.QueryString["name"] abgelegt. Mit diesem Wert [someone] wird der Parameter [name] von [Action01] initialisiert. Anschließend wird der Code von [Action01] ausgeführt:
return Content(string.Format("Contrôleur=First, Action=Action01, nom={0}", nom), "text/plain", Encoding.UTF8);
Dieser Code liefert die an den Client gesendete Antwort:

Hinweis: Bei der Parameterzuordnung wird die Groß-/Kleinschreibung nicht berücksichtigt. Wenn unsere Aktion also wie folgt definiert ist:
public ContentResult Action01(string NOM)
und der übergebene Parameter lautet [?Name=zébulon], findet die Bindung dennoch statt. Der Parameter [Name] von [Action01] erhält den Wert [zébulon].
Nun rufen wir dieselbe URL mit einem POST-Request auf. Dazu verwenden wir die Anwendung [Advanced Rest Client]:
![]() |
- in [1] die angeforderte URL;
- in [2] wird die POST-Methode verwendet;
- in [3] die POST-Parameter.
Senden wir diese Anfrage und sehen wir uns die HTTP-Protokolle an. Die HTTP-Anfrage lautet wie folgt:
![]() |
- in [1] die POST-Anfrage;
- in [2] die POST-Parameter. Technisch gesehen wurden sie nach den HTTP-Headern gesendet, im Anschluss an die Leerzeile, die das Ende dieser Header anzeigt;
- in [3] die empfangene Antwort. Wir haben den Parameter [name] erfolgreich aus dem POST abgerufen. Unter den getesteten Werten für den Parameter „name“ – Request.Form["name"], RouteData.Values["name"], Request.QueryString["name"], Request.Files["name"] – hat der erste funktioniert.
Ändern wir nun die Standardroute in [App_Start/RouteConfig]. Derzeit sieht diese Route wie folgt aus:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
Ändern wir das in:
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{nom}",
defaults: new { controller = "Home", action = "Index", nom = UrlParameter.Optional }
);
- In Zeile 3 haben wir das dritte Element einer Route [name] genannt;
- in Zeile 4 wird dieses Element als optional deklariert.
Kompilieren wir nun die Anwendung neu und rufen die URL [/First/Action01/zébulon] direkt im Browser auf. Wir erhalten folgende Antwort:

Von den getesteten Werten für den Parameter „name“ – Request.Form["name"], RouteData.Values["name"], Request.QueryString["name"], Request.Files["name"] – hat der zweite funktioniert.
Führen wir dieselbe Anfrage mit einer POST-Anfrage und [Advanced Rest Client] durch:
![]() |
- In [1] haben wir dem Element {name} der Route einen Wert zugewiesen;
- in [2] fügen wir der gesendeten Anfrage einen [name]-Parameter hinzu;
- die erhaltene Antwort ist in [3] zu sehen.
Von den für den Parameter [name] getesteten Werten – Request.Form["name"], RouteData.Values["name"], Request.QueryString["name"], Request.Files["name"] – waren zwei gültig: die ersten beiden. Der erste wurde verwendet.
4.2. Validierung von Aktionsparametern
Wenn eine Aktion einen Parameter namens [p] enthält, versucht ASP.NET MVC, ihm einen der folgenden Werte zuzuweisen: Request.Form["p"], RouteData.Values["p"], Request.QueryString["p"] oder Request.Files["p"]. Die ersten drei Werte sind Zeichenfolgen. Wenn der Parameter [p] nicht vom Typ [string] ist, kann es zu Problemen kommen.
Erstellen wir die folgende neue Aktion:
// Action02
public ContentResult Action02(int age)
{
string texte = string.Format("Contrôleur={0}, Action={1}, âge={2}", RouteData.Values["controller"], RouteData.Values["action"],age);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Die Aktion [Action02] akzeptiert einen Parameter namens [age] vom Typ int. Die abgerufene Zeichenfolge muss in int konvertierbar sein.
Rufen wir die URL [http://localhost:55483/First/Action02?age=21] auf. Wir erhalten die folgende Seite:

Rufen wir die URL [http://localhost:55483/First/Action02?age=21x] auf. Wir erhalten die folgende Seite:

Diesmal haben wir eine Fehlerseite erhalten. Es ist interessant, sich die vom Server in diesem Fall gesendeten HTTP-Header anzusehen:
- Zeile 1: Der Server antwortete mit dem Code [500 Internal Server Error] und sendete eine HTML-Seite (Zeile 3) mit einer Größe von 12.438 Bytes (Zeile 5), um die möglichen Ursachen für diesen Fehler zu erläutern.
Erstellen wir nun die folgende [Action03]-Aktion:
// Action03
public ContentResult Action03(int? age)
{
...
}
[Action03] ist identisch mit [Action02], außer dass wir den Typ des Parameters [age] in int? geändert haben, was „Ganzzahl oder null“ bedeutet.
Rufen wir die URL [http://localhost:55483/First/Action03?age=21x] auf. Wir erhalten die folgende Seite:

ASP.NET MVC konnte [21x] nicht in einen int-Typ konvertieren. Daher wurde dem Parameter [age] der Wert null zugewiesen, wie es sein int?-Typ zulässt. Es ist jedoch möglich festzustellen, ob der Parameter einen Wert aus der Anfrage erhalten hat oder nicht.
Wir erstellen die folgende neue Aktion [Action04]:
// Action04
public ContentResult Action04(int? age)
{
bool valide = ModelState.IsValid;
string texte = string.Format("Contrôleur={0}, Action={1}, âge={2}, valide={3}", RouteData.Values["controller"], RouteData.Values["action"], age, valide);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Wir haben den Typ [int?] beibehalten. Dies ermöglicht es insbesondere, den Parameter [age] in der Anfrage wegzulassen, der dann den Nullwert erhält;
- Zeile 4: Wir prüfen, ob das Aktionsmodell gültig ist. Das Aktionsmodell besteht aus all seinen Parametern, in diesem Fall [age]. Das Modell ist gültig, wenn alle Parameter einen Wert aus der Anfrage erhalten haben oder den Nullwert, sofern der Parametertyp dies zulässt;
- Zeile 5: Wir fügen den Wert der Variablen [valid] zum an den Client gesendeten Text hinzu.
Rufen wir die URL [http://localhost:55483/First/Action04?age=21x] auf. Wir erhalten die folgende Seite:

ASP.NET MVC konnte [21x] nicht in den Typ int konvertieren. Daher wurde dem Parameter [age] der Wert null zugewiesen, wie es sein Typ int? zulässt. Es gab jedoch Konvertierungsfehler, wie der Wert von [valid] zeigt.
Es ist möglich, die Fehlermeldung zu erhalten, die mit einer fehlgeschlagenen Konvertierung verbunden ist. Sehen wir uns die folgende neue Aktion an:
// Action05
public ContentResult Action05(int? age)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("Contrôleur={0}, Action={1}, âge={2}, valide={3}, erreurs={4}", RouteData.Values["controller"], RouteData.Values["action"], age, ModelState.IsValid, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Die neue Funktion befindet sich in Zeile 4. Hier rufen wir eine private Methode [getErrorMessagesFor] auf und übergeben ihr den Modellstatus der Aktion. Sie gibt eine Zeichenfolge zurück, die alle aufgetretenen Fehlermeldungen enthält. Diese Methode sieht wie folgt aus:
private string getErrorMessagesFor(ModelStateDictionary état)
{
List<String> erreurs = new List<String>();
string messages = string.Empty;
if (!état.IsValid)
{
foreach (ModelState modelState in état.Values)
{
foreach (ModelError error in modelState.Errors)
{
erreurs.Add(getErrorMessageFor(error));
}
}
foreach (string message in erreurs)
{
messages += string.Format("[{0}]", message);
}
}
return messages;
}
- Zeile 1: Der tatsächlich an die Methode übergebene [ModelState]-Parameter ist vom Typ [ModelStateDictionary];
- Zeile 3: eine Liste von Fehlermeldungen, die zunächst leer ist;
- Zeile 5: Wir prüfen, ob der als Parameter übergebene Zustand gültig ist. Ist dies nicht der Fall, fassen wir alle Fehlermeldungen zu einer einzigen Zeichenkette zusammen;
- Zeile 7: Der Typ [ModelStateDictionary] verfügt über eine [Values]-Eigenschaft, die eine Sammlung von [ModelState]-Typen darstellt. Pro Modellelement gibt es einen [ModelState]. Zum Beispiel:
- ModelState["age"]: der Modellzustand der Aktion für den Parameter [age],
- ModelState["age"].Errors: die Sammlung der Fehler für diesen Parameter. Die Fehler sind vom Typ [ModelError],
- ModelState["age"].Errors[i].ErrorMessage: die Fehlermeldung (falls vorhanden) für den Parameter [age] des Modells
- ModelState["age"].Errors[i].Exception: die Ausnahme für Fehler Nr. i in der Fehlersammlung für den Parameter [age],
- ModelState["age"].Errors[i].Exception.InnerException: die Ursache dieser Ausnahme,
- ModelState["age"].Errors[i].Exception.InnerException.Message: die Meldung zur Ursache der Ausnahme;
- Zeile 9: Wir durchlaufen die [Errors]-Sammlung eines bestimmten [ModelState];
- Zeile 11: Wir rufen die Fehlermeldung aus einem bestimmten [ModelError] ab und fügen sie der Liste der Fehlermeldungen aus Zeile 3 hinzu;
- Zeilen 14–17: Die Elemente der Fehlermeldungsliste werden zu einer einzigen Zeichenfolge verkettet.
Die Methode [getErrorMessageFor] in Zeile 11 lautet wie folgt:
private string getErrorMessageFor(ModelError error)
{
if (error.ErrorMessage != null && error.ErrorMessage.Trim() != string.Empty)
{
return error.ErrorMessage;
}
if (error.Exception != null && error.Exception.InnerException == null && error.Exception.Message != string.Empty)
{
return error.Exception.Message;
}
if (error.Exception != null && error.Exception.InnerException != null && error.Exception.InnerException.Message != string.Empty)
{
return error.Exception.InnerException.Message;
}
return string.Empty;
}
- Zeile 1: Wir erhalten einen Typ [ModelError], der einen Fehler an einem der Elemente des Aktionsmodells kapselt. Wir rufen die Fehlermeldung an drei verschiedenen Stellen ab:
- in [ModelError].ErrorMessage, Zeilen 3–6;
- in [ModelError].Exception.Message, Zeilen 7–10;
- in [ModelError].Exception.InnerException.Message, Zeilen 11–14;
Beim Testen stellen wir fest, dass die Fehlermeldung je nach Art des Modellelements an diesen drei Stellen zu finden ist. Es muss eine Regel geben, die sicherstellt, dass wir die zu einem Modellelement gehörige Fehlermeldung erhalten können, aber ich kenne sie nicht. Also suche ich an den verschiedenen Stellen, an denen sie zu finden ist, in einer bestimmten Reihenfolge danach. Sobald eine nicht leere Meldung gefunden wird, wird sie zurückgegeben.
Rufen wir die URL [http://localhost:55483/First/Action05?age=21x] auf. Wir erhalten folgende Seite:

4.3. Eine Aktion mit mehreren Parametern
Betrachten wir die folgende neue Aktion:
// Action06
public ContentResult Action06(double? poids, int? age)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, valide={4}, erreurs={5}", RouteData.Values["controller"], RouteData.Values["action"], poids, age, ModelState.IsValid, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Wir haben zwei Parameter [Gewicht] und [Alter].
Die oben beschriebenen Regeln gelten nun für beide Parameter. Hier sind einige Ausführungsbeispiele:


4.4. Verwendung einer Klasse als Aktionsmodell
Definieren wir eine Klasse, die als Modell für eine Aktion dienen soll. Wir legen sie im Ordner [Models] ab [1].
![]() |
Der Code sieht wie folgt aus:
namespace Exemple_02.Models
{
public class ActionModel01
{
public double? Poids { get; set; }
public int? Age { get; set; }
}
}
Unsere Klasse verfügt über die beiden zuvor besprochenen automatischen Eigenschaften [Gewicht] und [Alter]. Diese Klasse dient als Eingabeparameter für die Aktion [Action07]:
// Action07
public ContentResult Action07(ActionModel01 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, valide={4}, erreurs={5}", RouteData.Values["controller"], RouteData.Values["action"], modèle.Poids, modèle.Age, ModelState.IsValid, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Das Aktionsmodell ist eine Instanz vom Typ [ActionModel01].
Schauen wir uns noch einmal die beiden Beispiele von vorhin an:


Beachten Sie, dass bei der Parameterbindung die Groß-/Kleinschreibung keine Rolle spielt. Die Anforderungsparameter lauteten [age] und [weight]. Sie füllten die Eigenschaften [Age] und [Weight] der Klasse [ModelAction01].
Außerdem haben wir bisher [GET]-HTTP-Anfragen verwendet. Zeigen wir, dass sich [POST]-Anfragen genauso verhalten. Dazu verwenden wir erneut die Anwendung [Advanced Rest Client]:
![]() |
- in [1] die angeforderte URL;
- in [2] wird sie über eine POST-Anfrage gesendet;
- in [3] die POST-Parameter.
Wir erhalten dieselbe Antwort wie bei der GET-Anfrage:
![]()
4.5. Aktionsmodell mit Validierungsbeschränkungen – 1
Unter Verwendung des vorherigen Modells:
namespace Exemple_02.Models
{
public class ActionModel01
{
public double? Poids { get; set; }
public int? Age { get; set; }
}
}
Die Parameter [weight] und [age] können in der Anfrage weggelassen werden. In diesem Fall werden die Eigenschaften [Weight] und [Age] auf [null] gesetzt, und es wird kein Fehler gemeldet. Möglicherweise möchten Sie das Modell wie folgt umgestalten:
namespace Exemple_02.Models
{
public class ActionModel01
{
public double Poids { get; set; }
public int Age { get; set; }
}
}
Zeilen 5 und 6: Die Eigenschaften [Weight] und [Age] können nicht mehr den Wert [null] annehmen. Schauen wir uns an, was mit diesem neuen Modell passiert, wenn die Parameter [weight] und [age] in der Anfrage fehlen.

Es traten keine Fehler auf, und die Eigenschaften [Weight] und [Age] behielten ihren Initialisierungswert: 0. ASP.NET MVC:
- Erstellte eine Instanz des Modells mit `new ActionModel01`. Hier wurden den Eigenschaften [Weight] und [Age] der Wert 0 zugewiesen;
- diesen beiden Eigenschaften wurden keine Werte zugewiesen, da es keine Parameter mit diesen Namen gab.
Das erste Modell ermöglicht es uns, das Fehlen eines Parameters zu überprüfen: Die entsprechende Eigenschaft hat dann den Wert [null]. Das zweite Modell lässt dies nicht zu. Es ist möglich, Validierungsbeschränkungen hinzuzufügen, die über den einfachen Typ der Parameter hinausgehen. Diese werden wir nun vorstellen.
Betrachten Sie das folgende neue Aktionsmodell:

using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
public class ActionModel02
{
[Required]
[Range(1, 200)]
public double? Poids { get; set; }
[Required]
[Range(1, 150)]
public int? Age { get; set; }
}
}
- Zeile 6: gibt an, dass das Feld [Weight] erforderlich ist;
- Zeile 7: gibt an, dass das Feld [Weight] im Bereich [1,200] liegen muss;
- Zeile 9: gibt an, dass das Feld [Age] erforderlich ist;
- Zeile 7: gibt an, dass das Feld [Age] im Bereich [1,150] liegen muss;
Die Aktion, die diese Vorlage verwendet, lautet wie folgt [Action08]:
// Action08
public ContentResult Action08(ActionModel02 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, valide={4}, erreurs={5}", RouteData.Values["controller"], RouteData.Values["action"], modèle.Poids, modèle.Age, ModelState.IsValid, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Die Aktion erhält eine Instanz des Modells [ActionModel02];
Führen wir einige Tests durch:




Fehler werden korrekt erkannt. Aktualisieren wir nun das Modell wie folgt:
using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
public class ActionModel02
{
[Required]
[Range(1, 200)]
public double Poids { get; set; }
[Required]
[Range(1, 150)]
public int Age { get; set; }
}
}
Zeilen 8 und 11: Die Eigenschaften dürfen nicht mehr den Wert [null] annehmen. Kompilieren wir den Code und führen wir den Test erneut ohne Parameter aus:

Das Fehlen von Parametern führte dazu, dass die Eigenschaften [Weight] und [Age] den Wert beibehielten, den sie bei der Instanziierung des Modells angenommen hatten: 0. Daraufhin erfolgt die Validierung. Das Attribut [Required] ist somit erfüllt. Wir sehen, dass sich die obige Fehlermeldung auf das Attribut [Range] bezieht. Um das Vorhandensein eines Parameters zu prüfen, muss die zugehörige Eigenschaft daher nullfähig sein, d. h. sie muss den Wert null akzeptieren können.
Kehren wir zum ursprünglichen [ActionModel02]-Modell zurück und betrachten wir eine Aktion, deren Modell aus einer [ActionModel02]-Instanz und einem nullfähigen [DateTime]-Typ besteht:
// Action09
public ContentResult Action09(ActionModel02 modèle, DateTime? date)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("Contrôleur={0}, Action={1}, poids={2}, âge={3}, date={4}, valide={5}, erreurs={6}", RouteData.Values["controller"], RouteData.Values["action"], modèle.Poids, modèle.Age, date, ModelState.IsValid, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Führen wir ein paar Tests durch:

Wir haben keine Parameter an die Aktion übergeben. Die [Required]-Attribute bei den Eigenschaften [Weight] und [Age] haben ihre Aufgabe erfüllt. Das Datum erhielt jedoch den Nullwert, und es wurden keine Fehler gemeldet.
Geben wir nun ungültige Parameter an:

Geben wir nun gültige Werte übergeben:

Sehen wir uns weitere Validierungsbeschränkungen an. Das neue Aktionsmodell sieht wie folgt aus:

using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
public class ActionModel03
{
[Required(ErrorMessage = "Le paramètre email est requis")]
[EmailAddress(ErrorMessage = "Le paramètre email n'a pas un format valide")]
public string Email { get; set; }
[Required(ErrorMessage = "Le paramètre jour est requis")]
[RegularExpression(@"^\d{1,2}$", ErrorMessage = "Le paramètre jour doit avoir 1 ou 2 chiffres")]
public string Jour { get; set; }
[Required(ErrorMessage = "Le paramètre info1 est requis")]
[MaxLength(4, ErrorMessage = "Le paramètre info1 ne peut avoir plus de 4 caractères")]
public string Info1 { get; set; }
[Required(ErrorMessage = "Le paramètre info2 est requis")]
[MinLength(2, ErrorMessage = "Le paramètre info2 ne peut avoir moins de 2 caractères")]
public string Info2 { get; set; }
[Required(ErrorMessage = "Le paramètre info3 est requis")]
[MinLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
[MaxLength(4, ErrorMessage = "Le paramètre info3 doit avoir 4 caractères exactement")]
public string Info3 { get; set; }
}
}
- Zeile 6: das Attribut [Required], diesmal mit einer Fehlermeldung, die wir selbst definieren;
- Zeile 7: Das Attribut [EMailAddress] verlangt, dass das Feld [Email] eine gültige E-Mail-Adresse enthält;
- Zeile 11: Das Attribut [RegularExpression] verlangt, dass das Feld [Day] eine Zeichenfolge aus einer oder zwei Ziffern enthält. Der erste Parameter ist der reguläre Ausdruck, anhand dessen das Feld validiert werden muss;
- Zeile 15: Das Attribut [MaxLength] schreibt vor, dass das Feld [Info1] nicht mehr als 4 Zeichen enthalten darf;
- Zeile 19: Das Attribut [MinLength] schreibt vor, dass das Feld [Info2] mindestens 2 Zeichen enthalten muss;
- Zeilen 23–24: Die kombinierten Attribute [MaxLength] und [MinLength] schreiben vor, dass das Feld [Info3] genau 4 Zeichen enthalten muss;
Die Aktion [Action10] verwendet diese Vorlage:
// Action10
public ContentResult Action10(ActionModel03 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("email={0}, jour={1}, info1={2}, info2={3}, info3={4}, erreurs={5}",
modèle.Email, modèle.Jour, modèle.Info1, modèle.Info2, modèle.Info3, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Führen wir einige Tests mit dieser Aktion durch.
Zunächst ohne Parameter:

Dann mit ungültigen Parametern:

Dann mit gültigen Parametern:

4.6. Aktionsmodell mit Gültigkeitsbeschränkungen – 2
Wir führen zusätzliche Integritätsbeschränkungen ein. Das neue Aktionsmodell wird die folgende Klasse [ActionModel04] sein:
![]() |
using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
public class ActionModel04
{
[Required(ErrorMessage="Le paramètre url est requis")]
[Url(ErrorMessage="URL invalide")]
public string Url { get; set; }
[Required(ErrorMessage = "Le paramètre info1 est requis")]
public string Info1 { get; set; }
[Required(ErrorMessage = "Le paramètre info2 est requis")]
[Compare("Info1",ErrorMessage="Les paramètres info1 et info2 doivent être identiques")]
public string Info2 { get; set; }
[Required(ErrorMessage = "Le paramètre cc est requis")]
[CreditCard(ErrorMessage = "Le paramètre cc n'est pas un n° de carte de crédit valide")]
public string Cc { get; set; }
}
}
- Zeile 8: Das mit einer Anmerkung versehene Feld muss eine gültige URL sein;
- Zeile 13: verlangt, dass die Eigenschaften [Info1] und [Info2] denselben Wert haben;
- Zeile 16: Das kommentierte Feld muss eine gültige Kreditkartennummer sein.
Die Aktion, die diese Vorlage verwendet, sieht wie folgt aus:
// Action11
public ContentResult Action11(ActionModel04 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("URL={0}, Info1={1}, Info2={2}, CC={3},erreurs={4}",
modèle.Url, modèle.Info1, modèle.Info2, modèle.Cc, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Um die Aktion [Action11] zu testen, verwenden wir die Anwendung [Advanced Rest Client]:
![]() |
- in [1] die URL der Aktion [Action11];
- in [2] wird diese URL mit einem POST-Request abgefragt;
- in [3] wählen Sie die Registerkarte [Form];
- in [4] die Werte der vier erwarteten Parameter. Diese Initialisierung ist eine von [ARC] bereitgestellte Funktion. Die tatsächlich gesendeten Parameter können auf der Registerkarte [Raw] [5] eingesehen werden;
![]() |
- in [6] die POST-Parameter.
Auf diese Anfrage erhalten wir die folgende Antwort:

Geben wir nun ungültige Parameter an:

Wir erhalten dann die folgende Antwort:
![]()
4.7. Aktionsmodell mit Gültigkeitsbeschränkungen – 3
Manchmal reichen die verfügbaren Integritätsbeschränkungen nicht aus. In diesem Fall können Sie eigene erstellen. Insbesondere können Sie ein Modell verwenden, das die Schnittstelle [IValidatableObject] implementiert. In diesem Fall fügen Sie Ihre eigenen Modell- -Validierungen zur Methode [Validate] dieser Schnittstelle hinzu. Sehen wir uns ein Beispiel an. Das neue Aktionsmodell wird die folgende Klasse [ActionModel05] sein:
![]() |
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
namespace Exemple_02.Models
{
public class ActionModel05 : IValidatableObject
{
[Required(ErrorMessage = "Le paramètre taux est requis")]
public double? Taux { get; set; }
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
List<ValidationResult> résultats = new List<ValidationResult>();
bool ok = Taux < 4.2 || Taux > 6.7;
if (!ok)
{
résultats.Add(new ValidationResult("Le paramètre taux doit être < 4.2 ou > 6.7", new string[] { "Taux" }));
}
return résultats;
}
}
}
- Zeile 6: Das Modell implementiert die Schnittstelle [IValidatableObject];
- Zeile 10: die Methode [Validate] dieser Schnittstelle. Sie gibt eine Sammlung von Elementen des Typs [ValidationResult] zurück. Dieser Typ kapselt die zu meldenden Fehler;
- Zeile 9: Ein gültiger Wert ist ein Wert < 4,2 oder > 6,7;
- Zeile 12: Wir erstellen eine leere Liste von Elementen vom Typ [ValidationResult];
- Zeile 13: Wir prüfen die Gültigkeit der Eigenschaft [Rate];
- Zeilen 14–17: Ist die Eigenschaft [Rate] ungültig, wird ein Element vom Typ [ValidationResult] zur Ergebnisliste hinzugefügt. Der erste Parameter ist eine Fehlermeldung. Der zweite Parameter, der optional ist, ist eine Sammlung der von diesem Fehler betroffenen Eigenschaften.
Die Aktion, die dieses Modell verwendet, sieht wie folgt aus:
// Action12
public ContentResult Action12(ActionModel05 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("taux={0}, erreurs={1}", modèle.Taux, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Hier ist ein Ausführungsbeispiel:

4.8. Aktionsmodell vom Typ Table oder List
Betrachten Sie die folgende Aktion [Action13]:
// Action13
public ContentResult Action13(string[] data)
{
string strData = "";
if (data != null && data.Length != 0)
{
strData = string.Join(",", data);
}
string texte = string.Format("data=[{0}]", strData);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Das Aktionsmodell besteht aus einem Array vom Typ [string]. Es ermöglicht uns, einen Parameter namens [data] abzurufen, der in den Anfrageparametern mehrfach vorkommen kann, beispielsweise in [?data=data1&data=data2&data=data3]. Die verschiedenen [data]-Parameter in der Anfrage füllen das [data]-Array im Aktionsmodell. Dieses Szenario tritt bei Dropdown-Listen auf. Der Browser sendet dann die verschiedenen vom Benutzer ausgewählten Werte, alle mit demselben Parameternamen.
Hier ein Beispiel:

Das Modell kann auch eine Liste sein:
// Action14
public ContentResult Action14(List<int> data)
{
string erreurs = getErrorMessagesFor(ModelState);
string strData = "";
if (data != null && data.Count != 0)
{
strData = string.Join(",", data);
}
string texte = string.Format("data=[{0}], erreurs=[{1}]", strData, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Das Modell ist hier eine Liste von Ganzzahlen (Zeile 2). Hier ist der erste Durchlauf:

und ein zweiter:

4.9. Filtern eines Aktionsmodells
Manchmal haben wir ein Modell, möchten aber, dass nur bestimmte Elemente des Modells durch die HTTP-Anfrage initialisiert werden. Betrachten Sie das folgende Aktionsmodell [ActionModel06]:
using System.ComponentModel.DataAnnotations;
using System.Web.Mvc;
namespace Exemple_02.Models
{
[Bind(Exclude = "Info2")]
public class ActionModel06
{
[Required(ErrorMessage = "Le paramètre [info1] est requis")]
public string Info1 { get; set; }
public string Info2 { get; set; }
}
}
- Zeilen 9–10: Der Parameter [info1] ist erforderlich;
- Zeile 6: Der Parameter [info2] in Zeile 12 wird von der Bindung der HTTP-Anfrage an ihr Modell ausgeschlossen.
Die Aktion wird wie folgt aussehen [Action15]:
// Action15
public ContentResult Action15(ActionModel06 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("valide={0}, info1={1}, info2={2}, erreurs={3}", ModelState.IsValid, modèle.Info1, modèle.Info2, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Hier ist ein Ausführungsbeispiel:
![]() |
- in [1]: Wir übergeben den Parameter [info2] in der URL;
- in [2]: Die Eigenschaft [Info2] des Aktionsmodells bleibt leer.
4.10. Erweiterung des Datenbindungsmodells
Betrachten wir noch einmal die Ausführungsarchitektur einer Aktion:
![]() |
Die Aktionsklasse wird zu Beginn der Client-Anfrage instanziiert und am Ende derselben zerstört. Daher kann sie nicht zum Speichern von Daten zwischen Anfragen verwendet werden, selbst wenn sie wiederholt aufgerufen wird. Möglicherweise möchten Sie zwei Arten von Daten speichern:
- Daten, die von allen Benutzern der Webanwendung gemeinsam genutzt werden. Dabei handelt es sich in der Regel um schreibgeschützte Daten. Zur Umsetzung dieser gemeinsamen Datennutzung werden drei Dateien verwendet:
- [Web.Config]: die Anwendungskonfigurationsdatei
- [Global.asax, Global.asax.cs]: Ermöglichen es Ihnen, eine Klasse zu definieren, die als globale Anwendungsklasse bezeichnet wird und deren Lebensdauer der der Anwendung entspricht, sowie Handler für bestimmte Ereignisse derselben Anwendung.
Mit der globalen Anwendungsklasse können Sie Daten definieren, die für alle Anfragen aller Benutzer verfügbar sind.
- Daten, die über Anfragen desselben Clients hinweg gemeinsam genutzt werden. Diese Daten werden in einem Objekt namens „Session“ gespeichert. Wir bezeichnen dies als Client-Session, um den Speicher des Clients zu bezeichnen. Alle Anfragen eines Clients haben Zugriff auf diese Session. Sie können dort Informationen speichern und lesen.
![]() |
Oben zeigen wir die Arten von Speicher, auf die eine Aktion Zugriff hat:
- den Speicher der Anwendung, der größtenteils schreibgeschützte Daten enthält und für alle Benutzer zugänglich ist;
- den Speicher eines bestimmten Benutzers oder die Sitzung, die Lese-/Schreibdaten enthält und für aufeinanderfolgende Anfragen desselben Benutzers zugänglich ist;
- Nicht oben dargestellt ist der Anforderungsspeicher oder Anforderungskontext. Die Anforderung eines Benutzers kann von mehreren aufeinanderfolgenden Aktionen verarbeitet werden. Der Anforderungskontext ermöglicht es Aktion 1, Informationen an Aktion 2 weiterzugeben.
Sehen wir uns ein erstes Beispiel an, das diese verschiedenen Speichertypen veranschaulicht:
Zunächst ändern wir die Datei [Web.config] des Projekts [Example-02] wie folgt:
<appSettings>
<add key="webpages:Version" value="2.0.0.0" />
...
<add key="infoAppli1" value="infoAppli1"/>
</appSettings>
Wir fügen Zeile 4 hinzu, die den Wert [infoAppli1] mit dem Schlüssel [infoAppli1] verknüpft. Dies sind unsere Daten im [Application]-Bereich: Sie sind für alle Anfragen aller Benutzer zugänglich.
Als Nächstes ändern wir die Methode [Application_Start] in der Datei [Global.asax]. Diese Methode wird einmalig beim Start der Anwendung ausgeführt. Hier müssen wir die Datei [Web.config] verwenden:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// intialisation application
Application["infoAppli1"] = ConfigurationManager.AppSettings["infoAppli1"];
}
Wir fügen Zeile 10 hinzu. Diese erfüllt zwei Aufgaben:
- Sie ruft den Wert des Schlüssels [infoAppli1] aus der Datei [Web.config] mithilfe der Klasse [System.Configuration.ConfigurationManager] ab;
- sie speichert ihn im Wörterbuch [HttpApplication.Application], verknüpft mit dem Schlüssel [infoAppli1]. Alle Aktionen haben Zugriff auf dieses Wörterbuch.
In derselben [Global.asax]-Datei fügen wir die folgende [Session_Start]-Methode hinzu:
protected void Session_Start()
{
// initialisation compteur
Session["compteur"] = 0;
}
Die Methode [Session_Start] wird für jeden neuen Benutzer ausgeführt. Was ist ein neuer Benutzer? Ein Benutzer wird durch ein Session-Token „verfolgt“. Dieses Token wird
- vom Webserver erstellt und dem neuen Benutzer in den HTTP-Headern der ersten an ihn gesendeten Antwort übermittelt;
- wird vom Browser des Benutzers bei jeder neuen Anfrage zurückgesendet. Dadurch kann der Server den Benutzer erkennen und einen Speicherbereich für ihn verwalten, der als Benutzersitzung bezeichnet wird.
Der Webserver erkennt, dass es sich um einen neuen Benutzer handelt, wenn dieser kein Session-Token sendet. Der Server erstellt dann eines für ihn.
In Zeile 4 oben setzen wir einen Zähler in der Sitzung des Benutzers, der mit jeder Anfrage dieses Benutzers erhöht wird. Dies veranschaulicht den einem Benutzer zugeordneten Speicher. Die Klasse [Session] wird wie ein Wörterbuch verwendet (Zeile 4).
Anschließend schreiben wir die folgende [Action16]-Aktion:
// Action16
public ContentResult Action16()
{
// on récupère le contexte de la requête HTTP
HttpContextBase contexte = ControllerContext.HttpContext;
// on récupère les infos de portée Application
string infoAppli1 = contexte.Application["infoAppli1"] as string;
// et celles de portée Session
int? compteur = contexte.Session["compteur"] as int?;
compteur++;
contexte.Session["compteur"] = compteur;
// la réponse au client
string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 5: Wir rufen den Kontext der aktuell verarbeiteten HTTP-Anfrage ab. Dieser Kontext ermöglicht uns den Zugriff auf Daten in den Bereichen [Application] und [Session];
- Zeile 7: Wir rufen die Informationen des [Application]-Bereichs ab;
- Zeile 9: Wir rufen den Zähler aus der Sitzung ab;
- Zeilen 10–11: Er wird erhöht und dann wieder in der Sitzung gespeichert;
- Zeilen 13–14: Beide Informationen werden an den Client gesendet.
Hier sind einige Beispiele für die Ausführung:
[Action16] wird einmal aufgerufen [1], dann wird die Seite zweimal aktualisiert [F5] [2]:
![]() |
In [2] hat der Client insgesamt drei Anfragen gestellt. Bei jeder Anfrage konnte er den durch die vorherige Anfrage aktualisierten Zähler abrufen.
Um einen zweiten Benutzer zu simulieren, verwenden wir einen zweiten Browser, um dieselbe URL abzufragen:
![]() |
In [3] ruft der zweite Benutzer erfolgreich dieselben Informationen im [Application]-Bereich ab, verfügt jedoch über einen eigenen Zähler im [Session]-Bereich.
Kehren wir zum Code für die Aktion [Action16] zurück:
// Action16
public ContentResult Action16()
{
// on récupère le contexte de la requête HTTP
HttpContextBase contexte = ControllerContext.HttpContext;
// on récupère les infos de portée Application
string infoAppli1 = contexte.Application["infoAppli1"] as string;
// et celles de portée Session
int? compteur = contexte.Session["compteur"] as int?;
compteur++;
contexte.Session["compteur"] = compteur;
// la réponse au client
string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
return Content(texte, "text/plain", Encoding.UTF8);
}
Eines der Ziele des ASP.NET MVC-Frameworks ist es, Controller und Aktionen isoliert und ohne Webserver testbar zu machen. Wie jedoch in Zeile 5 zu sehen ist, wird der HTTP-Anforderungskontext benötigt, um Daten aus den Bereichen [Application] und [Session] abzurufen. Wir schlagen vor, eine neue Aktion [Action17] zu erstellen, die Daten aus den Bereichen [Application] und [Session] als Parameter erhält:
// Action17
public ContentResult Action17(ApplicationModel applicationData, SessionModel sessionData)
{
// retrieve range info Application
string infoAppli1 = applicationData.InfoAppli1;
// and Session range
int compteur = sessionData.Compteur++;
// customer response
string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
return Content(texte, "text/plain", Encoding.UTF8);
}
Der Code ist nun unabhängig von der HTTP-Anfrage. Er kann daher unabhängig von einem Webserver getestet werden.
Schauen wir uns an, wie das funktioniert. Zunächst müssen wir die Klassen [ApplicationModel] und [SessionModel] erstellen, die jeweils die Daten des [Application]- und [Session]-Scopes kapseln. Sie sehen wie folgt aus:
![]() |
namespace Exemple_02.Models
{
public class ApplicationModel
{
public string InfoAppli1 { get; set; }
}
}
namespace Exemple_02.Models
{
public class SessionModel
{
public int Compteur { get; set; }
public SessionModel()
{
Compteur = 0;
}
}
}
Als Nächstes müssen wir die Methoden [Application_Start] und [Session_Start] in der Datei [Global.asax] ändern:
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
WebApiConfig.Register(GlobalConfiguration.Configuration);
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
// intialisation application - case 1
Application["infoAppli1"] = ConfigurationManager.AppSettings["infoAppli1"];
// intialisation application - case 2
ApplicationModel data=new ApplicationModel();
data.InfoAppli1=ConfigurationManager.AppSettings["infoAppli1"];
Application["data"] = data;
}
protected void Session_Start()
{
// counter initialization - case 1
Session["compteur"] = 0;
// counter initialization - case 2
Session["data"] = new SessionModel();
}
}
- Zeile 14: Eine Instanz von [ApplicationModel] wird erstellt;
- Zeile 15: Sie wird initialisiert;
- Zeile 16: und in das [Application]-Wörterbuch abgelegt, verknüpft mit dem Schlüssel [data]. [Application] ist eine Eigenschaft der Klasse [HttpApplication] aus Zeile 1;
- Zeile 24: Eine Instanz von [SessionModel] wird erstellt und in das [Session]-Wörterbuch aufgenommen, verknüpft mit dem Schlüssel [data]. [Session] ist eine Eigenschaft der Klasse [HttpApplication] aus Zeile 1;
Basierend auf dem, was wir bisher gesehen haben, lautet die Signatur
public ContentResult Action17(ApplicationModel applicationData, SessionModel sessionData)
bedeutet, dass die von der Aktion verarbeitete HTTP-Anfrage Parameter mit den Namen [applicationData] und [sessionData] enthalten muss. Dies wird jedoch nicht der Fall sein. Wir müssen ein neues Datenbindungsmodell erstellen, damit, wenn eine Aktion einen Typ als Parameter erhält:
- [ApplicationModel] erhält, ihr die Daten mit dem Geltungsbereich [Application] und dem Schlüssel [data] bereitgestellt werden;
- [SessionModel], die Daten mit dem Geltungsbereich [Session] und dem Schlüssel [data] bereitgestellt werden.
Dazu müssen wir Klassen erstellen, die die Schnittstelle [IModelBinder] implementieren.
Zunächst erstellen wir im Projekt [Example-02] einen Ordner [Infrastructure]:

Darin erstellen wir die folgende [ApplicationModelBinder]-Klasse:
using System.Web.Mvc;
namespace Exemple_02.Infrastructure
{
public class ApplicationModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// render scope data [Application]
return controllerContext.RequestContext.HttpContext.Application["data"];
}
}
}
- Zeile 5: Die Klasse implementiert die Schnittstelle [IModelBinder]. Um den Code zu verstehen, muss man wissen, dass er jedes Mal aufgerufen wird, wenn eine Aktion einen Parameter vom Typ [ApplicationModel] hat. Diese Bindungs e [ApplicationModel] --> [ApplicationModelBinder] wird beim Start der Anwendung in der Methode [Application_Start] von [Global.asax] hergestellt;
- Zeile 7: Die einzige Methode der Schnittstelle [IModelBinder];
- Zeile 7: Der Parameter [ControllerContext] ermöglicht uns den Zugriff auf die aktuell verarbeitete HTTP-Anfrage;
- Zeile 7: Der Parameter vom Typ [ModelBindingContext] ermöglicht uns den Zugriff auf Informationen über das zu erstellende Modell, in diesem Fall den Typ [ApplicationModel];
- Zeile 7: Das Ergebnis von [BindModel] ist das Objekt, das dem Parameter bound zugewiesen wird, hier ein Parameter vom Typ [ApplicationModel];
- Zeile 10: Wir geben einfach das Objekt mit dem Bereich [Application] und dem Schlüssel [data] zurück.
Die Klasse [ SessionModelBinder] folgt dem gleichen Muster:
using System.Web.Mvc;
namespace Exemple_02.Infrastructure
{
public class SessionModelBinder : IModelBinder
{
public object BindModel(ControllerContext controllerContext, ModelBindingContext bindingContext)
{
// render scope data [Session]
return controllerContext.HttpContext.Session["data"];
}
}
}
Nun muss nur noch jedes [XModel]-Modell seinem [XModelBinder] zugeordnet werden. Dies geschieht in der [Application_Start]-Methode von [Global.asax]:
protected void Application_Start()
{
....
// intialisation application - case 2
ApplicationModel data=new ApplicationModel();
data.InfoAppli1=ConfigurationManager.AppSettings["infoAppli1"];
Application["data"] = data;
// model binders
ModelBinders.Binders.Add(typeof(ApplicationModel), new ApplicationModelBinder());
ModelBinders.Binders.Add(typeof(SessionModel), new SessionModelBinder());
}
- Zeile 9: Wenn eine Aktion einen Parameter vom Typ [ApplicationModel] hat, wird die Methode [ApplicationModelBinder.Bind] aufgerufen. Wir wissen, dass sie die Daten im [Application]-Bereich zurückgibt, die mit dem Schlüssel [data] verknüpft sind;
- Zeile 10: Das Gleiche gilt für den Typ [SessionModel].
Kehren wir zu unserer Aktion [Action17] zurück:
// Action17
public ContentResult Action17(ApplicationModel applicationData, SessionModel sessionData)
{
// retrieve range info Application
string infoAppli1 = applicationData.InfoAppli1;
// and Session range
sessionData.Compteur++;
int compteur = sessionData.Compteur;
// customer response
string texte = string.Format("infoAppli1={0}, compteur={1}", infoAppli1, compteur);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Wenn [Action17] aufgerufen wird, erhält sie
- ersten Parameter: die mit dem Schlüssel [data] verknüpften Daten des [Application]-Bereichs,
- zweiten Parameter: die mit dem Schlüssel [data] verknüpften Daten des [Session]-Bereichs;
Diese beiden Datensätze können beliebig komplex sein und zum einen alle Daten aus dem [Application]-Bereich und zum anderen alle Daten aus dem [Session]-Bereich umfassen.
Hier ist ein Beispiel für die Ausführung der Aktion [Action17]:

4.11. Späte Bindung des Aktionsmodells
Wir haben die folgende [Action12] geschrieben:
// Action12
public ContentResult Action12(ActionModel05 modèle)
{
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("taux={0}, erreurs={1}", modèle.Taux, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
Hinter den Kulissen erstellt ASP.NET MVC:
- erstellt eine Instanz vom Typ [ActionModel05] mithilfe ihres parameterlosen Konstruktors;
- initialisiert diese mit Anforderungsinformationen, die denselben Namen (Groß-/Kleinschreibung wird nicht berücksichtigt) wie eine der Eigenschaften von [ActionModel05] haben.
Manchmal entspricht dieses Verhalten nicht unseren Vorstellungen. Dies ist insbesondere dann der Fall, wenn wir einen bestimmten Konstruktor des Aktionsmodells verwenden möchten. Wir können dann wie folgt vorgehen:
// Action18
public ContentResult Action18()
{
ActionModel05 modèle = new ActionModel05();
TryUpdateModel(modèle);
string erreurs = getErrorMessagesFor(ModelState);
string texte = string.Format("taux={0}, erreurs={1}", modèle.Taux, erreurs);
return Content(texte, "text/plain", Encoding.UTF8);
}
- Zeile 2: Die Aktion erhält keine Parameter mehr. Daher findet keine automatische Datenbindung mehr statt;
- Zeile 4: Wir erstellen selbst eine Instanz des Aktionsmodells. Hier könnten wir einen anderen Konstruktor verwenden;
- Zeile 5: Wir initialisieren das Modell mit den Anforderungsinformationen. ASP.NET MVC übernimmt diese Aufgabe. Es geht dabei genauso vor, wie wenn das Modell ein Parameter gewesen wäre;
- Zeile 6: Wir befinden uns nun in derselben Situation wie bei der Aktion [Action12].
Hier ist ein Beispiel für die Ausführung:

4.12. Fazit
Kehren wir zur Architektur einer ASP.NET MVC-Anwendung zurück:
![]() |
Eine Anfrage [1] enthält verschiedene Informationen, die ASP.NET MVC [2a] der Aktion in Form eines Modells präsentiert, das wir als Aktionsmodell bezeichnet haben.
![]() |
- Die HTTP-Anfrage des Clients trifft bei [1] ein;
- bei [2] werden die in der Anfrage enthaltenen Informationen in ein Aktionsmodell [3] umgewandelt;
- In [4] generiert die Aktion auf der Grundlage dieses Modells eine Antwort. Diese Antwort besteht aus zwei Komponenten: einer Ansicht V [6] und dem Modell M für diese Ansicht [5];
- die Ansicht V [6] verwendet ihr Modell M [5], um die für den Client bestimmte HTTP-Antwort zu generieren.
Im MVC-Modell ist die Aktion [4] Teil des C (Controllers), das Ansichtsmodell [5] ist das M und die Ansicht [6] ist das V.
In diesem Kapitel wurden die Mechanismen untersucht, die die von der Anfrage übertragenen Informationen – bei denen es sich naturgemäß um Zeichenfolgen handelt – mit dem Modell der Aktion verknüpfen, bei dem es sich um eine Klasse mit Eigenschaften verschiedener Typen handeln kann. Wir haben außerdem gesehen, dass es möglich ist, das der Aktion vorgelegte Modell zu validieren. Schließlich haben wir gesehen, wie dieses Modell erweitert werden kann, um Daten aus den Bereichen [Session] und [Application] einzubeziehen.
Wir konzentrieren uns nun auf das Ende der Anforderungsverarbeitungskette [1]: die Erstellung der Ansicht [6] und ihres Modells [5]. Diese beiden Elemente werden von der Aktion [4] erzeugt.




















