Inhaltsverzeichnis         

10 MVC 4.0

10.1.1 Wichtige Quellen

10.1.2 Beispielwebseite

Die Übungen sind online lauffähig zu begutachten unter:

http://mk-net-prg.azurewebsites.net/

10.1.3 Das Model-View-Controler- Entwurfsmuster für Webanwendungen

Das MVC- Entwurfsmuster wurde in den Entwicklungslabors der Firma Xerox in den 1980-er Jahren erfunden. Angewendet auf die Entwicklung von Programmen mit graphischer Benutzeroberfläche führt es zu einer strikten Aufteilung in die zu verarbeitenden Daten (Modelle), die Geschäftslogik (Controller) und die IO-Masken der GUI (Views).

Im klassischen ASP.NET WebForm- Modell ist keine strenge Trennung wie in MVC vorgegeben. Nicht selten landet die Implementierung der gesamte Geschäftslogik in den Eventhandlern der Websteuerelemente. Die Anpassung/Änderung der GUI erfordert damit zwangsläufig eine Neuorganisation der Implementierung der Geschäftslogik. MVC soll dieses Problem von Anfang an ausschließen.


Anmerkung:

Auch das klassische WebForm- Modell ermöglicht es, die Geschäftslogik getrennt von der Logik der IO- Masken der Gui zu implementieren. Z.B. indem die gesamte Geschäftslogik unabhängig von der Webanwendung in einer separaten Klassenbibliothek zuerst implementiert und getestet wird.

10.1.3.1.1 Verarbeitung eines Http- Requests mittels klassischer Webform:


Die klassische WebForm integriert in den Seitenaufbau die Verarbeitung der Anwendungslogik durch serverseitige Eventhandler. Diese werden in einer Code- Behind- Datei implementiert.

10.1.3.1.2 WebForms als UI- Schnittelle zu Geschäftsobjekten

Aus Sicht der Geschäftslogik kann man klassische Webforms als graphische Benutzerschnittstelle von Geschäftsobjekten betrachten. In einfachen Fällen ist dabei die Webform das Geschäftsobjekt selbst. Im Folgenden eine Taschenrechneranwendung. Die Webform mit ihrem Markup und Eventhandlern stellt das komplette Geschäftsobjekt "Taschenrechner" dar:




Markup in der Page.aspx

<%@ Page Title="Start\UEs\01\Formular" Language="C#" MasterPageFile="~/UEs/Masters/DMSMaster.Master"
    AutoEventWireup="true" CodeBehind="SummeAusZwei.aspx.cs" Inherits="WebDms2.AspBasics.SummeAusZwei"
    EnableTheming="true" Theme="Round" Trace="false" %>

<%-- In Page wird durch das Attribut Trace="true" eine detailierte Protokollierung des Seitenaufbaus eingeschaltet (für Debugzwecke)  --%>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
    <style type="text/css">
        #divServer {
            margin-bottom: 0px;
        }
    </style>
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">

    <h1>Summe aus zwei</h1>

    <table class="tabStyle">
        <tr>
            <td class="tabItem">A = 
            </td>
            <td class="tabItemL">
                <asp:TextBox ID="tbxA" runat="server" Text="" />
            </td>
            <td>&nbsp;
            </td>
        </tr>

        <tr>
            <td class="tabItem">B = 
            </td>
            <td>
                <asp:TextBox ID="tbxB" runat="server" Text="" />
            </td>
            <td>&nbsp;
            </td>
        </tr>
        <tr>
            <td class="tabItem" colspan="3">
                <asp:Button ID="btnAdd" runat="server" Text="+" OnClick="btnOp_Click" CommandName="op"
                    CommandArgument="add" />
                &nbsp;
                <asp:Button ID="btnSub" runat="server" Text="-" OnClick="btnOp_Click" CommandName="op"
                    CommandArgument="sub" />
                &nbsp;
                <asp:Button ID="btnMul" runat="server" Text="x" OnClick="btnOp_Click" CommandName="op"
                    CommandArgument="mul" />
                &nbsp;
                <asp:Button ID="btnDiv" runat="server" Text="/" OnClick="btnOp_Click" CommandName="op"
                    CommandArgument="div" />
                &nbsp;
                <asp:Button ID="btnPow" runat="server" Text="^" CommandName="op"
                    CommandArgument="pow" />
            </td>
        </tr>
        <tr>
            <td class="tabItem">Res = 
            </td>
            <td runat="server" id="tabCellResult" class="tabItemL">
                <asp:Label ID="lblResult" runat="server" Text="0" />
            </td>
            <td>&nbsp;</td>
        </tr>
    </table>

</asp:Content>

Eventhandler in der Page.csw

namespace WebDms2.AspBasics
{
    public partial class SummeAusZwei : System.Web.UI.Page
    {
        protected void Page_Init(object sender, EventArgs e)
        {
            //btnPow.Click += new EventHandler(btnPow_Click);
        }

