Webmigration

Migration Ihrer Anwendung von WebMatrix nach ASP.NET MVC 3

Brandon Satrom

Beispielcode herunterladen

Im Januar diesen Jahres hat Microsoft ein neues Programmiermodell für die Webentwicklung mit dem Microsoft .NET Framework vorgestellt – die ASP.NET-Webseiten. Zurzeit werden die ASP.NET-Webseiten standardmäßig in WebMatrix (web.ms/WebMatrix) unterstützt. Das seitenorientierte Programmiermodell verhält sich ähnlich wie PHP, bei dem jede Seite eine eigene Geschäftslogik, einen eigenen Datenzugriff und eigene dynamische Inhalte zum Rendern von HTML für den Browser enthält.

Es gibt eine Vielzahl von Gründen, eine Website in WebMatrix zu erstellen. Aber was, wenn Sie wissen, dass Sie in absehbarer Zukunft zu Visual Studio migrieren möchten? Müssen Sie die Website von Grund auf neu entwickeln, falls ASP.NET MVC 3 Ihr Endzustand ist und der Migrationszeitpunkt näher rückt? Nein. Sie brauchen keine Sorge zu haben, mit WebMatrix und ASP.NET-Webseiten handlungsunfähig zu werden.

Die ASP.NET-Webseiten – als Teil des ASP.NET-Kernframeworks – wurden unter dem Aspekt der Flexibilität entwickelt. Zwar gibt es keine technischen Grenzen bei Webseiten, die Sie dazu zwingen, zu ASP.NET MVC zu migrieren, dennoch könnte dies für Ihr Team, Ihr Produkt oder Ihr Unternehmen in bestimmten Situationen durchaus sinnvoll sein.

In diesem Artikel erläutern wir das Für und Wider einer solchen Migration. Außerdem zeigen wir Ihnen Strategien zum Migrieren Ihrer ASP.NET-Webseiten auf ASP.NET MVC auf, falls Sie tatsächlich mit dem Gedanken einer Migration spielen. Sie erfahren, wie Sie von Seiten zu Ansichten wechseln, wie Geschäftslogiken und Hilfscode gehandhabt werden, und wie Sie Modelle in Ihre Anwendung integrieren. Und schließlich erklären wir im Detail, wie Sie vorhandene Website-URLs mithilfe von Routing beibehalten und dauerhafte Umleitungen bei Bedarf unterstützen.

Warum sollten Sie migrieren?

Ehe wir uns eingehender mit den Gründen für eine Migration von Webseiten zu ASP.NET MVC befassen, sollten wir uns erst einmal die Gründe ansehen, die dagegen sprechen. Zunächst einmal sollten Sie Ihre Website nicht migrieren, nur weil Sie befürchten, dass Ihre Anwendung nicht skalierbar ist. Denn Webseiten basieren auf ASP.NET und bieten eine Reihe der Leistungsmerkmale von Anwendungen, die für ASP.NET MVC oder Webformulare geschrieben werden. Natürlich besitzt jeder Anwendungstyp ein etwas anderes Ausführungsmodell, aber nichts an der Standardversion von ASP.NET MVC macht die Lösung von vornherein skalierbarer oder weniger skalierbar als eine Webseiten-Anwendung. Skalierbarkeit und Leistung sind genau so wichtig für Ihre Entwurfsentscheidungen beim Aufbau Ihrer Website wie das zugrunde liegende Framework, für das Sie sich entscheiden.

Ein weiterer wenig geeigneter Grund für das Migrieren ihrer Website von Webseiten zu ASP.NET MVC ist, dass es einfach „in“ ist und jeder es macht. Oder weil Sie mit Ihrer Website in Visual Studio arbeiten möchten. Denn das können Sie auch mit Webseiten. ASP.NET MVC ist schlicht eine mögliche Webanwendungsarchitektur – nicht mehr und nicht weniger. Und schon gar kein Zaubermittel, das Ihre Website im Handumdrehen schöner und besser macht. Die Migration von Webseiten zu ASP.NET MVC ist eine Option, aber keinesfalls eine Notwendigkeit. Und obwohl Microsoft keine Mühen gescheut hat, diese Migration möglich zu machen und reibungslos zu gestalten, kostet sie Sie trotzdem Zeit, Ressourcen und Geld – wie jede Migration. Daher ist es wichtig, dass Sie sich aus den richtigen Gründen für eine Migration auf ASP.NET MVC entscheiden.

Ein guter Grund ist, dass Komponententests eine wichtige Rolle für Sie und Ihr Team spielen. Da das Webseiten-Modell seitenorientiert ist, können vorhandene Komponententesttools bei Webseiten-Websites nicht eingesetzt werden. Das Testen der Webbenutzeroberfläche mit Tools wie WatiN oder Selenium ist nach wie vor möglich, ein Komponententest auf Codeebene mit Tools wie NUnit oder MsTest jedoch nicht. Wenn Ihre Website komplexer geworden ist und Sie Wert auf Komponententests legen, ist die Migration auf ASP.NET MVC durchaus sinnvoll.

