Razor Pages mit Datenbankanbindung

Wenn Sie ein Abonnement des Magazins 'DATENBANKENTWICKLER' besitzen, können Sie sich anmelden und den kompletten Artikel lesen.
Anderenfalls können Sie das Abonnement hier im Shop erwerben.

Razor Pages mit Datenbankanbindung

Nachdem Sie in weiteren Artikeln einige Grundlagen zur Programmierung von Razor Pages mit ASP.NET Core kennengelernt haben, geht es nun einen Schritt weiter: Wir wollen eine erste kleine Anwendung für den Zugriff auf die Daten einer Datenbank programmieren. Dazu beginnen wir mit einer kleinen Tabelle, für die wir eine Übersicht und eine Detailseite zum Ansehen und Bearbeiten der einzelnen Felder erstellen. Die Übersicht soll natürlich auch eine Möglichkeit zum Hinzufügen und Löschen der Datensätze bieten.

Voraussetzungen

Sie benötigen Visual Studio in der Version von 2017, die Sie mit den Komponenten wie im Artikel Visual Studio 2017 Community Edition beschrieben installieren.

Projekt erstellen

Wir erstellen ein Projekt auf Basis der Vorlage Visual C#|Web|ASP.NET Core Webanwendung (siehe Bild 1).

Projekt anlegen

Bild 1: Projekt anlegen

Im folgenden Dialog wählen Sie den Eintrag Webanwendung aus, da wir hier schon einige Elemente bekommen, die wir sonst manuell nachreichen müssten (siehe Bild 2).

Projektvorlage definieren

Bild 2: Projektvorlage definieren

Anpassen des Rahmens

Mit Rahmen meinen wir die Seite, welche die Navigationsleiste und den Fußbereich der Seite definiert. In unserem automatisch erstellten Demo-Projekt ist dies die Seite _Layout.cshtml. Wie die enthaltenen Elemente einer Webanwendung anpassen können, erfahren Sie beispielsweise im Artikel ASP.NET Core-Anwendung anpassen. In unserem Fall wollen wir die Bezeichnung unseres Projekts, das an verschiedenen Stellen in der Datei _Layout.cshtml gelandet ist, durch die Bezeichnung unserer Anwendung ersetzen – also beispielsweise Kundenverwaltung. Außerdem wollen wir das Navigationsmenü so ändern, dass es hinter den Einträgen Home und About den Eintrag Kunden anzeigt.

Damit dies geschieht, ändern wir in Bild 3 markierten Zeilen in der Datei _Layout.cshtml. Die eingerahmten Stellen markieren die zu ändernden Stellen, der Pfeil zeigt den Befehl @RenderBody(), an dem später die eigentlichen Inhalte eingefügt werden.

Datei _Layout.cshtml anpassen

Bild 3: Datei _Layout.cshtml anpassen

Den Inhalt der Index-Seite, also der Seite, die direkt beim Starten der Anwendung im Browser angezeigt wird, können Sie auch anpassen. Den ersten div-Teil mit id="myCarousel" entfernen wir komplett. Im folgenden div-Element entfernen wir die hinteren Auflistungen. Den vorderen Teil können wir wie folgt anpassen. Dabei wollen wir eine kleine Übersicht über die enthaltenen Funktionen bieten. Der Code sieht anschließend wie folgt aus:

@page
@model IndexModel
@{
     ViewData["Title"] = "Home page";
}
<div class="row">
     <div class="col-md-3">
         <h2>Die Beispielanwendung Kundenverwaltung</h2>
         <p>Diese Beispielanwendung zeigt, wie Sie eine Demo-Anwendung um Datenbankfunktionen erweitern. Dazu gehören:</p>
         <ul>
             <li>Übersichten für Kunden und andere Entitäten mit Schaltflächen zum Anzeigen oder Löschen von Kunden</li>
             <li>Anzeige der Kundendetails zum Bearbeiten der Daten</li>
             <li>Anzeige der Kundendetails zum Anlegen neuer Kundendatensätze</li>
         </ul>
     </div>
</div>

Wenn Sie die Anwendung danach starten, zeigt der Browser die Seite aus Bild 4 an. Für die wenigen Änderungen, die wir vorgenommen haben, sieht diese schon recht ordentlich aus. Ein Klick auf den Link Kunden in der Navigationsleiste zeigt nun leider noch keine Kundenseite an, was wir als Nächstes in Angriff nehmen werden.

Die angepasste Startseite

Bild 4: Die angepasste Startseite

Kunden-Klasse hinzufügen