        protected void Page_Load(object sender, EventArgs e)
        {
            Trace.Write("Bin in Page_Load");
            Debug.WriteLine("Bin in PageLoad (System.Diagnostics)");

        }


        protected void btnOp_Click(object sender, EventArgs e)
        {
            Trace.Write("Bin in btnOp_Click");
            Debug.WriteLine("Bin in btnOp_Click (System.Diagnostics)");

            var btn = (Button)sender;
            Debug.Assert(btn.CommandName=="op", "SummeAusZwei.btnOp_Click: Der Button startet keine arithmetische Operation!");

            // Einlesen der Operanden
            double a = double.Parse(tbxA.Text);
            double b = double.Parse(tbxB.Text);

            double result = 0;
            string opSymbol = "";
            switch (btn.CommandArgument)
            {
                case "add":
                    opSymbol = "+";
                    result = a + b;
                    break;
                case "sub":
                    opSymbol = "-";
                    result = a - b;
                    break;
                case "mul":
                    opSymbol = "*";
                    result = a * b;
                    break;
                case "div":
                    opSymbol = "/";
                    result = a / b;
                    break;
                case "pow":
                    break;
                default:
                    throw new Exception("unbekannte Operation");
            }

            var resultBackColor = result > 0 ? "0x3399FF" : "red";
            tabCellResult.BgColor = resultBackColor;
            lblResult.Text = string.Format("A {0} B = {1:N}", opSymbol, result.ToString());
        }

    }
}
10.1.3.1.2.1 Vorteil WebForms:
10.1.3.1.2.2 Nachteil WebForms:
10.1.3.1.3 Verarbeitung eines Requests in einer MVC- WebApp






Model View Controler ist ein sog. Entwurfsmuster aus der objektorientierten Programmierung. Dabei wird ein Request wie folgt verarbeitet:

  1. Jedem Url, der vom Request abgerufen wird, ist eine eine Methode, genannt Action einer Controllerklasse zugeordnet. Die Zuordnung wird Route genannt. Zum jedem Request wird ein Controler instanziiert.

  2. Die Anwendungslogik ist in die Methoden (Actions) einer Controler- Klasse verlagert. Bei der Ausführung der Action werden die Ergebnisse in einer Datenstruktur, genannt Modell gepeichert. Am Ende wählt die Action eine View aus und übergibt diese zusammen mit dem Modell an die ASP.NET Laufzeit.

  3. Die Laufzeit instanziiert die View und setzt in ihr die Eigenschaft Modell auf das zuvor übergebene Modellobjekt. Dann wird die Render- Methode der View ausgeführt, wobei serverseitiger Code genutzt wird, um die Daten aus dem Modell in die Seite einzubetten. Das Ergebnis ist HTML und Javascript- Code, der an den Browser gesendet wird.

10.1.3.1.4 MVC als graphische Oberfläche zur Steuerung von Geschäftsabläufen (Workflows)

In MVC werden über Formulare aufzurufende Aktionen aus einem Geschäftsablauf parametriert, und die Aktionen schließlich aufgerufen. Die Aktionen generieren und konfigurieren wiederum Geschäftsobjekte, deren Zustände mittels Views präsentiert werden.




Komplexe Geschäftsabläufe sind durch eine MVC Anwendung nahezu 1:1 abbildbar. Im folgenden der Workflow in einer Taschenrechner- Anwendung:

Modelle als Geschäftsobjekte

namespace MVC.Basics.Models.Exc01_MVCPattern
{
  
    public class BinOpModel : BasicsModelBase
    {
        private int myPrivate = 0;
        protected int myProtected = 0;

        public BinOpModel()
        {
            Operator = Operators.add;
        }

        public enum Operators
        {
            add, sub, mul, div
        }
        /// <summary>
        /// Operand A
        /// </summary>
        public double A { get; set; }

        /// <summary>
        /// Operand B
        /// </summary>
        public double B { get; set; }

        public Operators Operator { get; set; }

        /// <summary>
        /// Ergebnis der Operation
        /// </summary>
        public double Result { get; set; }


        /// <summary>
        /// Hilsfunktion für die Views: konnte bis dato noch nicht in die Extension- Klasse ausgelagert werden.
        /// </summary>
        public string OperatorTxt
        {
            get
            {
                return Operator.ToString();
            }

            set
            {
                Operator = (Operators)Enum.Parse(typeof(Operators), value);
            }
        }       

    }
}

Controller mit Actions aus einem Workflow

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

using MVC.Basics.Models.Exc01_MVCPattern;