Wenn Sie Ihren Code lieber anhand von Komponententests prüfen, dann ziehen Sie vermutlich auch eine grundlegende Trennung der Bereiche in Ihren Anwendungen vor. Es ist zwar möglich, in Webseiten einen sauberen und getrennten Code mithilfe von Helfern und Codedateien zu erstellen, das Modell macht eine solche Trennung jedoch nicht so leicht, wie ASP.NET MVC es tut. Wenn die Trennung der Bereiche eine große Rolle spielt und Sie eine Anwendungsarchitektur wünschen, die eine derartige Trennung unterstützt, ist die Migration eine echte Option für Sie.

Daneben gibt es – je nach Kontext Ihrer Website oder Organisation – noch weitere Gründe, die für eine Migration sprechen. Wenn Ihr Team weiter wächst und Ihre Website immer komplexer wird und eine umfangreichere Geschäftsfunktionalität erfordert, tun Sie gut daran, zu migrieren. Darüber hinaus kann eine Migration durchaus notwendig sein, um im Rahmen von Quellcodeverwaltung, Auslastungstests usw. auf ein reichhaltigeres Entwicklungsökosystem zurückgreifen zu können.

Vorbereiten der Migration

Für diesen Artikel nehmen wir die Websitevorlage der Fotogalerie, die in WebMatrix enthalten ist, und migrieren sie zu ASP.NET MVC. Im Zentrum des Migrationsprozesses steht das Verschieben von Seiten zu Modellen, Controllern und Ansichten, aber ehe wir soweit sind, müssen wir einige Vorbereitungen treffen.

Da wir in diesem kurzen Artikel detailliert auf die einzelnen Schritte einer manuellen Migration eingehen, können wir nicht jedem Schritt dieselbe Aufmerksamkeit zukommen lassen. Wir möchten in erster Linie die wichtigsten Schritte erläutern und kleinere Überlegungen zumindest erwähnen. Außerdem haben wir uns entschieden, Elemente auszulassen, die mit dem Kernpunkt der Gesamtkonvertierung nichts zu tun haben, wie Best Practices für den Datenzugriff, mögliche Assemblystrukturen, Abhängigkeitsinjektionen und Ähnliches. Diese Elemente sind durchaus wichtig, aber bei vielen geht es im Grunde um Entwicklerkultur und persönliche Vorlieben, und sie können am besten während der Umgestaltung nach der Migration angegangen werden.

Wir sollten außerdem darauf hinweisen, dass wir das Feature zum Öffnen in Visual Studio, mit dem Sie Ihre aktuelle Website als Websiteprojekt in Visual Studio anzeigen können, im Rahmen dieses Artikels nicht verwenden werden. Sie können diese Option natürlich gerne verwenden, selbst wenn Sie sich gegen eine Migration auf ASP.NET MVC entscheiden. Aber wir nutzen für ASP.NET MVC lieber das Visual Studio-Webanwendungsprojekt und beginnen mit einer leeren Website, in die wir die Komponenten dann manuell migrieren.

Dazu wählen wir zunächst das Menüelement „Datei | Neu | Projekt“ und dann eine ASP.NET MVC 3-Webanwendung mit der leeren Vorlage. Als Standardanzeigemodul verwenden wir Razor.

Nachdem Sie Ihre Zielanwendung eingerichtet haben, müssen Sie einige erste Migrationsarbeiten vornehmen. Der folgende Überblick zeigt die ersten Schritte:

  1. Fügen Sie alle Pakete, die Sie in ihrer Webseiten-Website verwenden, mithilfe von NuGet (nuget.org) zu Ihrer ASP.NET MVC-Website hinzu.
  2. Fügen Sie Referenzen zu System.Web.Helpers, WebMatrix.Data und WebMatrix.WebData hinzu. Setzen Sie im Bereich „Eigenschaften“ jede Referenz auf „Lokale Kopie = true“.
  3. Verschieben Sie die Inhalte von _AppStart.cshtml zur Application_Start-Methode von Global.asax. Zwar kann _AppStart so, wie es ist, verschoben und verwendet werden, es empfiehlt sich aber, die Logik in Global.asax mithilfe des vorhandenen ASP.NET MVC-Startcodes zu zentralisieren.
  4. Fügen Sie <roleManager enabled=true /> zum Abschnitt <system.web> in Ihrer root.web.config-Datei hinzu. Die Fotogalerie-Anwendung verwendet den neuen WebSecurity-Mitgliedschaftsanbieter in WebMatrix.WebData, daher muss dieser Eintrag in unserer Konfiguration enthalten sein, damit die Website funktioniert.
  5. Verschieben Sie außerdem alle Stylesheets, Skriptdateien und Bilder aus den Ordnern „Inhalt“ oder „Skripte“ in Ihrer Anwendung. Aktualisieren Sie alle Ressourcenlinks in diesen Dateien mit ihren neuen Pfaden.
  6. Ändern Sie die Standardroute in Global.asax so, dass sie auf den Galeriecontroller und die Standardaktion zeigt.
  7. Kopieren Sie die SQL Compact-Datenbank aus dem Ordner „App_Data“ in den gleichnamigen Ordner in Ihrer Website. Wenn Sie eine andere Datenbank für Ihre Website verwenden, fügen Sie diese Verbindungszeichenfolge zur Web.Config-Datei in Ihrer Anwendung hinzu.

