Teil 4: Modelle und Datenzugriff
von Jon Galloway
Der MVC Music Store ist eine Tutorialanwendung, die schrittweise erläutert, wie ASP.NET MVC und Visual Studio für die Webentwicklung verwendet werden.
Der MVC Music Store ist eine einfache Beispielspeicherimplementierung, die Musikalben online verkauft und grundlegende Websiteverwaltung, Benutzeranmeldung und Warenkorbfunktionen implementiert.
In dieser Tutorialreihe werden alle Schritte zum Erstellen der ASP.NET MVC Music Store-Beispielanwendung beschrieben. Teil 4 behandelt Modelle und Datenzugriff.
Bisher haben wir nur Dummydaten von unseren Controllern an unsere Ansichtsvorlagen übergeben. Jetzt sind wir bereit, eine echte Datenbank zu integrieren. In diesem Tutorial erfahren Sie, wie Sie SQL Server Compact Edition (oft als SQL CE bezeichnet) als Datenbank-Engine verwenden. SQL CE ist eine kostenlose, eingebettete, dateibasierte Datenbank, die keine Installation oder Konfiguration erfordert, was sie für die lokale Entwicklung sehr praktisch macht.
Datenbankzugriff mit Entity Framework Code-First
Wir verwenden die Entity Framework-Unterstützung (EF), die in ASP.NET MVC 3-Projekten enthalten ist, um die Datenbank abzufragen und zu aktualisieren. EF ist eine flexible ORM-Daten-API (Object Relational Mapping), mit der Entwickler in einer Datenbank gespeicherte Daten objektorientiert abfragen und aktualisieren können.
Entity Framework Version 4 unterstützt ein Entwicklungsparadigma namens Code-First. Code-First ermöglicht Ihnen das Erstellen eines Modellobjekts, indem Sie einfache Klassen schreiben (auch als POCO aus "einfachen" CLR-Objekten bezeichnet) und die Datenbank sogar direkt aus Ihren Klassen erstellen.
Änderungen an unseren Modellklassen
In diesem Tutorial nutzen wir die Datenbankerstellungsfunktion in Entity Framework. Bevor wir dies tun, nehmen wir jedoch einige kleinere Änderungen an unseren Modellklassen vor, um einige Dinge hinzuzufügen, die wir später verwenden werden.
Hinzufügen der Künstlermodellklassen
Unsere Alben werden Künstlern zugeordnet, sodass wir eine einfache Modellklasse hinzufügen, um einen Künstler zu beschreiben. Fügen Sie dem Ordner Models mit dem Namen Artist.cs mithilfe des unten gezeigten Codes eine neue Klasse hinzu.
namespace MvcMusicStore.Models
{
public class Artist
{
public int ArtistId { get; set; }
public string Name { get; set; }
}
}
Aktualisieren unserer Modellklassen
Aktualisieren Sie die Album-Klasse wie unten gezeigt.
namespace MvcMusicStore.Models
{
public class Album
{
public int AlbumId { get; set; }
public int GenreId { get; set; }
public int ArtistId { get; set; }
public string Title { get; set; }
public decimal Price { get; set; }
public string AlbumArtUrl { get; set; }
public Genre Genre { get; set; }
public Artist Artist { get; set; }
}
}
Nehmen Sie als Nächstes die folgenden Aktualisierungen an der Genre-Klasse vor.
using System.Collections.Generic;
namespace MvcMusicStore.Models
{
public partial class Genre
{
public int GenreId { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public List<Album> Albums { get; set; }
}
}
Hinzufügen des Ordners "App_Data"
Wir fügen dem Projekt ein App_Data Verzeichnis hinzu, um die SQL Server Express-Datenbankdateien zu speichern. App_Data ist ein spezielles Verzeichnis in ASP.NET, das bereits über die richtigen Sicherheitszugriffsberechtigungen für den Datenbankzugriff verfügt. Wählen Sie im Menü Projekt die Option ASP.NET Ordner hinzufügen aus, und App_Data.
Erstellen einer Verbindungszeichenfolge in der web.config-Datei
Wir fügen der Konfigurationsdatei der Website einige Zeilen hinzu, damit Entity Framework weiß, wie eine Verbindung mit unserer Datenbank hergestellt werden kann. Doppelklicken Sie auf die Web.config Datei, die sich im Stammverzeichnis des Projekts befindet.
Scrollen Sie zum Ende dieser Datei, und fügen Sie einen <connectionStrings-Abschnitt> direkt über der letzten Zeile hinzu, wie unten gezeigt.
<connectionStrings>
<add name="MusicStoreEntities"
connectionString="Data Source=|DataDirectory|MvcMusicStore.sdf"
providerName="System.Data.SqlServerCe.4.0"/>
</connectionStrings>
</configuration>
Hinzufügen einer Kontextklasse
Klicken Sie mit der rechten Maustaste auf den Ordner Models, und fügen Sie eine neue Klasse mit dem Namen MusicStoreEntities.cs hinzu.
Diese Klasse stellt den Entity Framework-Datenbankkontext dar und verarbeitet unsere Erstellungs-, Lese-, Aktualisierungs- und Löschvorgänge für uns. Im Folgenden finden Sie den Code für diese Klasse.
using System.Data.Entity;
namespace MvcMusicStore.Models
{
public class MusicStoreEntities : DbContext
{
public DbSet<Album> Albums { get; set; }
public DbSet<Genre> Genres { get; set; }
}
}
Das ist es – es gibt keine andere Konfiguration, keine speziellen Schnittstellen usw. Durch Erweitern der DbContext-Basisklasse kann unsere MusicStoreEntities-Klasse unsere Datenbankvorgänge für uns verarbeiten. Nachdem sie nun eingebunden wurde, fügen wir unseren Modellklassen einige weitere Eigenschaften hinzu, um einige der zusätzlichen Informationen in unserer Datenbank zu nutzen.
Hinzufügen unserer Store-Katalogdaten
Wir nutzen ein Feature in Entity Framework, das einer neu erstellten Datenbank Seeddaten hinzufügt. Dadurch wird unser Store-Katalog vorab mit einer Liste von Genres, Künstlern und Alben gefüllt. Der MvcMusicStore-Assets.zip-Download , der die zuvor in diesem Tutorial verwendeten Websiteentwurfsdateien enthält, enthält eine Klassendatei mit diesen Startdaten, die sich in einem Ordner namens Code befindet.
Suchen Sie im Ordner Code/Models die Datei SampleData.cs, und legen Sie sie im Ordner Models in unserem Projekt ab, wie unten gezeigt.
Nun müssen wir eine Codezeile hinzufügen, um Entity Framework über diese SampleData-Klasse zu informieren. Doppelklicken Sie im Stammverzeichnis des Projekts auf die Datei Global.asax, um sie zu öffnen, und fügen Sie oben in der Application_Start-Methode die folgende Zeile hinzu.
protected void Application_Start()
{
System.Data.Entity.Database.SetInitializer(
new MvcMusicStore.Models.SampleData());
AreaRegistration.RegisterAllAreas();
RegisterGlobalFilters(GlobalFilters.Filters);
RegisterRoutes(RouteTable.Routes);
}
An diesem Punkt haben wir die erforderlichen Arbeiten zum Konfigurieren von Entity Framework für unser Projekt abgeschlossen.
Abfragen der Datenbank
Nun aktualisieren wir unseren StoreController so, dass er statt "Dummydaten" stattdessen in unsere Datenbank aufruft, um alle zugehörigen Informationen abzufragen. Wir beginnen mit der Deklarierung eines Felds auf dem StoreController, das eine instance der MusicStoreEntities-Klasse mit dem Namen storeDB enthält:
public class StoreController : Controller
{
MusicStoreEntities storeDB = new MusicStoreEntities();
Aktualisieren des Speicherindexes zum Abfragen der Datenbank
Die MusicStoreEntities-Klasse wird vom Entity Framework verwaltet und macht eine Sammlungseigenschaft für jede Tabelle in unserer Datenbank verfügbar. Aktualisieren wir die Aktion "Index" des StoreControllers, um alle Genres in unserer Datenbank abzurufen. Zuvor haben wir dies durch hartcodierende Zeichenfolgendaten gemacht. Jetzt können wir stattdessen einfach die Entity Framework-Kontext-Generes-Auflistung verwenden:
public ActionResult Index()
{
var genres = storeDB.Genres.ToList();
return View(genres);
}
Es müssen keine Änderungen an der Ansichtsvorlage vorgenommen werden, da wir immer noch das gleiche StoreIndexViewModel zurückgeben, das wir zuvor zurückgegeben haben – wir geben jetzt nur Livedaten aus unserer Datenbank zurück.
Wenn wir das Projekt erneut ausführen und die URL "/Store" aufrufen, wird nun eine Liste aller Genres in unserer Datenbank angezeigt:
Aktualisieren von Store Browse und Details zur Verwendung von Livedaten
Mit der Aktion /Store/Browse?genre=[some-genre] suchen wir nach einem Genre anhand des Namens. Wir erwarten nur ein Ergebnis, da wir niemals zwei Einträge für den gleichen Genrenamen haben sollten, und daher können wir verwenden. Single()-Erweiterung in LINQ, um das entsprechende Genre-Objekt wie folgt abzufragen (geben Sie dies noch nicht ein):
var example = storeDB.Genres.Single(g => g.Name == "Disco");
Die Single-Methode verwendet einen Lambda-Ausdruck als Parameter, der angibt, dass ein einzelnes Genre-Objekt verwendet werden soll, sodass sein Name mit dem definierten Wert übereinstimmt. Im obigen Fall laden wir ein einzelnes Genre-Objekt mit einem Name-Wert, der mit Disco übereinstimmt.
Wir nutzen ein Entity Framework-Feature, mit dem wir auch andere verwandte Entitäten angeben können, die geladen werden sollen, wenn das Genre-Objekt abgerufen wird. Dieses Feature wird als Abfrageergebnisstrukturierung bezeichnet und ermöglicht es uns, die Anzahl der Zugriffe auf die Datenbank zu reduzieren, um alle benötigten Informationen abzurufen. Wir möchten die Alben für das genre, das wir abrufen, vorab abrufen. Daher aktualisieren wir unsere Abfrage so, dass sie aus Genres.Include("Alben") eingeschlossen wird, um anzugeben, dass auch verwandte Alben verwendet werden sollen. Dies ist effizienter, da sowohl die Genre- als auch die Albumdaten in einer einzelnen Datenbankanforderung abgerufen werden.
Mit den Erläuterungen aus dem Weg, sehen Sie hier, wie unsere aktualisierte Aktion Zum Durchsuchen des Controllers aussieht:
public ActionResult Browse(string genre)
{
// Retrieve Genre and its Associated Albums from database
var genreModel = storeDB.Genres.Include("Albums")
.Single(g => g.Name == genre);
return View(genreModel);
}
Wir können jetzt die Store-Browse-Ansicht aktualisieren, um die Alben anzuzeigen, die in jedem Genre verfügbar sind. Öffnen Sie die Ansichtsvorlage (in /Views/Store/Browse.cshtml), und fügen Sie wie unten gezeigt eine Aufzählung der Alben hinzu.
@model MvcMusicStore.Models.Genre
@{
ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
<ul>
@foreach (var album in Model.Albums)
{
<li>
@album.Title
</li>
}
</ul>
Wenn Sie unsere Anwendung ausführen und zu /Store/Browse?genre=Jazz navigieren, wird angezeigt, dass unsere Ergebnisse jetzt aus der Datenbank abgerufen werden und alle Alben in unserem ausgewählten Genre angezeigt werden.
Wir nehmen die gleiche Änderung an unserer /Store/Details/[id]-URL vor und ersetzen unsere Dummydaten durch eine Datenbankabfrage, die ein Album lädt, dessen ID mit dem Parameterwert übereinstimmt.
public ActionResult Details(int id)
{
var album = storeDB.Albums.Find(id);
return View(album);
}
Wenn Sie unsere Anwendung ausführen und zu /Store/Details/1 navigieren, wird angezeigt, dass unsere Ergebnisse jetzt aus der Datenbank abgerufen werden.
Nachdem die Seite "Store-Details" so eingerichtet ist, dass ein Album anhand der Album-ID angezeigt wird, aktualisieren wir nun die Ansicht Durchsuchen , um einen Link zur Detailansicht zu erstellen. Wir verwenden Html.ActionLink genau wie beim Verknüpfen von Store Index zum Store Browse am Ende des vorherigen Abschnitts. Die vollständige Quelle für die Ansicht Durchsuchen wird unten angezeigt.
@model MvcMusicStore.Models.Genre
@{
ViewBag.Title = "Browse";
}
<h2>Browsing Genre: @Model.Name</h2>
<ul>
@foreach (var album in Model.Albums)
{
<li>
@Html.ActionLink(album.Title,
"Details", new { id = album.AlbumId })
</li>
}
</ul>
Wir können jetzt von unserer Store-Seite zu einer Genre-Seite navigieren, auf der die verfügbaren Alben aufgelistet sind, und durch Klicken auf ein Album können wir Details zu diesem Album anzeigen.