namespace MVC.Basics.Controllers
{
    public class Exc01_MVCPatternController : BasicsBaseController
    {        
        /// <summary>
        /// Behandlung von Ausnahmen erfolgen durch überschreiben eines Eventhandlers der Controllerklasse
        /// </summary>
        /// <param name="filterContext"></param>
        protected override void OnException(ExceptionContext filterContext)
        {
            filterContext.ExceptionHandled = true;
            View("Error").ExecuteResult(ControllerContext);
        }

        /// <summary>
        /// Action, die den Startpunkt eines Workflows darstellt, den die Actions des Controllers abbilden
        /// </summary>
        /// <returns></returns>
        public ActionResult Index()
        {
            BinOpModel Model = RecordActionAndCreateModel<Models.Exc01_MVCPattern.BinOpModel>(this, "Index", "WF_Start", "Exc01", CreateRoutInfo(Controllers.Workflows.Exc01_BinOp.ToString()));
                
            return View("BinOpView", Model);
        }

        /// <summary>
        /// Action, durch die der Workflow des Aufbaus und der Berechnung eines einfachen mathematischen Ausdrucks im Controller abgebildet wird
        /// </summary>
        /// <param name="binOpModel"></param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult BinOp(BinOpModel binOpModel)
        {
            if (ModelState.IsValid)
            {
                Basics.Models.Exc01_MVCPattern.BinOpComputer.Exec(binOpModel);                 
            } 
            return View("BinOpView", binOpModel);
        }

    }
}

View mit Servercode in Razor(C#)

@using Props = MVC.Basics.Properties

@model MVC.Basics.Models.Exc01_MVCPattern.BinOpModel

@{
    ViewBag.Title = "BinOpView";
    Layout = "~/Views/Shared/_LayoutOhneNavigation.cshtml";

    // Abschalten der Clientseitigen Validierung
    HtmlHelper.ClientValidationEnabled = false;
}

@*  Ein Kommentar in Razor beginnt mit Klammeraffe*, und endet mit *Klammeraffe 
    Razor ist eine innovativer Parser auf der Serverseite, durch welchen die Integration
    serverseitiger Logik ins HTML- Markup mit einer besonders schlanken Syntax beginnt. 
    Dabei gilt ein einfaches Prinzip: jeder Serverseitig auszuführende Ausdruck beginnt mit einem @.
*@

@using MVC.Basics.Models.Exc01_MVCPattern;

@using (Html.BeginForm("BinOp", "Exc01_MVCPattern"))
{
    <div>
        <h2>Binäre Arithmetik</h2>
        @Html.ValidationSummary(true)

        <section>
            <div>
                @Html.LabelFor(model => model.A, "A=", new { Class = "mathOpLabel" })
                @Html.EditorFor(model => model.A, new { Class = "mathOpField" })
                
                @Html.DropDownListFor(model => model.OperatorTxt, Model.UIOperatorSelectionList(), new { Class = "mathOperator" })                

                @Html.LabelFor(model => model.B, "B=", new { Class = "mathOpLabel" })
                @Html.EditorFor(model => model.B, new { Class = "mathOpField" })

                <input type="submit" value="=" />

                @Html.DisplayFor(model => model.Result, new { Class = "mathOpField" })
            </div>
        </section>
    </div>
}

@* Benannter Abschnitt, der in der Layoutseite im Kopf eingeblendet wird, und zusätzliche Styles enthält *@
@section Styles {
    @Styles.Render("~/Content/themes/Exc01")
}

@* Bennanter Abschnitt, der im Fuss der Layoutseite eingeblendet wird, und die Skripte enthält *@
@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/bundles/jqueryui")
    <script>
        // Hier wird auf dem DOM mittels jQuery eine Operation ausgeführt, welche bewirkt,
        // das alle title- Attribute als Tooltips dienen
        //jQuery(document).tooltip();
    </script>
}

Vorteile MVC

Nachteile MVC

10.1.4 Kompatibilität zwischen ASP.NET WebForms und MVC

Webforms und MVC bauen auf den gleichen grundlegenden ASP.NET Bibliotheken auf. So sind viele Klassen in beiden Architekturen einsetzbar. Jedoch wird weder der ViewState, noch Postback- Ereignisse unterstützt. Steuerelemente, die auf ViewState und PostBack aufbauen wie Buttons, GridView, Repeater und Datalist sind so nicht ohne Einschränkungen einstetzbar in MVC einsetzbar.

Bekannte Klassen der ASP.NET Webfrom Bibliothek

Verfügbar in MVC

Anwendungszustand

Ja, Eigenschaft von Controller

Sitzungszustand

Ja, Eigenschaft von Controller

Viewstate

Nein

ASPX- Steuerelemente, die auch bei abgeschalteten Viewstate funktionieren wie Label, Textbox, DropDown

Ja, in aspx- Views

ASPX-Steuerelemente, die Viewstate benötigen, oder PostBack Events benötigen wie Buttons