Von Seiten zu Ansichten migrieren

Nachdem Sie dieses anfängliche Setup Ihrer ASP.NET MVC-Website durchgeführt haben, können Sie den Kern Ihrer vorhandenen Website migrieren: die Seiten. In Webseiten enthält eine Seite (.[cs/vb]html) Markups, Geschäftslogik und den gesamten Datenzugriff, der für diese Seite erforderlich ist. Ihre Hauptaufgabe bei der Migration auf ASP.NET MVC ist es, jede Seite aufzubrechen und ihre Inhalte in Controlleraktionen (Geschäftslogik), Datenzugriffsklassen (Datenzugriff) und Ansichten (Markup) aufzuteilen.

Zunächst müssen Sie das Layout Ihrer Website migrieren: Ähnlich den Masterseiten in Webformulare und ASP.NET MVC, handelt es sich bei Layoutseiten um Dateien, die die Layoutstruktur Ihrer Website festlegen. Webseiten und ASP.NET MVC 3 (bei Verwendung des Razor-Ansichtsmoduls) verwenden beide dasselbe Layoutsubsystem – dieser Migrationsschritt sollte also einfach sein. Die Stammdatei „_SiteLayout.cshtml“ in der Fotogalerie-Site enthält die Websitestruktur. Kopieren Sie den Inhalt, und navigieren Sie dann zu Ihrer ASP.NET MVC-Website. Öffnen Sie die Layoutdatei unter „Views/Shared/_Layout.cshtml“, und fügen Sie den Inhalt von „_SiteLayout.cshtml“ ein.

Anschließend müssen Sie einige kleine Änderungen an der Datei „_Layout.cshtml“ vornehmen. Ändern Sie zunächst den Link zu Ihrem Stylesheet in den neuen Speicherort Ihrer ASP.NET MVC-Anwendung („~/Content/Site.css“ anstelle von „~/Styles/Site.css“). Als nächstes müssen Sie „@Page.Title“ in „@ViewBag.Title“ ändern. Beide Objekte sind dynamisch und können Anzeige- oder andere Daten für Seiten in Ihrer Website enthalten. Wie Sie wahrscheinlich schon vermutet haben, wird „Pages“ für Webseiten und „ViewBag“ für ASP.NET MVC verwendet.

Die letzte Änderung, die Sie an Ihrer _Layout.cshtml-Datei vornehmen müssen, sollten Sie sich für alle Seiten, die Sie zu ASP.NET MVC migrieren, merken. Beachten Sie, dass „_Layout.cshtml“ zum Einfügen von URLs in die Seite @Href-Aufrufe verwendet. Für jeden Aufruf, der statische Inhalte referenziert (z. B. Skripte, CSS usw.) können diese unverändert bleiben. Aber vielleicht möchten Sie alle @Href-Aufrufe, die zu Seiten auf Ihrer Website zeigen, ändern. Zwar funktionieren die Aufrufe auch nach der Migration einwandfrei, doch sie zeigen auf statische URLs. In ASP.NET MVC gilt die Verwendung von ASP.NET-Routing zum Erstellen von URLs als bessere Lösung, wenn es um das Rendern von Ansichten geht. Das Ergebnis ist klarer. Weniger anfällige Link werden nicht auf Ihrer Website hartcodiert, sondern mit Ihren Route-Tabellendefinitionen verknüpft.

Daher sollten Sie sämtliche Links wie den folgenden ändern:

<div id="banner">
  <p class="site-title">
    <a href="@Href("~/")">Photo Gallery</a>
  </p>
...
</div>

Verwenden Sie stattdessen „@Url.RouteUrl“ oder „@Url.Action“:

<div id="banner">
  <p class="site-title">
    <a href="@Url.Action("Default", "Gallery")">Photo Gallery</a>
  </p>
...
</div>

Nachdem Sie nun Ihr Websitelayout migriert haben, können Sie mit der Migration von Seiten zu Ansichten beginnen. Wenn es in Ihrer Webseiten-Anwendung CSHTML-Seiten gibt, die von RenderPage-Aufrufen ausgeführt werden, verschieben Sie diese entweder in „Views/Shared“ (Website-übergreifende Seiten) oder in den entsprechenden Ansichten-Unterordner (Seiten, die von einem Controller freigegeben werden, z. B. „Konto“). Jede Seite, die eine dieser Teilseiten aufruft, muss mit dem neuen Speicherort aktualisiert werden.