Nun wollen wir uns um die anzuzeigenden Daten kümmern. Dazu benötigen wir eine Datenbank und ein entsprechendes Entity Data Model mit den Klassen für die einzelnen Entitäten. Wir verwenden den Code First-Ansatz, bei dem wir erst die Entitätsklassen definieren und daraus dann automatisch die Datenbank erstellen lassen. Für die Entitätsklassen legen wir direkt unterhalb des Projektordners ein neues Verzeichnis namens Models an.

In diesem Verzeichnis erstellen wir mit dem Kontextmenü-Eintrag Hinzufügen|Klasse... eine neue Klassendatei, der wir im folgenden Dialog Neues Element hinzufügen den Namen Customer geben. Wir könnten auch die deutsche Bezeichnung verwenden, aber beim Entity Framework ist es einfacher, wenn man die Bezeichnungen in englischer Sprache angibt. So kann man leichter Singular durch einfaches Anhängen des Buchstaben s in Plural verwandeln. Außerdem muss man dort nicht aufpassen, ob eine Bezeichnung gleichzeitig Singular und Plural ist (im Deutschen beispielsweise Artikel!).

Die Klasse sieht wie folgt aus und wird in der Klassendatei Customer.cs angelegt:

namespace ASPNETCoreEF.Models {
     public class Customer {
         public int ID { get; set; }
         public string FirstName { get; set; }
         public string LastName { get; set; }
         public string Street { get; set; }
         public string Zip { get; set; }
         public string City { get; set; }
     }
}

Das Feld ID soll der Primärschüssel der noch zu erstellenden Tabelle sein, in der die Daten zu den Kunden gespeichert werden. Die übrigen Felder benötigen keine weitere Erläuterung. Fremdschlüsselfelder und weitere Tabellen lassen wir aus Gründen der Übersicht zunächst außen vor.

Beachten Sie, dass durch das Anlegen der Klassendatei im Verzeichnis Models auch der Namespace auf ASPNETCoreEF.Models eingestellt wurde (ASPNETCoreEF ist der Name des Projekts und somit auch als Namespace des Projekts voreingestellt).

Datenbankkontext erstellen

Nun benötigen wir noch die Klasse, welche die Informationen der einzelnen Entitätsklassen – hier nur eine – zusammenfasst und Informationen zum Erstellen des Datenmodells speichert. Dazu fügen wir dem Projekt zunächst einen weiteren neuen Ordner namens Data hinzu. Hier legen wir eine neue Klasse namens CustomerManagementContext an. Dieser fügen wir im Kopf der Klassendatei zunächst einen Verweis auf den Namespace Microsoft.EntityFrameworkCore hinzu:

using Microsoft.EntityFrameworkCore;

Da die Customer-Klasse sich im Verzeichnis Models und somit auch im Namespace ASPNETCoreEF.Models befindet, fügen wir auch diesen Namespace noch hinzu:

using ASPNETCoreEF.Models;

Die Klasse soll die Schnittstelle DbContext implementieren. Außerdem erhält Sie ein DbSet-Objekt namens Customers, welches Elemente des Typs Customer aufnehmen soll:

public class CustomerManagementContext : DbContext {
     public CustomerManagementContext(DbContextOptions<CustomerManagementContext> options) : base(options) {
     }
     public DbSet<Customer> Customers { get; set; }
}

Wir überschreiben noch die Methode OnModelCreating und geben dort an, dass die Tabelle für die Kunden nicht Customer heißen soll, sondern im Plural Customers angelegt werden soll:

protected override void OnModelCreating(ModelBuilder modelBuilder) {
     modelBuilder.Entity<Customer>().ToTable("Customers");
}

DbContext beim Anwendungsstart registrieren

Damit die Datenbank über das DbContext-Objekt gleich nach dem Start zur Verfügung steht, fügen wir der Methode ConfigureServices der Datei noch eine entsprechende Anweisung hinzu. Die Datei Startup.cs erhält jedoch zuvor zwei zusätzliche Verweise auf die benötigten Namespaces:

using Microsoft.EntityFrameworkCore;
using ASPNETCoreEF.Data;

Dann ergänzen wir ConfigureServices wie folgt:

public void ConfigureServices(IServiceCollection services) {
     services.AddMvc();
     services.AddDbContext<CustomerManagementContext>(options => 
         options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
}

Dadurch greift das Projekt auf die Verbindungszeichenfolge zu, die wir noch in der Datei appsettings.json speichern müssen. Diese erweitern wir wie folgt:

{
   "ConnectionStrings": {
     "DefaultConnection": "Server=(localdb)mssqllocaldb;Database=CustomerManagement;ConnectRetryCount=0;        Trusted_Connection=True;MultipleActiveResultSets=true"
   },
   "Logging": {
     "IncludeScopes": false,
     "LogLevel": {
       "Default": "Warning"
     }
   }
}

Hier legen wir in der Einstellung ConnectionStrings fest, dass die Standardverbindungszeichenfolge den Server localdb verwenden soll und der Datenbankname CustomerManagement lautet. Außerdem soll Windows-Authentifizierung für den Zugriff auf die Datenbank verwendet werden (Trusted_Connection=True).

Ein paar Daten vorbereiten

Nun wollen wir noch dafür sorgen, dass die Tabelle Customers beim Start der Anwendung nicht völlig leer ist. Daher fügen wir der Anwendung im Verzeichnis Data eine neue Klassendatei namens DbInitializer.cs hinzu. Diese füllen wir wie folgt:

public class DbInitializer {
     public static void Initialize(CustomerManagementContext dbContext) {
         dbContext.Database.EnsureCreated();
         if (dbContext.Customers.Any()) {
             return;
         }
         var customers = new Customer[] {
             new Customer{FirstName="André", LastName="Minhorst", Street="Borkhofer Str. 17", Zip="47137", City="Duisburg"},
             new Customer{ FirstName="Klaus", LastName="Müller", Street="Teststr. 1", Zip="12345", City="Berlin" },
             new Customer{ FirstName="Herbert", LastName="Meier", Street="Beispielweg 2", Zip="23456", City="Bremen"}
         };
         foreach (Customer c in customers) {
             dbContext.Customers.Add(c);
         }
         dbContext.SaveChanges();
     }
}

Die Klasse enthält eine Methode namens Initialize, welche mit dem Parameter dbContext ein Objekt des Typs CustomerManagementContext entgegennimmt. Sie prüft zunächst ob die Datenbank bereits erstellt wurde und erledigt dies gegebenenfalls im gleichen Schritt mit der Methode EnsureCreated. Dann prüft sie, ob die Customers-Tabelle bereits Daten enthält. Falls ja, bricht sie an dieser Stelle ab. Anderenfalls erstellt sie ein Array mit drei Elementen des Typs Customer. Die folgende foreach-Schleife durchläuft dieses Array und fügt die einzelnen Elemente des Typs Customer zum DbSet namens Customers des dbContext hinzu. Die Änderungen werden schließlich mit der SaveChanges-Methode gespeichert.

Nun müssen wir noch dafür sorgen, dass diese Methode zum Erstellen beziehungsweise Füllen der Datenbank auch ausgeführt wird. Dazu ändern wir in der Klassendatei Program.cs die Methode Main wie folgt:

public static void Main(string[] args) {
     //            BuildWebHost(args).Run();
     var host = BuildWebHost(args);
     using (var scope = host.Services.CreateScope()) {
         var services = scope.ServiceProvider;
         var dbContext = services.GetRequiredService<CustomerManagementContext>();
         DbInitializer.Initialize(dbContext);
     }
     host.Run();
}

Dabei kommentieren wir die dort bereits vorhandene Anweisung aus. Diese ein neues Webhost-Objekt und führte direkt seine Run-Methode aus. Hier wollen wir das Erstellen der Datenbank zwischenschalten. Dazu teilen wir die Anweisung auf, sodass diese zunächst das neue Objekt mit BuildWebHost erstellt und in der neuen Variable namens host speichert. Die Run-Methode für die Variable host wird dann als letzte Anweisung ausgeführt. Dazwischen fügen wir ein paar neue Anweisungen ein. Die erste erstellt ein neues Scope-Element und speichert es in der Variablen scope. Über die Eigenschaft ServiceProvider holt die Prozedur dann ein Service-Objekt und speichert es in der Variablen services. Diees stellt dann über die Methode GetRequiredService mit dem Typ CustomerManagementContext den Datenbankkontext zur Verfügung und speichert diesen in der Variablen dbContext. Dieses Objekt übergeben wir dann der Methode Initialize der zuvor programmierten Klasse DbInitializer.

Damit alle als fehlerhaft markierten Stellen verschwinden, müssen Sie wieder die folgenden Namespaces referenzieren:

using Microsoft.Extensions.DependencyInjection;
using ASPNETCoreEF.Data;

Datenbank erstellen

Damit ist es soweit: Sie können die Anwendung nun starten und damit die Datenbank erstellen. Sobald der Browser aufgerufen wurde und die Startseite der Anwendung anzeigt, können Sie diese wieder beenden. Wir blenden dann den SQL Server-Objekt-Explorer ein (Menüeintrag Ansicht|SQL Server-Objekt-Explorer) und klappen die Einträge wie in Bild 5 auf. Wenn die Datenbank CustomerManagement dort angezeigt wird, navigieren Sie weiter zur Tabelle dbo.Customers und wählen den Kontextmenü-Eintrag Daten anzeigen aus. Damit sollten auch die soeben per Code hinzugefügten Datensätze erscheinen.

Die neue Datenbank mit den frischen angelegten Datensätzen

Bild 5: Die neue Datenbank mit den frischen angelegten Datensätzen

Die beiden Datenbankdateien CustomerManagement.mdf und CustomerManagement.ldf wurden auf dem lokalen Rechner im Verzeichnis C:Users gespeichert. Dabei wurde automatisch der Name als Datenbankname verwendet, den wir in der Verbindungszeichenfolge angegeben haben. Der Tabellenname entspricht wie von uns gewünscht der Plural-Form der verwendeten Klasse Customer, also Customers. Die Eigenschaftsnamen der Klasse Customer wurden als Feldnamen verwendet und das Feld mit dem Namen ID wurde automatisch als Primärschlüsselfeld festgelegt.

Kundenübersicht hinzufügen

Da wir neben den Kunden später noch weitere Daten zur Anwendung hinzufügen werden, legen wir für jede Entität einen eigenen Ordner an. Als Erstes benötigen wir also einen Ordner für Kunden, der die Übersicht, die Detailseite et cetera aufnehmen soll. Diesen Ordner legen wir unterhalb des Ordners Pages an, und zwar über dessen Kontextmenü-Eintrag Hinzufügen|Neuer Ordner. Der neue Ordner soll analog zur englischen Bezeichnung der Tabelle Customers heißen.

Diesem Ordner fügen wir gleich eine neue Razor-Seite hinzu, die wir Index.cshtml nennen. Eine neue Razor-Seite fügen Sie dem Ordner hinzu, indem Sie aus seinem Kontextmenü den Eintrag Hinzufügen|Razor-Seite... auswählen. Im folgenden Dialog finden Sie dann drei Möglichkeiten (siehe Bild 6):

Anlegen einer neuen Razor-Seite

Bild 6: Anlegen einer neuen Razor-Seite

  • Razor-Seite
  • Razor-Seite mit Verwendung von Entity Framework
  • Razor-Seiten mithilfe des Entity Frameworks (CRUD)

Einfache Razor-Seite anlegen

Wählen Sie die erste Option wählen, geben Sie im folgenden Schritt einfach den Namen der gewünschten Seite an und erstellen diese. Damit erhalten Sie dann eine fast leere Razor-Seite mit zugehöriger .cshtml.cs-Klassendatei, die Sie weitgehend selbst füllen müssen. Diese Option ist interessant, wenn Sie komplett neue Seiten aufbauen möchten. Die anderen beiden Optionen hören sich aber wesentlich interessanter an!

Razor-Seite mit Verwendung von Entity Framework

Die zweite Option unterscheidet sich vom Titel her von der dritten Option, weil sie offenbar nur eine Seite anlegt und nicht mehrere. Schauen wir uns diese an. Nach der Auswahl erscheint der Dialog aus Bild 7. Hier geben wir zunächst den Namen der Razor-Seite an. Diese soll Index heißen, weil wir zunächst eine Übersicht der Kunden erstellen wollen. Warum nennen wir die Seite dann nicht CustomerOverview oder ähnlich? Weil die Übersicht die Standardseite sein soll, die auch beim Aufrufen des Ordners /Customer bereits erscheint. Wird ein Ordner in der URL angegeben und keine spezielle Datei, ruft die Anwendung die Datei Index.cshtml auf, sofern diese in dem Ordner enthalten ist.

Auswahl des Inhalts der Seite

Bild 7: Auswahl des Inhalts der Seite

Direkt unter dem Namen wählen Sie aus, welche Vorlage für das Erstellen der Seite genutzt werden soll. Es gibt fünf Möglichkeiten zur Auswahl:

  • Create: Anzeige eines leeren, neuen Datensatzes
  • Delete: Bestätigungsseite zum Löschen eines Datensatzes, nachdem auf der Übersichtsseite die Löschen-Schaltfläche angeklickt wurde
  • Details: Detailansicht eines einzelnen Datensatzes ohne die Möglichkeit zum Bearbeiten
  • Edit: Detailansicht eines einzelnen Datensatzes zum Bearbeiten
  • List: Auflistung der enthaltenen Datensätze

Wir wählen hier zunächst den Eintrag List aus, da wir ja eine Übersicht erstellen wollen.

Das nächste Auswahlfeld dient der Auswahl der Klasse, für welche die Seite erstellt werden soll. In unserem Fall ist die Klasse Customer die einzige bisher erstellte Klasse, also wählen wir diese aus (siehe Bild 8).

Dies war die Leseprobe dieses Artikels.
Melden Sie sich an, um auf den vollständigen Artikel zuzugreifen.

Bitte geben Sie die Zeichenfolge in das nachfolgende Textfeld ein

Die mit einem * markierten Felder sind Pflichtfelder.

Ich habe die Datenschutzbestimmungen zur Kenntnis genommen.