Nein



10.1.5 Struktur einer MVC- Anwendung

Im Folgenden ein Ausschnitt aus einem MVC Beispielprojekt



Erläuterungen von oben nach unten:



App_Start

Hier werden Klassen definiert, deren Instanzen für die gesamte Anwendung sichtbar sind. Dazu zählen z.B. Tabellen mit Routendefinitionen (RouteConfig.cs)

Controllers

Hier werden die Controllerklassen miit ihren Action- Methoden definiert

Models

Hier werden die Modelle definiert, in denen die Controler Ergebnisse an die Views übergeben oder von diesen Eingaben entgegennehmen.

Views

Hier werden die View- Klassen definiert.

Views/Shared

Hier werden Masterseiten für die Views definiert


10.1.5.1.1 Pfade

Um die Installation von Webseiten auf Servern zu vereinfachen, ist es notwendig, Dateipfade auf Ressourcen in Serversteuerelementen an den jeweiligen Installationsort der Webseite anzupassen. ASP.NET bietet dazu den RootPath- Operator, welcher durch die Tilde(~) ausgedrückt wird.

  ~                 // RootPath Operator: verweist auf Installationspfad der aktuellen Webanwendung
~/Images/My.gif   // Absoluter Verweis auf die Gif Datei My.Gif im Image- Verzeichnis 
                  // unterhalb des Installationsverzeichnisses
/Images/My.gif    // Relativer Verweis auf die Gif Datei My.gif im Image- Verzeichnis
                  // unterhalb des Speicherortes der aktuell aktiven Webform
10.1.5.1.2 Namenskonventionen für Controller, Actions und Views

Die Quelltextdateien von Controllern werden sind nach folgendem Schema zu bezeichnen:

<Name>Controller.cs

Wird z.B. ein Controller namens Home implementiert, dann ist im Ordner Controllers eine Quelltextdatei

HomeController.cs

abzulegen.

Liefert eine Action immer genau eine View zurück, dann sollte Action und View gleich benannt werden. Beispiel:

Der Controller HomeController.cs enthalte eine Action Index():

namespace mko.Asp.Mvc.Test.Controllers
{
    public class HomeController : Controller
    {
        //
        // GET: /Home/

        public ActionResult Index()
        {
            return View();
            
        }
    }
}

Die Methode View() wählt die DefaultView nach folgender Konvention aus:

DefaultViewName := ActionName
               

10.1.6 http Post

Durch einen Submitbutton in einem Webformular wird ein http- Post Befehl ausgelöst. Beim Post werden die Formulardaten in einem http Datentelegramm verpackt und an die Adresse zurückgesendet, von der die Webseite ursprünglich geladen wurde.

Die Postback Datentelegramm wird schließlich von einer Action im Controller verarbeitet, die

  1. Parameter haben, deren Namen mit Formularelementen übereinstimmen

  2. einen Parameter haben, der vom Typ des Modells ist

  3. Die mit dem Attribut [HttpPost] gekennzeichnet wurde

  4. Die im Formularelement als PostBack- Action definiert wurde

  @model MVC.Basics.Models.Exc02_Routing.RoutingInfos

@{
    ViewBag.Title = "Index";
    Layout = "~/Views/Shared/_Layout.cshtml";
}

@*  Mit BeginForm wird der Formularbereich im html- Dokument definiert.
    Der erste Parameter definiert die Action, und der zweite den Controller,
    der für die Bearbeitung des http-Postback zuständig ist.
*@
@using (Html.BeginForm("PostBackAction", "Exc02_Routing"))
{
    <text>

        @Html.HiddenFor(model => model.WfID)

        <h3>Routing mittels http Post Befehl</h3>

        <input type="submit" value="Submit" />
   </text>
}


Die Postback- Action hat folgende Signatur:

  [HttpPost] // Attribut optional. Nur notwendig, wenn PostBackAtion Funktion überladen
public ActionResult PostBackAction(string WfID, string TestInput)
{}

10.1.7 http Get und Routing

Hyperlinks definieren dabei im href Attribut einen URL, von dem der Browser mittels eines http- Get Befehls eine Ressource abruft.

Aufbau eines URL bei einem http Get:

URL::=<Schema(z.B.http)><Host(z.B.www.mkoit.de)><Path(z.B./Impressum)><Querystring(z.B.?ID=1)>

Während Schema und Host für eine Webanwendung im allgemeinen festgelegt sind, kann im Pfad und im Querystring Informationen für die Navigation zur gewünschten Ressource hinterlegt werden. Dies wird auch als Routing bezeichnet.

10.1.7.1.1 Routing beim Get (ActionLink und RoutLink)

Allgemeine Infos dazu hier:http://msdn.microsoft.com/en-us/library/cc668201.aspx