Die übrigen Seiten sollten in „Ansichten“ verschoben werden, in vom Controller organisierte Ordner. Da Ihre Webseiten-Website kein Controllerkonzept hat, müssen Sie während der Migration Controller einbinden. Die gute Nachricht lautet, dass die Fotogalerie-Anwendung eine Controllerstruktur aufweist. Sie können diese Vorgehensweise dann auch für Ihre eigenen Websites übernehmen.

Die Websitevorlage der Fotogalerie verwendet beispielsweise folgende Ordner zum Gruppieren von Seiten: „Konto“, „Galerie“, „Foto“, „Tag“ und „Benutzer“. Jeder Ordner enthält Seiten, die mit dieser Gruppe verwandte Funktionen aktivieren. So enthält der Konto-Ordner zum Beispiel Seiten zum Anmelden an und Abmelden von der Website und für registrierte Benutzer. Der Galerie-Ordner enthält eine Galerieauflistungsseite, eine Seite zum Hinzufügen einer neuen Galerie und eine Seite zum Anzeigen von Fotos in einer Galerie. Die übrigen Ordner sind in ähnlicher Weise organisiert. Eine derartige Struktur ist zwar auf Webseiten-Websites nicht erforderlich, vereinfacht aber die Migration auf ASP.NET MVC. In diesem Fall weist jeder Ordner auf einen Controller und jede CSHTML-Datei auf eine Aktion und Ansicht.

Beginnen wir, indem wir den Konto-Ordner und seine drei Seiten (Anmelden, Abmelden und Registrieren) in Ihre ASP.NET MVC-Anwendung im Ansichten-Verzeichnis verschieben. In ASP.NET MVC-Begriffen ausgedrückt, werden Ihre Seiten umgehend zu Ansichten – aufgrund ihres Speicherorts innerhalb der Anwendung. Das war aber noch nicht alles. Ihre Anwendung benötigt noch einen Controller und eine Aktion, um Benutzern bei Bedarf die gewünschten Ansichten bereitzustellen.

Einführung in Controller

Gemäß der MVC-Konvention bedeutet die Tatsache, dass ein Konto-Ordner unter „Ansichten“ liegt, dass es einen Controller mit dem Namen „AccountController“ geben sollte. Unser nächster Schritt ist also das Erstellen dieses Controllers im Controller-Ordner. Klicken Sie dazu einfach mit der rechten Maustaste auf „Hinzufügen“ | „Controller“. Über diesen leeren Controller können wir jetzt Aktionsmethoden mit der Logik erstellen, die nun oben in jeder der in unsere Anwendung verschobenen CSHTLM-Dateien aufgeführt wird.

Wir werden zunächst auf Login.cshtml eingehen, die den Code in Abbildung 1 beinhaltet.

Abbildung 1 Geschäftslogik in Login.cshtml

Page.Title = "Login";
if (IsPost) {
  var email = Request["email"];
  if (email.IsEmpty()) {
    ModelState.AddError(
      "email", "You must specify an email address.");
  }
  var password = Request["password"];
  if (password.IsEmpty()) {
    ModelState.AddError(
      "password", "You must specify a password.");
  }

  if (ModelState.IsValid) {
    var rememberMe = Request["rememberMe"].AsBool();
    if (WebSecurity.Login(email, password, rememberMe)) { 
      string returnUrl = Request["returnUrl"];        
      if (!returnUrl.IsEmpty()) {
        Context.RedirectLocal(returnUrl);
      } else{
        Response.Redirect("~/");
      }
    } else {
      ModelState.AddFormError(
        "The email or password provided is incorrect.");
    }
  }
}

Beachten Sie, dass hier zwei Szenarien behandelt werden. Im ersten Szenario lädt der Benutzer die Anmeldeseite zum ersten Mal. Dabei setzt die Seite ihre Titel und Übergänge direkt in Markup. Das zweite Szenario ist in der IsPost-Bedingung enthalten und stellt die Logik dar, die ausgeführt wird, wenn der Benutzer das Anmeldeformular ausgefüllt hat und auf die Schaltfläche „Anmelden“ klickt.

In ASP.NET MVC wird die Bereitstellung eines leeren Formulars und das Akzeptieren einer Formulareinsendung durch Erstellen von zwei Aktionsmethoden in unserem Controller gehandhabt. Dabei ist eine Aktion dem Leerformat und die andere der Verwaltung der Einsendung zugeteilt. Die erste Aktion legt den Seitentitel fest und gibt die Anmeldeansicht aus, die zweite Aktion enthält die Logik innerhalb der IsPost-Bedingung. Diese Aktionen sind in Abbildung 2 dargestellt. Nachdem Sie die beiden Aktionen hinzugefügt haben, löschen Sie den Kopfzeilencode aus der Datei „Login.cshtml“.

Abbildung 2 Aktionen des Anmeldecontrollers

public ActionResult Login() {
  ViewBag.Title = "Login";
  return View();
}

[HttpPost]
public ActionResult Login(string email, string password, 
  bool? rememberMe, string returnUrl) {
  if (email.IsEmpty())
    ModelState.AddModelError("email", 
      "You must specify an email address.");
  if (password.IsEmpty())
    ModelState.AddModelError("password", 
      "You must specify a password.");
  if (!ModelState.IsValid)
    return View();
  if (WebSecurity.Login(email, password, 
    rememberMe.HasValue ? rememberMe.Value : false)) {
    if (!string.IsNullOrEmpty(returnUrl))
      return Redirect(returnUrl);
    return RedirectToAction("Default", "Gallery");
  }

  ModelState.AddModelError("_FORM", 
    "The email or password provided is incorrect");
  return View();
}

Es gibt eine Reihe von wichtigen Unterschieden zwischen der Originalseite und den resultierenden Aktionsmethoden: Zunächst einmal werden Sie feststellen, dass die IsPost-Bedingung nicht erforderlich ist. In ASP.NET MVC erstellen wir eine Folgeaktion für die Anmeldeseite, indem wir eine zweite Anmeldeaktionsmethode schaffen und diese mit dem [HttpPost]-Attribut versehen. Unsere erste Anmeldemethode macht jetzt nichts weiter, als die ViewBag.Title-Eigenschaft festzulegen und ein Anzeigeergebnis auszugeben, das nach einer Anzeigeseite „Login.cshtml“ unter Ansichten/Konto sucht.

Den zweiten Unterschied, den Sie wahrscheinlich bemerken werden, ist, dass unsere Folgeaktion diverse Parameter enthält und alle Anforderungsaufrufe, die die Originalseite verwendet, verschwunden sind. Indem wir Parameter über unsere Methode legen, die Feldnamen in unserem Anmeldeformular (E-Mail, Kennwort und RememberMe) entsprechen, können wir den ASP.NET MVC-Standardmodellbinder so verwenden, dass uns diese Elemente als Parameter zu der Aktion weitergeleitet werden. Das erspart das Aufrufen des Anforderungsobjekts und macht die Aktionslogik prägnanter.

Und schließlich gibt es leichte Unterschiede bei der Handhabung der Validierung und der Durchführung von Umleitungen in ASP.NET-Webseiten- und ASP.NET MVC-Anwendungen. In unserer ASP.NET-Webseiten-Website sind „ModelState.AddError“ und „.AddFormError“ die Aufrufe, die wir verwenden, um die Seite zu informieren, dass ungültige Formulardaten vorliegen. In ASP.NET MVC-Anwendungen verwenden wir den Aufruf „ModelState.AddModelError“, der sich nur wenig unterscheidet, aber eine notwendige Änderung für alle Ihre Seiten darstellt. Bei Umleitungen ruft unsere Webseiten-Website „Response.Redirect“ auf, wenn der Benutzer umgeleitet wird. In ASP.NET MVC rufen wir „RedirectToRoute(‚Default‘)“ auf, da unsere Controlleraktionen ein „ActionResult“ zurückgeben sollen. Das Ergebnis ist das gleiche.

Nachdem wir die Anmeldeseite migriert haben, können wir auch schnell die Abmeldeseite („Logout.cshtml“) abwickeln. In Webseiten können einige Seiten Logik aber kein Markup enthalten, wenn Ziel der Seite die Durchführung einer Aktion und die Anschließende Umleitung des Benutzer ist, wie es bei „Logout.cshtml“ der Fall ist:

@{
  WebSecurity.Logout();
  Response.Redirect("~/");
}

In ASP.NET MVC fügen wie eine Abmelde-Aktion hinzu, die diese Aufgabe übernimmt:

public ActionResult Logout() {
  WebSecurity.Logout();
  return RedirectToAction("Default", "Gallery");
}

Da „Ansichten“ nur die visuellen Elemente einer Seite, nicht aber Funktionen darstellt und wir eine Aktion erstellt haben, die das Abmelden und Umleiten des Benutzers handhabt, können wir die Ansicht „Logout.cshtml“ aus unserer Anwendung löschen.

Bisher haben wir unsere Konto-Seiten in Ansichten umgewandelt, indem wir sie in den Ansichten/Konto-Ordner kopiert, einen AccountController zur Verwaltung von Anforderungen an unsere Konto-Seiten erstellt und Aktionsmethoden zur Verwaltung von Anmelde- und Abmeldeszenarien implementiert haben. Jetzt können Sie die Website erstellen und ausführen und „Konto/Anmelden“ an die Adressleiste Ihres Browsers anfügen. Beachten Sie, dass die Standardhomepage zu „Galerie/Standard“ zeigt, die wir noch nicht implementiert haben und deshalb nicht angezeigt wird.