Mit den Grundeinstellungen ordnet ein Webserver einem URL immer eine Dateiressource zu (HTML-File). In MVC ist diese Zuordnung zu grob. Hier muss über einen URL die Action- Methode eines Controllers ausgewählt und parametriert werden. Dies wird durch die Definition von Routen erreicht.

Die Routen werden definiert im App_Start- Verzeichnis unter RouteConfig.cs. Die Definitionen erfolgen Mittels der MapHttpRoute - Methode des globalen Objektes routes . Dabei ist mindestens ein Name für die Route, sowie ein URL- Muster zu definieren.

URL Muster bestehen aus Segmenten, die durch / getrennt sind:

SEGMENT1/SEGMENT2/.../SEGMENTn

Segmente können feste Werte oder Platzhalter sein. Die Paltzhalter werden dabei in geschweifte Klammern gesetzt:

SEGMENT1/{PLATZHALTER1}/.../SEGMENTn

Ein Muster wird wie folgt auf eine URL gemapt:

  1. Übereinstimmende Segmente in URL und Muster werden einander zugeordnet. Die Reihenfolge im Muster darf dabei nicht verletzt werden

  2. Korrespondieren Platzhalter mit noch nicht zugeordneten Segmenten der URL, dann werden diese Segmente als Textwerte in einem Dictionary eingetragen, wobei der Bezeichner des Platzhalters der Schlüssel ist

  3. Die MVC- Laufzeit sucht in dem Dictionary mindestens zwei Einträge:

    1. Eintrag unter dem Schlüssel controller: Der Wert wird der String Controller angehängt. Er stellt den Namen der Controllerklasse dar

    2. Eintrag unter dem Schlüssel action: Der Wert ist der Name der Action- Methode innerhalb der Controller- Klasse.

  4. Platzhalter, die nach dem Platzhalter für die Action- Methode folgen, stehen für die Parameter der Action- Methode. Die Bezeichner der Platzhalter müssen mit den Parameternamen übereinstimmen.

Z.B. ordnet das folgende Pattern den URL http://localhost:49353/Graphic/MoveTriangle/100 den Aufruf der Action GraphicController.MoveTriangle(100) zu:

routes.MapRoute(
                name: "MyRoute1",
                url: "{controller}/{action}/{translation}"
            );



10.1.7.1.2

10.1.8 Modelle

10.1.8.1.1 Modelle = Geschäftsobjekte

Eine MVC- Anwendung verarbeitet die Informationen in Modellen. Modelle sind die Klassen der Geschäftsobjekte. Eine Anwendung zur Verwaltung des Lagerbestandes wird ein Modell namens Artikel besitzen. Ein Computerspiel hat ein Modell namens SpielfigurSchlachtschiff.

10.1.8.1.2 Speicherort

Innerhalb einer MVC- Anwendung ist der Ordner Models zur Aufnahme der Klassendeklarationen der Modelle vorgesehen. In größeren Anwendungen werden Modelle in separaten Klassenbibliotheken ausgelagert.

10.1.8.1.3 Einschränkungen definieren für Validierung

Die Definition von Eigenschaften in Modellen bildet häufig ungenügend die Anforderungen an diese ab. So kann auf die Festlegung bestimmter Eigenschaften verzichtet werden, während anderen zu jedem Zeitpunkt ein gültiger Wert zugewiesen sein muss (required). Eine Eigenschaft könnte einen Währungswert darstellen, für den es aber in C# keinen Elementaren Datentyp gibt.

Mittels Attribute aus dem Namespace System.ComponentModel.DataAnnotations können die Modelleigenschaften mit zusätzlichen Informationen versehen werden, aus der die MVC- Laufzeit erkennt, ob es sich z.B. um Währungs- oder erforderliche Werte handelt. Z.B. werden diese Metainformationen von den HTML- Helper Erweiterungen zur Bindung von Modelldaten an Formularfelder für die Implementierung von Validierungen genutzt.

  using System.ComponentModel.DataAnnotations;

namespace Calculator.Models
{
    public class BinOpModel
    {        
        [Required]
        [Range(-1.0e100, 1.0e100)]
        public double A { get; set; }

        [Required]
        [Range(-1.0e100, 1.0e100)]
        public double B { get; set; }

        ...
    }
}

Achtung: Für Das RangeAttribute dürfen als Minimum und Maximum nicht double.MinValue oder double.MaxValue definiert werden, da sonst die Validierung fehlschlägt.

Validierung im Client durch View

Beim Rendern der View wird lt. Validierungsattributen JavaScript- Code eingebettet, der die Validierung im Browser implementiert.

Die Validierung funktioniert für nummerische Werte aktuell nur für angloamerikanische Darstellung von nummerischen Werten (also 3.14 anstatt 3,14). Für die deutsche Kultur muss die clientseitige Validierung abgeschaltet werden, indem in der View folgendes notiert wird:

@{
    ViewBag.Title = "BinOpView";
    Layout = "~/Views/Shared/_Layout.cshtml";

    // Abschalten der Clientseitigen Validierung
    HtmlHelper.ClientValidationEnabled = false;

}

Weitere Informationen dazu unter:

  1. http://www.dotnetlearners.com/blogs/view/86/Enable-or-disable-client-side-validations-in-mvc4.aspx

  2. http://msdn.microsoft.com/de-de/library/gg674880(v=vs.98).aspx

Validierung in Action auf dem Server

Sollte die Validierung auf dem Client nicht durchgeführt werden (deaktiviertes JavaScript), dann kann die Action gesichert werden, indem auf dem Server die Validierungsregeln ausgeführt werden. Dazu wird die Methode ModelState.IsValid aufgerufen

public ActionResult MyAction(MyModel model) 
{
   if(!ModelState.IsValid) return View("Error");
   …
}
10.1.8.1.4 Rendering der Modelleigenschaften in der View definieren

In einem Modellzentrieren Ansatz können auch die Darstellungen der Eigenschaftswerte bereits im Modell definiert werden. Dies geschieht durch Attribute aus System.ComponentModel.DataAnnotations.

Attribut

Bedeutung

Beispiel

DisplayFormat

Formatierung der Ausgabe. Z.B. wie viele Nachkommastellen bei einem nummerischen Typ usw.

[DisplayFormat(DataFormatString="{0:N3}")]



10.1.8.1.5 Modelle aktualisieren

Modellobjekte werden in Aktionen erzeugt, mit Daten gefüllt und an Views gesendet. Dort werden mit einem Teil ihrer Eigenschaften Formularfelder gefüllt, die der Anwender verändern kann. Anschließend werden die Eingaben in den Formularfeldern eine Aktion zurückgesendet. In der Aktion ist das Modellobjekt mit den geänderten Daten zu aktualisieren.

Die Menge aller Daten in einem Modellobjekt ist häufig größer als die Menge der in der View geänderten Daten. So kann das Modellobjekt nicht einfach durch die geänderten Daten ersetzt werden. Nur die Werte der geänderten Eigenschaften im Modellobjekt dürfen ersetzt werden.

(I)  Create() → M{p1,… , pn} → View() → F{f1(pi), … , fm(pj)}
(II)          → Anwender() → F{f1(pi'), … , fm(pj')}
(III.1)       → PostBack() → M{…, pi', … , pj', …}
(III.2)       → PostBack() → FormCollection{pi', … , pj'} 
              → TryUpdateModel() → M{p1,…, pi', … , pj', …, pn}

_ III.1 und III.2 sind die von MVC unterstützen Varianten.

III.1

Voraussetzung: Die aufgerufene Action erwartet als Parameter ein Modell.

Beim PostBack wird in der View ein neues Modell erzeugt und mittels eines Modelbinders wird dieses mit den geänderten Werten aus den Formularfeldern aktualisiert. Eigenschaften, die nicht mit Formularfeldern korrespondieren, werden mit Default- werten gefüllt. Mittels Attribute [Bind]- Attribut kann in die Bindung der Formularfelder an die Modelleigenschaften eingeriffen werden

public ActionResult Edit([Bind(Exclude="AnzLeben")] Spielfigur){...}
10.1.8.1.6 III.2

Voraussetzung: Die aufgerufene Action hat einen Parameter vom Typ FormCollection

Beim Postback werden die Formulardaten in eine FromCollection verpackt. In der Action kann das Modell aus dem Sitzungszustand oder der Datenbank geladen werden. Dann wird es mittels der Methode ViewPage.TryUpdate mit den Daten aus dem FormCollection- Parameter aktualisiert.

Public ActionResult Edit(FormCollection fc) {

        …
        Spielfigur fig = Session["Spielfigur"] as Spielfigur
      TryUpdateModel(fig, fc);
        …
}

10.1.9 Controller und Actions

Der Controller ist eine Klasse, die Methoden besitzt, welche mittels Routing über URL und HTTP- Requests von einem Webclient gestartet werden können. Diese Methoden werden Actions genannt. Sie verarbeiten den Requests, indem Modelle erzeugt oder abgerufen, und mit einer View verknüpft werden.

10.1.9.1 Modelle validieren und aktualisieren






10.1.9.2 Modelle an Views binden, Response- Daten erzeugen




10.1.9.3 Eventhandler




10.1.10 Views

View sind für die Darstellung der Daten aus dem Modellen im Browser zuständig. Sie enthalten Code, der vom Server vor der Auslieferung an den Browser ausgeführt wird, und die Modelldaten einbettet, sowie Code, den der Browser ausführt (HTML, JavaScript).

Beispiel: BinOpView für den Math- Controler:




@* Server
@* Definition des Models *@
@model mko.Asp.Mvc.Test.Models.BinOpModel
@{
    ViewBag.Title = "BinOpView";
}

<h2>Binäre Arithmetik</h2>
@* Einbindung eines .NET Namespaces auf der Serverseite *@
@using Props= mko.Asp.Mvc.Test.Properties

@* Ein c# Usingblock, der eine Form- Instanz kapselt. Am Ende wird die Dispose- Methode der Form-
   Instanz aufgerufen, was die Ausgabe eines schließenden Formtags zur folge hat.
   In BeginForm wird als Ziel eines Submits die Action "BinOp" aus dem Controller "Math" definiert.
 *@
@using (Html.BeginForm("BinOp", "Math"))
{
    @* Html- Helper, der ein Zusammenfassung von Fehlermeldungen einblendet *@
    @Html.ValidationSummary(true)
    
    <section>
        <div>

            @* Folgende Helper integireren die Werte von Modelleigenschaften, sowie HTML- Formularelemente und
               JavaScript Code zum Editieren und Validieren *@

            @Html.LabelFor(model => model.A, new { Class = "mathOpLabel" })
            @Html.EditorFor(model => model.A, new { Class = "mathOpField" })
            @Html.ValidationMessageFor(model => model.A)

            @Html.DropDownListFor(model => model.OperatorTxt, Model.OperatorSelection, new { Class = "mathOperator" })

            @* Anstatt MVC- Helper Html.Display wird hier direkt mit Html label und jQuery.UI Tooltip gearbeitet.
               Das title- Attribut ist dabei die Datenquelle der Tooltips. Das Verhalten mit den Tooltips wird durch
               die jQuery- Operation auf dem DOM im Scriptblock ganz unten realisiert.
               Vorteil: der Tooltip ist auch aus einer Resource einlesbar
             *@           
            <label title="@Props.Resources.BinOpViewOperand2Description" class="mathOpLabel">@Props.Resources.BinOpViewOperand2Name :&nbsp;</label>
            @Html.EditorFor(model => model.B, new { Class = "mathOpField" })
            @Html.ValidationMessageFor(model => model.B)


            @* Der Submittbutton sendet einen Post- Request an den Webserver, wobei die in 
               BeginForm definierte Action angefordert wird *@
            <input type="submit" value="=" />


            @Html.DisplayFor(model => model.Result, new { Class = "mathOpField" })
        </div>

    </section>
    
    <div>
        @Html.ActionLink("Back to List", "Index")
    </div>    
}

@* Benannter Abschnitt, der in der Layoutseite im Kopf eingeblendet wird, und die Styles enthält *@
@section Styles {
    @Styles.Render("~/Content/themes/base/css")
}

@* Bennanter Abschnitt, der im Fuss der Layoutseite eingeblendet wird, und die Skripte für den Browser
   in die Ausgabe an den Browser integriert  *@
@section Scripts {

    @* Achtung: JavaScripte sollten immer in einer separaten .js Date definiert werden, die mittels 
       Bundel- Konfiguration (Ordner App_Start\BundelConfig) in eine View eingebunden werden.
       Sonst funktioniert z.B. das Scriptdebugging in VisualStudio nicht ! *@

    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/bundles/jqueryui")   
    <script>
        // Hier wird auf dem DOM mittels jQuery eine Operation ausgeführt, welche bewirkt,
        // das alle title- Attribute als Tooltips dienen
        // Achtung: in Visual Studio nicht debuggbar, da nicht über Bundel- config eingebunden (siehe oben)
        jQuery( document ).tooltip();
    </script> 
}

10.1.10.1 Serverseitiger Code mit RAZOR parsen

Der vom Server auszuführende Code wird in einer View in der sog. RAZOR- Syntax eingebunden. RAZOR ist ein Parser, der sich am @- Symbol orientiert. Dank eingebauter Intelligenz sind keine weiteren Steuersymbole notwendig, die den serverseitigen Code vom clientseitigen abgrenzen. Im Vergleich zu klassischen ASP.NET Code, der Serverseitigen Code in <% … %> einschloss, sind RAZOR Views leichter lesbar, da schlanker und weniger überladen.

Es gelten folgende Regeln für den RAZOR- Parser:

@

RAZOR Einleitungssymbol. Soll @ als literaler Text eingesetzt werden, dann @@

@Variablenname

Inhalt einer Variable wird serverseitig in die Ausgabe an den Browser eingebettet

@Methode(...)

Rückgabewert der Methode wird serverseitig eingebettet

@(...)

Wirkungsweise von RAZOR auf Klammerinhalt beschränken. Muss eingesetzt werden, wenn RAZOR das Ende einer serverseitigen Anweisung nicht korrekt erkennt

@{ … }

Einbetten serverseitiger Anweisungsliste in View

@model Typename