Der andere Bereich der Websitefunktionen, die Sie an diesem Punkt behandeln möchten, sind der Code und Helfer, die in dem App_Code-Verzeichnis Ihrer Webseiten-Website enthalten sind. Zu Beginn der Migration können Sie das gesamte Verzeichnis in Ihre ASP.NET MVC-Anwendung hinüber kopieren und in Ihr Projekt einbinden. Wenn das Verzeichnis Codedateien (CS- oder VB-Dateien) enthält, können Sie sie entweder in App_Code beibehalten oder an einen anderen Speicherort verschieben. In jedem Fall müssen Sie die Eigenschaft „Buildvorgang“ jeder Datei in „Kompilieren“ anstelle von „Inhalt“ ändern. Wenn das Verzeichnis CSHTML-Dateien mit „@helper“-Methodendeklarationen enthält, können Sie diese unverändert in Ihrer ASP.NET MVC-Anwendung verwenden.

Für den Rest Ihrer Webseiten-Website gehen Sie ähnlich vor: Sie erstellen einen Controller für jeden Ansichten-Ordner, erstellen Aktionen für jede Seite und verschieben den Kopfzeilencode jeder Seite in eine oder mehrere Aktionen. Und im Handumdrehen sind alle Ihre Seiten fein säuberlich in Controlleraktionen und Ansichten unterteilt. Aber es gibt noch einen Teil im MVC-Muster, über das wir bisher nicht gesprochen haben: das Modell.

Migrieren von Datenzugriff in Repositoryklassen

Die Vorgehensweise, die Geschäftslogik jeder Seite in eine oder mehrere Controlleraktion zu verschieben, stellt keinen großen Aufwand dar. Mit einer Ausnahme: dem Datenzugriff. Während einige Ihrer Seiten den Anmelde- und Abmeldeseiten wahrscheinlich sehr ähnlich sind und Logik, aber keinen Datenzugriff enthalten, verwendet ein Großteil Ihrer Webseiten-Website höchstwahrscheinlich eine Datenbank.

Die Account/Register.cshtml-Seite ist ein Beispiel dafür. Wenn der Benutzer das Registrierungsformular ausfüllt und auf „Registrieren“ klickt, macht die Seite zwei Datenbankaufrufe (siehe Abbildung 3).

Abbildung 3 Register.cshtml-Datenbanklogik

var db = Database.Open("PhotoGallery");
      