Datentyp des Modells in der View festlegen (streng typisierte View)

@using Namespace

Deklaration eines Namespaces

@helper Methodensig{...}

RAZOR- Unterprogramm.

@* … *@

Kommentar

<text>...</text>

Der Inhalt des Textelementes ist clientseitiger Code, den RAZOR überspringt. Beim Rendern werden die <text> Tags entfernt.

<htmlTag>...</htmlTag>

Text in HTML- Tags wird von RAZOR übersprungen und an den Client geliefert.

10.1.10.1.1 RAZOR- Unterprogramme

Wiederholen sich serverseitige Anweisungen, dann können die in einen helper- Unterprogrammblock ausgelagert werden:

@* Selbsdefinierter Helper, der die Einbettung von Operanden automatisiert *@
@helper MyLabel(string name, string description)
{
    <label title="@description" class="mathOpLabel">@name :&nbsp;</label>
}


<h2>Binäre Arithmetik</h2>
@using Props = mko.Asp.Mvc.Test.Properties


@using (Html.BeginForm("BinOp", "Math"))
{
@* Html- Helper, der ein Zusammenfassung von Fehlermeldungen einblendet *@
    @Html.ValidationSummary(true)
    
    <section>
        <div>

            @* Einsatz selbsdefinierter Helper *@

            @MyLabel(Props.Resources.BinOpViewOperand1Name, @Props.Resources.BinOpViewOperand1Description)
            @Html.EditorFor(model => model.A, new { Class = "mathOpField" })
            @Html.ValidationMessageFor(model => model.A)
10.1.10.1.2 Html.Helper

http://www.asp.net/mvc/tutorials/views/introduction-to-razor-syntax

10.1.10.1.3 Anpassen der Ausgaben von Html.Display etc. mit Templates

10.1.10.2 Partielle Views

http://haacked.com/archive/2009/11/18/aspnetmvc2-render-action.aspx

10.1.10.3 Partielle Views aktualisieren mit jQuery

http://stackoverflow.com/questions/1670415/mvc-how-to-return-instruction-to-run-javascipt-method/1670447#1670447

10.1.11 Clientseitige Scripts einbinden

JavaScript sollte in die Views nicht direkt eingebunden werden wie im folgenden Beispiel, da z.B. das Scriptdebuggen von Scriptcode direkt in der View nicht unterstützt wird:

@section Scripts {

    @* Achtung: JavaScripte sollten immer in einer separaten .js Date definiert werden, die mittels 
       Bundel- Konfiguration (Ordner App_Start\BundelConfig) in eine View eingebunden werden.
       Sonst funktioniert z.B. das Scriptdebugging in VisualStudio nicht ! *@

    @Scripts.Render("~/bundles/jqueryval")
    @Scripts.Render("~/bundles/jqueryui")   
    <script>
        // Hier wird auf dem DOM mittels jQuery eine Operation ausgeführt, welche bewirkt,
        // das alle title- Attribute als Tooltips dienen
        // Achtung: in Visual Studio nicht debuggbar, da nicht über Bundel- config eingebunden (siehe oben)
        jQuery( document ).tooltip();
    </script> 
}

Stattdessen ist das Script in eine .js- Datei auszulagern, die im Scriptordner abgelegt wird. Das Script wird dann mittel @Script.Render("Url") in die View eingebunden:

@section Scripts {

    @Scripts.Render("~/bundles/Stat")

}

10.2 Sicherheit

MVC ist Teil von ASP.NET. Damit sind die gleichen Systeme zur Absicherung des Zugriffs auf Anwendung und Daten nutzbar wie im klassischen ASP.NET. Im einzelnen:

  1. Authentifizierung über IIS

  2. Formular basierte Authentifizierung mittels ASP.NET Membership

  3. Rollenverwaltung mittels ASP.NET RoleManager

  4. Autorisierung des Zugriffs auf Anwendungsordner über WebConfig

  5. Autorisierung auf Dateisystemressourcen über NTFS- Dateizugriffsrechte

Achtung: Die Projektvorlage Internet Application registriert für Demozwecke einen vereinfachten Membership- Provider über das Attribut [InitializeSimpleMembership] vor dem Account- Controller. Um die echte Membership zu nutzen, muss dieses Attribut auskommentiert/entfernt werden.

10.2.1 Zugriff auf Actions einschränken

Zusätzlich zu den genannten Instrumenten der Zugriffskontrolle können mittels Attribute die Zugriffe auf Aktions eines Controllers eingeschränkt werden:

[Authorize]

Nur angemeldete Benutzer dürfen diese Action aufrufen

[Authorize(Roles="A, B")]

Nur Benutzer, die zur Rolle A oder B gehören, dürfen die Action aufrufen.

[AllowAnonymous]

Alle Benutzer dürfen diese Action aufrufen