var user = db.QuerySingle("SELECT Email FROM 
UserProfiles WHERE LOWER(Email) = LOWER(@0)", email);
       
if (user == null) {      
  db.Execute(
    "INSERT INTO UserProfiles (Email, DisplayName, Bio) 
    VALUES (@0, @1, @2)", email, email, "");

  try {
    WebSecurity.CreateAccount(email, password);
    WebSecurity.Login(email, password);
    Response.Redirect("~/");
  } catch (System.Web.Security.MembershipCreateUserException e) {
    ModelState.AddFormError(e.ToString());
  }
} else {
  ModelState.AddFormError("Email address is already in use.");
}

Zunächst öffnet die Registerseite die Fotogalerie-Datenbank und gibt ein WebMatrix.Data.Database-Objekt zurück, das die Datenbank repräsentiert. Anschließend verwendet die Seite das Objekt, um nach einer vorhandenen E-Mail-Adresse mit dem vom Benutzer bereitgestellten Wert zu suchen. Existiert diese Adresse nicht, wird ein neuer UserProfile-Datensatz erstellt, und mithilfe des WebSecurity-Mitgliedschaftsanbieter wird ein Konto für den Benutzer angelegt.

Solange wir eine Referenz zu „WebMatrix.Data“ hinzugefügt und die Lokale Kopie-Eigenschaft auf „true“ gesetzt haben, können wir diese Datenbanklogik verwenden – die Website funktioniert wie üblich. Da Sie sich mitten in der Migration befinden, möchten Sie diesen Ansatz möglicherweise als taktischen Schritt einsetzen, damit die Website funktional bleibt.

In diesem Artikel aber gehen wir noch einen Schritt weiter und erstellen zusätzliche Objekte, die Ihren Datenzugriff enthalten – so, wie wir es bei einer ASP.NET MVC-Anwendung tun würden, wenn wir von Grund auf beginnen müssten. Es stehen eine Vielzahl von Mustern zur Auswahl, um Controller und Datenzugrifflogik zu trennen. Wir verwenden das Repositorymuster für die Fotogalerie. Indem wir unseren Datenzugriff in Repositoryklassen abstrahieren, können wir diese Logik einkapseln und Beeinträchtigungen minimieren, falls wir formale Modellobjekte oder eine objektrelationalen Zuordnung (Object Relational Mapping, ORM) wie Entity Framework hinzufügen möchten.

Beginnen wir also mit dem Erstellen eines Repositoryordners in unserer Anwendung und einer einfachen Klasse „AccountRepository.cs“. Anschließend können wir die einzelnen Datenbankaufrufe in unserer Registrieren-Aktion durchgehen und die Logik in unser Repository verschieben (siehe Abbildung 4).

Abbildung 4 AccountRepository

public class AccountRepository {
  readonly Database _database;
  public AccountRepository() {
    database = Database.Open("PhotoGallery");
  }

  public dynamic GetAccountEmail(string email) { 
    return _database.QuerySingle(
      "SELECT Email FROM UserProfiles 
      WHERE LOWER(Email) = LOWER(@0)", email);
  }
 
  public void CreateAccount(string email) {
    _database.Execute(
      "INSERT INTO UserProfiles 
      (Email, DisplayName, Bio) VALUES (@0, @1, @2)", 
      email, email, "");
  }
}

Wir haben den Aufruf zu „Database.Open“ zum Konstruktor unseres Repositorys hinzugefügt und zwei Methoden erstellt – eine zum Suchen einer Konto-E-Mail, die andere zum Erstellen des Kontos.

Beachten Sie, dass der Rückgabetyp für „GetAccountEmail“ dynamisch ist. In „WebMatrix.Data“ geben viele der Abfragemethoden entweder dynamische oder IEnumerable<dynamic>-Sequenzen zurück. Es spricht nichts dagegen, diese Methode in Ihren Repositorys zu verwenden, solange sie sich bewährt. 

Die neue Registrierungsmethode unter Einsatz unseres AccountRepositorys ist in Abbildung 5 dargestellt.

Abbildung 5 Registrierungsaktion unter Verwendung von AccountRepository

[HttpPost]
public ActionResult Register(string email, string password, 
  string confirmPassword) {

  // Check Parameters (omitted)

  if (!ModelState.IsValid)
    return View();
 
  var db = new AccountRepository();
  var user = db.GetAccountEmail(email);
 
  if (user == null) {
    db.CreateAccount(email);
 
    try {
      WebSecurity.CreateAccount(email, password);
      WebSecurity.Login(email, password);
      return RedirectToAction("Default", "Gallery");
    }
    catch (System.Web.Security.MembershipCreateUserException e) {
      ModelState.AddModelError("_FORM", e.ToString());
    }
  }
  else {
    ModelState.AddModelError("_FORM", 
      "Email address is already in use.");
  }
 
  return View();
}

Die Verwendung von dynamischen Rückgabetypen ist absolut akzeptabel und kann während einer Migration sogar sehr sinnvoll sein, da Ihre Seite im Handumdrehen so funktionstüchtig wie eine vollwertige ASP.NET MVC-Anwendung ist. Sie müssen in ASP.NET MVC-Anwendungen keine Modelle mit fester Typbindung verwenden. Daher können Sie diese Strategie einsetzen, sofern Sie keine Code-bezogene Definition Ihres Datenmodells benötigen. Die Controllerlogik und die Ansichten Ihrer dynamischen ASP.NET MVC-Anwendung funktionieren wie gewohnt – mit einer Ausnahme:

Vielleicht haben Sie bemerkt, dass Formularfelder in Ihrer Webseiten-Anwendung mit Standardmarkup ausdrücklich definiert sind:

<input type="text" />
<input type="submit" />
...

In ASP.NET MVC ist die bevorzugte Methode der Verwendung von Formularsteuerelementen der Einsatz von HTML-Hilfsmethoden wie „Html.TextBox“ oder „Html.TextBoxFor“. Diese Methoden verwenden das in Ihre Ansicht übergebene Modell, um aktuelle Werte festzulegen und die Formularvalidierung zu handhaben. Wenn Sie diese Hilfsmethoden nach der Migration in Ihren Ansichten nutzen möchten, müssen Sie stark typisierte Modellobjekte einführen. Dynamische Typen können Sie dann nicht länger in Ihren Repositorys verwenden, da diese Hilfsmethoden nicht mit dynamischen Modellen arbeiten können.

Bewahren von Website-URLs

Ihre Website-URLs sind wichtig. Unabhängig vom Status Ihrer Website hängen viele externe Quellen von Ihren vorhandenen URLs ab – beispielsweise Suchmaschinen, Dokumentation, Kommunikation, Testskripts und Ähnliches. Aufgrund dieser Abhängigkeiten sollten Sie Ihre URLs nicht willkürlich ändern, selbst bei einer Migration nicht.

ASP.NET-Routing kann Ihnen dabei helfen, vorhandene URLs beizubehalten. ASP.NET-Routing vereinfacht Anforderungen und gleicht sie mit der korrekten Ressource ab – in unserem Fall mit einem Controller und einer Aktion. Webseiten verwenden ein anderes Routingsystem als ASP.NET MVC, Sie müssen sich also ein wenig Zeit nehmen, um sicherzustellen, dass Ihre vorhandenen URLs gewahrt bleiben.

Webseiten-Anwendungen können URLs mit und ohne Erweiterungen handhaben. Die beiden folgenden URLs beispielsweise werden in dieselbe Seite aufgelöst:

http://mysite/Gallery/Default.cshtml

http://mysite/Gallery/Default

Eine ASP.NET MVC-Anwendung dagegen kann die erste URL mit der CSHTML-Erweiterung nicht auflösen. Durch das Website-weite Verwenden von URLs ohne Erweiterung stellen Sie sicher, dass Suchmaschinen und andere abhängige Sites dasselbe tun. Dadurch werden die Migrationsauswirkungen auf Ihre Website minimiert. Wenn Sie aber vorhandene URLs mit Erweiterungen handhaben müssen, können Sie Routen in Ihrer ASP.NET MVC-Anwendung erstellen, um sicherzustellen, dass die URLs nicht beschädigt werden.

Sehen Sie sich zum Beispiel die folgende Standardroute für unsere Fotogalerie-Anwendung an:

routes.MapRoute(
  "Default", 
  "{controller}/{action}/{id}", 
  new { controller = "Home", 
    action = "Index", id = "" } 
);

Um Legacy-URLs in unserem System zu unterstützen, müssen wir zu unserer Routentabelle zusätzliche Routen über dieser Definition in der Global.asax-Datei hinzufügen. Hier ist ein Beispiel für eine solche Definition:

routes.MapRoute(
  "LegacyUrl",
  "{controller}/{action}.cshtml/{id}",
  new { controller = "Gallery", 
    action = "Default", id = "" }
);

In diesem Routeneintrag werden URLs mit CSHTML-Erweiterung verarbeitet und an den entsprechenden Controller und die entsprechende Aktion gesendet – vorausgesetzt, Ihre vorhandene Webseiten-Website ist einer Controller/Aktion-Struktur zugeordnet.

Bedenken Sie beim Planen einer Migration, dass Sie die Standardroute oder sogar zusätzliche Routen in Ihrer Anwendung möglicherweise ändern müssen, um vorhandene URLs zu unterstützen. Wenn Sie allerdings beschließen, eine vorhandene URL aufzuteilen, schließen Sie in jedem Fall Aktionen mit ein, um dauerhafte Umleitungen für Benutzer zu verarbeiten.

Zusammenfassung

Um die Auswirkungen einer Migration von Webseiten zu ASP.NET MVC zu unterstreichen, sollten wir einen Blick auf die Vorher-/Nachher-Struktur unserer Fotogalerie-Website werfen. In Abbildung 6 sehen Sie links die Struktur der Webseiten-Website in WebMatrix. Auf der rechten Seite ist die Website nach der abgeschlossenen Migration auf ASP.NET MVC abgebildet. Beachten Sie, dass das Endergebnis im Großen und Ganzen sehr vertraut aussieht – trotz einer Strukturunterschiede.

Application Layout Before and After

Abbildung 6 Anwendungslayout: Vorher – Nachher

Heute stehen ASP.NET-Entwicklern drei Frameworkoptionen zur Auswahl: Webformulare, Webseiten und ASP.NET MVC. Natürlich hat jedes Framework seine individuellen Vorteile. Wenn Sie sich aber für eines entscheiden, bedeutet das nicht, dass Sie ein anderes nicht verwenden können. Sie könnten sogar irgendwann in der Zukunft alle zusammen migrieren. Und alle drei Frameworks basieren auf ASP.NET – der Migration von einem Framework zu einem anderen stehen also keine technischen Hindernisse im Weg. Wenn Sie sich für eine Migration entscheiden, können Sie dank der Ähnlichkeiten zwischen Webseiten und ASP.NET MVC auch weiterhin Technologien wie NuGet, Razor, Web Deploy, IIS Express und SQL Compact verwenden, ohne Änderungen vornehmen zu müssen.

Wenn Sie Ihre Anwendung mithilfe von Webseiten erstellen und beschließen, dass Wechsel sinnvoll ist, ist die Migration von Webseiten zu ASP.NET MVC der einfachste Weg – vor allem, wenn Sie im Vorfeld einige Entwurfsentscheidungen treffen und Seiten in Ihrer Webseiten-Website gemäß ihrer Funktion in Ordner gruppieren, relative URLs für alle Ressourcen verwenden und die gesamte Geschäftslogik in den Kopfzeilen der einzelnen Seiten platzieren. Wenn die Zeit für eine Migration reif ist, werden sie feststellen, dass die Migration von Webseiten zu ASP.NET MVC – wie geplant – ganz einfach und reibungslos verläuft.

Brandon Satrom arbeitet als Senior Developer Evangelist für Microsoft in der Nähe von Austin (Texas) aus. Auf Twitter können Sie seine Beiträge unter twitter.com/BrandonSatrom verfolgen.

Clark Sell arbeitet als Senior Developer Evangelist für Microsoft von Chicago aus. Seine Blogbeiträge finden Sie unter csell.net und seine Podcasts unter DeveloperSmackdown.com. Auf Twitter können Sie seine Beiträge unter twitter.com/csell5 verfolgen.

Unser Dank gilt den folgenden technischen Experten für die Durchsicht dieses Artikels: Phil Haack und Erik Porter