Advertisement
  1. Code
  2. ASP.NET

Erstellen einer ASP.NET MVC4-Anwendung mit EF und WebAPI

Scroll to top
Read Time: 20 min

() translation by (you can also view the original English article)

ASP.NET MVC hat einen langen Weg zurückgelegt, seit "The Gu" 2007 während einer Flugzeugfahrt zu einer Konferenz einige Ideen aufgeschrieben hat. In knapp vier Jahren hat ASP.NET MVC seine vierte Veröffentlichung gesehen und bietet Entwicklern eine Umgebung das erleichtert die Entwicklung, rationalisiert Prozesse und fördert moderne Muster.


Eintauchen

Direkt einzusteigen ist eine der besten Möglichkeiten, neue Technologien in den Griff zu bekommen. Lassen Sie uns weitermachen und direkt in die Codez eintauchen!

Aufstellen

Ich werde Visual Studio 2012 Release Candidate verwenden, das hier verfügbar ist. Ich empfehle auch, SQL Server 2012 herunterzuladen, da das neue Management Studio eine dringend benötigte Verbesserung gegenüber früheren Versionen darstellt.

Sobald VS 2012 betriebsbereit ist, erstellen Sie ein neues Projekt. Gehen Sie zu Datei -> Neues Projekt und wählen Sie eine Internetanwendung aus. Es ist keine perfekte Vorlage, aber es wird die Arbeit erledigen.

Project OptionsProject OptionsProject Options

Hinweis: Der Code für diese Demoanwendung befindet sich in einem Github-Repository. Ich werde nicht jedes einzelne Stück Code in dieser App durchgehen, aber Sie werden am Ende dieses Tutorials ein gutes Verständnis einer MVC4-Anwendung haben.

Entity Framework

Ich werde Entity Framework (EF) Code First für das Datenmodell verwenden. Mit EF Code First können wir Datenbanktabellen mit nur wenigen Plain Old CLR Objects (POCO) generieren. Außerdem können wir mit EF LINQ to Entities und Lambda-Ausdrücke verwenden, was das Abfragen und Ausgeben von Befehlen vereinfacht. Ein Win-Win!

Unsere Anwendung wird eine Überprüfungsseite für die Überprüfung von ... Sachen sein. Daher muss das Datenmodell alle notwendigen Details für eine einzelne Überprüfung enthalten. Wir beginnen mit einer Klasse namens Review. Schreiben Sie die folgende Klasse in eine eigene Datei im Models-Verzeichnis:

1
// Review.cs

2
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Models/Review.cs

3
public class Review
4
{
5
    public int Id { get; set; }
6
    
7
    [Required]
8
    public string Content { get; set; }
9
    
10
    [Required]
11
    [StringLength(128)]
12
    public string Topic { get; set; }
13
    
14
    [Required]
15
    public string Email { get; set; }
16
    
17
    [Required]
18
    public bool IsAnonymous { get; set; }
19
    
20
    public int CategoryId { get; set; }
21
    public virtual Category Category { get; set; }
22
    
23
    public virtual IEnumerable<Comment> Comments { get; set; }
24
}

Die Review-Klasse verfügt über ihre Id (den Primärschlüssel), die Content-Eigenschaft zum Speichern der Bewertung, ein Topic, z. B. einen Restaurantnamen (oder einen beliebigen Namen einer Organisation), eine Email-Eigenschaft und ein IsAnonymous-Flag, um anzuzeigen, ob der Überprüfer anonym ist. Die CategoryId und die Category-Eigenschaften erstellen eine Fremdschlüsselbeziehung, um eine Bewertung an eine Category zu binden (zB: Ärzte, Zahnärzte usw.). Und zuletzt ist eine Sammlung von Comment-Objekten.

Schreiben Sie nun die Klasse Comment. Fügen Sie die neue Klasse erneut zum Models-Verzeichnis hinzu:

1
// Comment.cs

2
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Models/Comment.cs

3
public class Comment
4
{
5
    public int Id { get; set; }
6
    
7
    [Required]
8
    public string Content { get; set; }
9
    
10
    [Required]
11
    public string Email { get; set; }
12
    
13
    [Required]
14
    public bool IsAnonymous { get; set; }
15
16
    public int ReviewId { get; set; }
17
    public Review Review { get; set; }
18
}

Die Kommentarklasse hat eine Id-Eigenschaft für den Primärschlüssel, Content des Kommentars, eine Email-Eigenschaft und ein IsAnonymous-Flag für Benutzer. Dann gibt es die Eigenschaften ReviewId und Review, um eine Fremdschlüsselbeziehung zwischen Kommentaren und Bewertungen zu erstellen.

Zuletzt ist die Category-Klasse. Hier ist sein Code:

1
// Category.cs

2
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Models/Category.cs

3
public class Category
4
{
5
    public int Id { get; set; }
6
    
7
    [Required]
8
    [StringLength(32)]
9
    public string Name { get; set; }
10
}

Diese Klasse ist selbsterklärend.

Sie haben wahrscheinlich bemerkt, dass die Datenanmerkung [Required] in den obigen Klassen häufig verwendet wird. Diese bezeichnen ein Feld in der Datenbank, das keine NULL-Werte zulässt, und stellen später eine Validierung bereit. Sie können auch Ihre eigenen benutzerdefinierten Validierung-Attribute erstellen, wenn Sie dies wünschen. Zusätzlich zu [Required] haben wir für einige Eigenschaften auch das Schlüsselwort virtual verwendet; diese Eigenschaften bezeichnen Fremdschlüsselbeziehungen mit anderen Tabellen.

Um die entsprechenden Tabellen für diese Klassen zu erstellen, müssen Sie eine DbContext-Klasse erstellen. Der folgende Code erstellt eine Kontextklasse namens ReviewedContext:

1
    // https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Models/ReviewedContext.cs

2
    public class ReviewedContext : DbContext
3
    {
4
        public DbSet<Review> Reviews { get; set; }
5
        public DbSet<Category> Categories { get; set; }
6
        public DbSet<Comment> Comments { get; set; }
7
8
        public ReviewedContext()
9
        {
10
            Configuration.ProxyCreationEnabled = false;
11
        }
12
    }

Mit EF Code First können wir Datenbanktabellen mit nur wenigen Plain Old CLR Objects (POCO) generieren.

Jede Eigenschaft dieser Klasse entspricht beim Generieren der Datenbank einer Tabelle. Configuration.ProxyCreationEnabled = false; stellt sicher, dass die Entitäten als Objekte ihrer jeweiligen Klassen und nicht als Proxys abgerufen werden, was das Debuggen erheblich vereinfacht.

Als nächstes richten wir einen Datenbankinitialisierer ein. Ein Initialisierer stellt sicher, dass die Datenbank korrekt erstellt wird, wenn das Datenmodell geändert wird. Ohne Initialisierer müssen Sie die Datenbank manuell löschen, wenn Sie eine Änderung an einem Ihrer POCOs vornehmen. Es stehen verschiedene Arten von Initialisierern zur Auswahl: DropCreateDatabaseAlways und DropCreateDatabaseIfModelChanges. Die Namen sind selbsterklärend. Wir werden DropCreateDatabaseIfModelChanges verwenden.

Die Initialisierer DropCreateDatabaseAlways und DropCreateDatabaseIfModelChanges haben einen Nebeneffekt: Sie löschen die Tabellen (und damit die Daten) in der Datenbank, wenn sich die Modellstruktur ändert. EF Code First bietet jedoch eine dritte Möglichkeit zum Generieren von Datenbanken: Migrationen. Diese neue Funktion verfolgt Änderungen an der Datenbank und verliert keine Daten, wenn sich die POCO-Klassen ändern.

Hier ist der Code für unseren Initialisierer:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Models/ReviewedContextInitializer.cs

2
// http://slipsum.com/

3
public class ReviewedContextInitializer : DropCreateDatabaseIfModelChanges<ReviewedContext>
4
{
5
    protected override void Seed(ReviewedContext context)
6
    {
7
        // Use the context to seed the db.

8
    }
9
}

Die ReviewedContextInitializer-Klasse überschreibt die Seed()-Methode. Dies gibt uns die Möglichkeit, unsere Datenbank mit einigen Testdaten zu füllen. Jetzt müssen wir die Datei Global.asax besuchen und die folgende Zeile zur Methode Application_Start() hinzufügen:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Global.asax.cs

2
Database.SetInitializer(new ReviewedContextInitializer());

Lassen Sie uns einige Repositorys zum Abrufen von Daten aus der Datenbank erstellen, und wir werden mit Ninject fortfahren und Dependency Injection (DI) einrichten. Wenn Sie nicht genau wissen, was DI oder Inversion of Control (IoC) sind, dann nehmen Sie sich einen Moment Zeit, um diesen Artikel zu lesen.

Grundsätzlich besteht die Idee der Abhängigkeitsinjektion darin, eine konkrete Abhängigkeit in eine Klasse zu injizieren, im Gegensatz zur harten Codierung der Klasse, um von der konkreten Abhängigkeit abhängig zu sein. Mit anderen Worten, es ist eine Entkopplung einer konkreten Klasse von einer anderen. Wenn das immer noch schlammklar ist, schauen wir uns ein kurzes Beispiel an:

1
    public class Foo
2
    {
3
        private Bar _bar; 
4
        
5
        public Foo()
6
        {
7
            _bar = new Bar();
8
        }
9
    }

Dieser Code erstellt eine Klasse namens Foo. Es hängt von der Funktionalität eines Objekts vom Typ Bar ab, und das Bar-Objekt wird innerhalb der Foo-Klasse erstellt. Dies kann schwierig zu warten und zu testen sein, weil:

  • Foo und Bar sind eng miteinander verbunden. Infolgedessen ist die Wartung nicht ideal.
  • Foo ist von einer bestimmten Implementierung von Bar abhängig, was Unit-Tests erschwert.

Dieser Code kann mit nur wenigen Modifikationen verbessert werden. Schauen Sie sich die überarbeitete Foo-Klasse an:

1
    public class Foo
2
    {
3
        private IBar _bar;
4
        
5
        public Foo(IBar bar)
6
        {
7
            _bar = bar;
8
        }
9
    }

In knapp vier Jahren hat ASP.NET MVC seine vierte Veröffentlichung...

Nun ist die Foo-Klasse nicht von einer bestimmten Implementierung von Bar abhängig. Stattdessen wird Foo über dessen Konstruktor ein Objekt einer Klasse geliefert, die die IBar-Schnittstelle implementiert. Dieser Ansatz verbessert die Wartbarkeit erheblich und ermöglicht es uns gleichzeitig, jedes IBar-Objekt einzufügen, was es einfacher macht, unseren Code zu testen.

Nachdem wir diese kurze Beschreibung aus dem Weg geräumt haben, bringen wir Ninject zum Laufen. Starten Sie die Paketmanager-Konsole und führen Sie Install-Package Ninject.MVC3 aus. Dadurch wird Ninject zu unserem Projekt hinzugefügt.

Das erste Repository, das wir erstellen, ist das ReviewsRepository, und es implementiert die IReviewRepository-Schnittstelle. Hier ist die Schnittstelle:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Models/Abstract/IReviewRepository.cs

2
public interface IReviewRepository
3
{
4
    Review Get(int id);
5
    IQueryable<Review> GetAll();
6
    Review Add(Review review);
7
    Review Update(Review review);
8
    void Delete(int reviewId);
9
    IEnumerable<Review> GetByCategory(Category category);
10
    IEnumerable<Comment> GetReviewComments(int id);
11
}

Diese Schnittstelle stellt sicher, dass unsere Review-Repositorys die grundlegenden CRUD-Operationen bereitstellen. Wir haben auch den Nutzen, Bewertungen nach einer bestimmten Kategorie abzurufen sowie die Kommentare zu einer bestimmten Bewertung abzurufen. Lassen Sie uns nun eine konkrete Klasse schreiben, die diese Schnittstelle implementiert:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Models/Repos/ReviewRepository.cs

2
public class ReviewRepository : IReviewRepository
3
{
4
    private ReviewedContext _db { get; set; }
5
6
    public ReviewRepository()
7
        :this (new ReviewedContext())
8
    {
9
        
10
    }
11
12
    public ReviewRepository(ReviewedContext db)
13
    {
14
        _db = db;
15
    }
16
17
    public Review Get(int id)
18
    {
19
        return _db.Reviews.SingleOrDefault(r => r.Id == id);
20
    }
21
22
    public IQueryable<Review> GetAll()
23
    {
24
        return _db.Reviews;
25
    }
26
27
    public Review Add(Review review)
28
    {
29
        _db.Reviews.Add(review);
30
        _db.SaveChanges();
31
        return review;
32
    }
33
34
    public Review Update(Review review)
35
    {
36
        _db.Entry(review).State = EntityState.Modified;
37
        _db.SaveChanges();
38
        return review;
39
    }
40
41
    public void Delete(int reviewId)
42
    {
43
        var review = Get(reviewId);
44
        _db.Reviews.Remove(review);
45
    }
46
47
    public IEnumerable<Review> GetByCategory(Category category)
48
    {
49
        return _db.Reviews.Where(r => r.CategoryId == category.Id);
50
    }
51
52
    public IEnumerable<Comment> GetReviewComments(int id)
53
    {
54
        return _db.Comments.Where(c => c.ReviewId == id);
55
    }
56
}

WebAPI ist ein MVC-ähnliches Framework, mit dem wir einfach eine RESTful-API erstellen können...

Dieses Repository basiert auf einem ReviewedContext-Objekt, das als Klassenvariable gespeichert wird. Dadurch können wir LINQ in allen Methoden des Repositorys verwenden, was die Datenbankinteraktion vereinfacht.

WebAPI hat eine nette Funktion, mit der wir unser eigenes DI-Framework hinzufügen können. Diese Funktion würde den Rahmen dieses Tutorials sprengen, also lesen Sie unbedingt diesen Artikel, um dieses Setup zu erhalten.

Einer der wichtigsten Speicherorte für unseren Code ist der Ordner App_Start, der eine Datei namens NinjectCommonWeb.cs enthält (bei der Installation von Ninject wird diese Datei automatisch zu App_Start hinzugefügt). Diese Datei enthält eine statische Klasse namens NinjectWebCommon und eine Methode namens RegisterServices(). Fügen Sie in dieser Methode den folgenden Code hinzu:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/App_Start/NinjectWebCommon.cs

2
kernel.Bind<IReviewRepository>().To<ReviewRepository>();
3
kernel.Bind<ICategoriesRepository>().To<CategoriesRepository>();
4
kernel.Bind<ICommentsRepository>().To<CommentsRepository>();
5
6
GlobalConfiguration.Configuration.DependencyResolver = new NinjectResolver(kernel);

Die ersten drei Anweisungen binden eine Schnittstelle an eine konkrete Implementierung der Schnittstelle, und die vierte Zeile richtet die DI für WebAPI ein (die im oben genannten Artikel behandelte Funktion).

WebAPI

Lassen Sie uns nun die Controller für die API erstellen. WebAPI ist ein MVC-ähnliches Framework, mit dem wir einfach einen RESTful-Dienst erstellen können, und es kann innerhalb einer MVC4-Anwendung, in einem eigenen Projekt ausgeführt oder außerhalb von IIS selbst gehostet werden. Aber das ist nicht alles; Es hat viele andere Funktionen, wie zum Beispiel: Inhaltsverhandlung (um die Daten automatisch in ein beliebiges Format zu serialisieren), Modellbindung, Validierung und vieles mehr.

Wir müssen zuerst einen Endpunkt mit WebAPI erstellen, und wir tun dies, indem wir eine Klasse erstellen, die ApiController erbt. Damit anzufangen ist recht einfach. Visual Studio 2012 verfügt über ein neues Feature, das einen neuen, teilweise gerüsteten Controller erstellt.

Create ApiControllerCreate ApiControllerCreate ApiController

Dadurch wird eine Controller-Klasse mit einigen bereits für Sie definierten Methoden erstellt. Hier ist ein Beispiel:

1
// GET api/default1

2
public IEnumerable<string> Get()
3
{
4
    return new string[] { "value1", "value2" };
5
}
6
7
// GET api/default1/5

8
public string Get(int id)
9
{
10
    return "value";
11
}
12
13
// POST api/default1

14
public void Post(string value)
15
{
16
}
17
18
// PUT api/default1/5

19
public void Put(int id, string value)
20
{
21
}
22
23
// DELETE api/default1/5

24
public void Delete(int id)
25
{
26
}

Die Methodennamen entsprechen dem HTTP-Verb, das sie repräsentieren. Wir erstellen jetzt die ReviewsController-Klasse. Der Code ist etwas lang, aber ziemlich einfach.

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Controllers/ReviewsController.cs

2
public class ReviewsController : ApiController
3
{
4
    private ICategoriesRepository _categoriesRepository { get; set; }
5
    private IReviewRepository _reviewRepository { get; set; }
6
7
    public ReviewsController(IReviewRepository reviewRepository, ICategoriesRepository categoriesRepository)
8
    {
9
        _reviewRepository = reviewRepository;
10
        _categoriesRepository = categoriesRepository;
11
    }
12
13
    // GET api/review

14
    public IEnumerable<Review> Get()
15
    {
16
        var reviews = _reviewRepository.GetAll();
17
        return reviews;
18
    }
19
20
    // GET api/review/5

21
    public HttpResponseMessage Get(int id)
22
    {
23
        var category = _reviewRepository.Get(id);
24
        if (category == null)
25
        {
26
            return Request.CreateResponse(HttpStatusCode.NotFound);
27
        }
28
        return Request.CreateResponse(HttpStatusCode.OK, category);
29
    }
30
31
    // POST api/review

32
    public HttpResponseMessage Post(Review review)
33
    {
34
        var response = Request.CreateResponse(HttpStatusCode.Created, review);
35
        // Get the url to retrieve the newly created review.

36
        response.Headers.Location = new Uri(Request.RequestUri, string.Format("reviews/{0}", review.Id));
37
        _reviewRepository.Add(review);
38
        return response;
39
    }
40
41
    // PUT api/review/5

42
    public void Put(Review review)
43
    {
44
        _reviewRepository.Update(review);
45
    }
46
47
    // DELETE api/review/5

48
    public HttpResponseMessage Delete(int id)
49
    {
50
        _reviewRepository.Delete(id);
51
        return Request.CreateResponse(HttpStatusCode.NoContent);
52
    }
53
54
    // GET api/reviews/categories/{category}

55
    public HttpResponseMessage GetByCategory(string category)
56
    {
57
58
        var findCategory = _categoriesRepository.GetByName(category);
59
        if (findCategory == null)
60
        {
61
            return Request.CreateResponse(HttpStatusCode.NotFound);
62
        }
63
        return Request.CreateResponse(HttpStatusCode.OK,_reviewRepository.GetByCategory(findCategory));
64
    }
65
66
    // GET api/reviews/comments/{id}

67
    public HttpResponseMessage GetReviewComments(int id)
68
    {
69
70
        var reviewComments = _reviewRepository.GetReviewComments(id);
71
        if (reviewComments == null)
72
        {
73
            return Request.CreateResponse(HttpStatusCode.NotFound);
74
        }
75
        return Request.CreateResponse(HttpStatusCode.OK, reviewComments);
76
    }
77
}

Dieser Code verwendet IReviewRepository- und ICategoriesRepository-Objekte, um die entsprechende Aktion auszuführen (zB: Abrufen von Daten für GET-Anforderungen, Hinzufügen von Daten mit POST-Anforderungen usw.). Diese Repositorys werden mit Ninject über Constructor Injection injiziert.

Wenn Sie Fiddler noch nicht haben, holen Sie es sich jetzt – auch wenn Sie kein .NET-Entwickler sind.

Beachten Sie, dass einige der Methoden unterschiedliche Datentypen zurückgeben. WebAPI gibt uns die Möglichkeit, einen Nicht-String-Datentyp (wie IEnumerable<Review>) zurückzugeben und das Objekt zu serialisieren, um es in der Serverantwort zu senden. Sie können auch die neue HttpResonseMessage-Klasse verwenden, um zusammen mit den zurückgegebenen Daten einen bestimmten HTTP-Statuscode zurückzugeben. Eine Möglichkeit zum Erstellen eines HttpResponseMessage-Objekts besteht darin, Request.CreateResponse(responseCode, data) aufzurufen.

Wir können unser WebAPI-Projekt mit einem Tool wie Fiddler2 richtig testen. Wenn Sie Fiddler noch nicht haben, holen Sie es sich jetzt – auch wenn Sie kein .NET-Entwickler sind. Fiddler ist ein fantastisches HTTP-Debugging-Tool. Sobald Sie Fiddler ausführen, klicken Sie auf RequestBuilder und geben Sie die API-URL ein, die Sie testen möchten. Wählen Sie dann den entsprechenden Anforderungstyp aus. Wenn Sie eine POST-Anfrage stellen, geben Sie unbedingt einen Content-Type: application/json-Header an und fügen Sie dann eine gültige JSON-Struktur in den Anfragetext ein. Das folgende Bild zeigt eine unformatierte JSON POST-Anfrage an die api/reviews-URL:

Wenn Sie die Anfrage senden, sehen Sie etwa das folgende Bild:

Beachten Sie, dass der Statuscode der POST-Anforderung 201 ist. Die WebAPI leistet hervorragende Arbeit, um den richtigen Statuscode für einen RESTfull-Webdienst zurückzugeben. Viel Spaß mit Fiddler2, es ist ein fantastisches Werkzeug!

Mit WebAPI können Sie das Routing für die Controller festlegen (genau wie MVC). In MVC4 wird dem Ordner App_Start eine Datei RouteConfig.cs hinzugefügt. Routen für ein WebAPI-Projekt sind wie MVC-Routen.

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/App_Start/RouteConfig.cs

2
routes.MapHttpRoute(
3
    name: "GetReviewComments",
4
    routeTemplate: "api/reviews/comments/{id}",
5
    defaults: new { id = RouteParameter.Optional, controller = "Reviews", action = "GetReviewComments" }
6
);
7
8
routes.MapHttpRoute(
9
    name: "GetByCategories",
10
    routeTemplate: "api/reviews/categories/{category}",
11
    defaults: new { category = RouteParameter.Optional, controller = "Reviews", action = "GetByCategory" }
12
);
13
14
routes.MapHttpRoute(
15
    name: "DefaultApi",
16
    routeTemplate: "api/{controller}/{id}",
17
    defaults: new { id = RouteParameter.Optional }
18
);

Die DefaultApi-Route wird automatisch von Visual Studio generiert. Die anderen beiden Routen sind benutzerdefiniert und werden bestimmten Methoden auf dem Reviews-Controller zugeordnet. Es gibt viele Artikel und Tutorials, die gute Informationen zum Routing bieten. Achten Sie darauf, dieses zu überprüfen.

MVC4

Das deckt viel von dem ab, was WebAPI zu bieten hat. Als Nächstes schreiben wir einige Methoden zum Anzeigen der Daten. Wir werden die API in Kürze verbrauchen, aber vorerst verwenden wir die Repositorys in unserem HomeController. Ein HomeController wurde von Visual Studio erstellt; Lassen Sie uns einfach seine Methoden ändern, um die Daten anzuzeigen. Lassen Sie uns zunächst eine Liste der Kategorien in der Index-Methode abrufen.

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Controllers/HomeController.cs

2
private ICategoriesRepository _categoriesRepository { get; set; }
3
private IReviewRepository _reviewRepository { get; set; }
4
5
public HomeController(ICategoriesRepository categoriesRepository, IReviewRepository reviewRepository)
6
{
7
    _categoriesRepository = categoriesRepository;
8
    _reviewRepository = reviewRepository;
9
}
10
11
public ActionResult Index()
12
{
13
    var categories = _categoriesRepository.GetAll();
14
    return View(categories);
15
}

Hier verwenden wir weiterhin DI, indem wir die Repositorys als Parameter für den HomeController-Konstruktor akzeptieren. Ninject spritzt uns automatisch die passenden Betonklassen ein. Als Nächstes fügen wir der Index-Ansicht etwas Code hinzu, um die Kategorien anzuzeigen:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Views/Home/Index.cshtml

2
@model IEnumerable<Reviewed.Models.Category>
3
4
<h3>Pick a Category:</h3>
5
<ul class="round">

6
    @foreach(var category in Model)
7
    {
8
        <li>
9
        <a href="@Url.Action("Reviews", new { id = @category.Name} )">@category.Name</a>
10
        </li>
11
    }
12
</ul>

Dadurch wird eine Liste von Kategorien erstellt, auf die Benutzer klicken können. Fügen Sie nun HomeController eine neue Methode hinzu, die eine Review abruft. Wir nennen diese Methode Reviews, die hier gezeigt werden:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Controllers/HomeController.cs

2
public ActionResult Reviews(string id)
3
{
4
    List<Review> reviews = new List<Review>();
5
    if (!string.IsNullOrWhiteSpace(id))
6
    {
7
        reviews = _reviewRepository.GetByCategory(_categoriesRepository.GetByName(id)).ToList();
8
    }
9
    else
10
    {
11
        reviews = _reviewRepository.GetAll().ToList();
12
    }
13
14
    foreach (var review in reviews)
15
    {
16
        var comments = _reviewRepository.GetReviewComments(review.Id);
17
        review.Comments = comments.ToList();
18
    }
19
    return View(reviews);
20
}

Da für /{controller}/{action}/{id} bereits eine Route existiert, können Sie eine URL wie Home/Reviews/Doctors verwenden. Die Routing-Engine übergibt "Doctors" als id-Parameter an die Reviews-Methode. Wir verwenden die id als Kategorie und rufen alle Bewertungen ab, die dieser Kategorie zugeordnet sind. Wenn jedoch keine Kategorie angegeben ist, rufen wir einfach alle Bewertungen in der Datenbank ab. Sobald wir alle Bewertungen haben, übergeben wir die Bewertungsliste an die Ansicht. Schauen wir uns jetzt die Ansicht an:

1
// https://github.com/jcreamer898/NetTutsMvcEf/blob/master/Reviewed/Views/Home/Reviews.cshtml

2
<div class="reviews">

3
@foreach(var review in Model)
4
{
5
    <h3>@review.Topic</h3>
6
    <p>
7
        @review.Content
8
    </p>
9
    
10
    var hasComments = review.Comments.Count > 0 ? "is-comments" : null;
11
    
12
    <ul class="@hasComments">

13
        @foreach(var comment in review.Comments)
14
        {
15
            <li>
16
                <strong>@(!comment.IsAnonymous ? string.Format("{0} says,", comment.Email) : "")</strong>
17
                <blockquote>@comment.Content</blockquote>
18
            </li>
19
        }
20
    </ul>
21
}
22
</div>

Dieser Code verwendet eine neue Funktion von MVC4. Das class-Attribut des <ul/>-Elements wird nicht im HTML-Code angezeigt, wenn hasComments null ist. Lesen Sie hier mehr über diese Funktion.

JavaScript

Keine moderne Web-App ist ohne JavaScript vollständig, und wir verwenden es, um unseren WebAPI-Dienst zu nutzen. Dafür verwenden wir Backbone.js; Also, gehen Sie weiter und laden Sie Backbone und seine Abhängigkeit Underscore herunter. Legen Sie die JavaScript-Dateien im Verzeichnis Scripts ab.

Wir werden eine weitere neue MVC4-Funktion namens Skriptbündelung nutzen. Im Ordner App_Start finden Sie die Datei BundleConfig.cs. In dieser Datei können Sie MVC4 so konfigurieren, dass JavaScript-Dateien gebündelt werden. Öffnen Sie es und fügen Sie ein neues Bündel wie folgt hinzu:

1
bundles.Add(new ScriptBundle("~/bundles/backbone").Include(
2
        "~/Scripts/underscore*",
3
        "~/Scripts/backbone*"));

Fügen Sie dann in der Datei /Views/Shared/_Layout.cshtml am unteren Rand des Seitentexts Folgendes hinzu:

1
@Scripts.Render("~/bundles/backbone")

Dadurch werden Ihre Skripts gebündelt, wenn sich Ihre Anwendung im Debug-Modus befindet, oder lassen Sie sie mit deaktivierter Funktion in Ruhe.

Der MVC4-Code, den wir zum Abrufen der Überprüfungsliste geschrieben haben, ist eine gute Möglichkeit, sie anzuzeigen, aber die ganze neue Hawtness verwendet Ajax. Lassen Sie uns den Code so umgestalten, dass er Backbone.js verwendet. Über JavaScript rufen wir die Ansichten asynchron ab, nachdem die Seite geladen wurde. Erstellen Sie im scripts-Ordner eine neue Datei namens home.js. Fügen Sie dieser Datei den folgenden Code hinzu:

1
var Review = Backbone.Model.extend();
2
var Reviews = Backbone.Collection.extend({
3
    model: Review,
4
    url: '/api/reviews'
5
});
6
var Comment = Backbone.Model.extend({});
7
var Comments = Backbone.Collection.extend({
8
    model: Comment,
9
    url: '/api/reviews/comments/'
10
});

Dies sind die JavaScript-Datenmodelle, die jeweils einer URL zum Abrufen von Daten vom WebAPI-Dienst entsprechen. Schreiben wir nun die Ansicht:

1
var ListReviews = Backbone.View.extend({
2
    el: '.reviews',
3
    initialize: function() {
4
        this.collection.on('reset', this.render, this);
5
        this.collection.fetch();
6
    },
7
    render: function() {
8
        this.collection.each(this.renderItem, this);
9
    },
10
    renderItem: function(model) {
11
        var view = new ReviewItem({
12
            model: model
13
        });
14
        this.$el.append(view.el);
15
    }
16
});

Diese Ansicht gilt für die gesamte Liste der Bewertungen. Wenn die Methode fetch() der Sammlung aufgerufen wird, löst sie das reset-Ereignis aus und ruft dann render() auf. Der dritte Parameter, der an die on()-Methode übergeben wird, ist der Gültigkeitsbereich oder was this im render()-Callback sein wird. Rufen Sie in der Methode render() die Methode each() der Sammlung auf und übergeben Sie die Methode renderItem(). Die Methode renderItem() wird für jedes Element in der Sammlung aufgerufen, wodurch für jede Überprüfung ein neues ReviewItem generiert wird.

Der Code für ReviewItem folgt:

1
var ReviewItem = Backbone.View.extend({
2
    events: {
3
        'click a': 'getComments'
4
    },
5
    tagName: 'li',
6
    initialize: function () {
7
        
8
        this.template = _.template($('#reviewsTemplate').html());
9
        this.collection = new Comments();
10
        this.collection.on('reset', this.loadComments, this);
11
        this.render();
12
    },
13
    render: function () {
14
        var html = this.template(this.model.toJSON());
15
        this.$el.append(html);
16
    },
17
    getComments: function () {
18
        this.collection.fetch({
19
            data: {
20
                Id: this.model.get('Id')
21
            }
22
        });
23
    },
24
    loadComments: function () {
25
        var self = this,
26
            item;
27
        this.comments = this.$el.find('ul');
28
        this.collection.each(function (comment) {
29
            item = new CommentItem({
30
                model: comment
31
            });
32
            self.comments.append(item.el);
33
        });
34
        
35
        this.$el.find('a').hide();
36
    }
37
});

WebAPI ist eine fantastische Ergänzung zum ASP.NET-Stack; eine funktionsreiche REST-basierte API war noch nie so einfach.

Die ReviewItem-Ansicht ist für das Rendern jeder einzelnen Überprüfung verantwortlich. Die Methode initialize() kompiliert die Vorlage, die zum Anzeigen jeder Überprüfung verwendet wird; Diese Vorlage befindet sich in einem <script/>-Element. Backbone zieht die Vorlage aus dem <script/>-Element und kombiniert sie mit der Überprüfung.

Außerdem ist ein click-Event-Handler eingerichtet, um die Kommentare für jede Überprüfung zu laden. Wenn auf den Link geklickt wird, wird die Methode getComments() aufgerufen, die die Kommentare abruft, indem eine Id an den WebAPI-Dienst übergeben wird. Die Methode fetch() ist lediglich eine Abstraktion der $.ajax-Methode von jQuery, sodass normale Ajax-Parameter wie data im fetch()-Aufruf übergeben werden können. Schließlich wird die Methode loadComments() ausgelöst und eine neue CommentItem-Ansicht für jeden zurückgegebenen Kommentar erstellt. Der tagName in dieser Ansicht stellt sicher, dass die Ansicht mit einem <li/> als $el-Eigenschaft erstellt wird.

Sehen wir uns als Nächstes die CommentItem-Ansicht an:

1
var CommentItem = Backbone.View.extend({
2
    tagName: 'li',
3
    initialize: function () {
4
        this.template = _.template($('#commentsTemplate').html());
5
        this.render();
6
    },
7
    render: function () {
8
        var html = this.template(this.model.toJSON());
9
        this.$el.html(html);
10
    }
11
    
12
});

Dies ist eine einfache Ansicht, die jeden Kommentar rendert. Ändern wir nun die Ansicht Review.cshtml wie folgt:

1
@model IEnumerable<Reviewed.Models.Review>
2
3
@section scripts {
4
    <script type="text/javascript" src="/Scripts/home.js"></script>
5
}
6
7
<div class="reviews">
8
    <ul></ul>
9
</div>
10
11
<script type="text/html" id="reviewsTemplate">
12
13
    <h3><%= Topic %></h3>

14
    <p>
15
        <%= Content %>
16
    </p>

17
    
18
    <a href="#comments">Load Comments</a>

19
    <ul></ul>

20
21
</script>
22
23
<script type="text/html" id="commentsTemplate">
24
<li>
25
    <strong><%= !IsAnonymous ? Email + " says," : "" %></strong>

26
    <blockquote><%= Content %></blockquote>

27
</li>       

28
</script>

Beachten Sie die @section scripts im obigen Code. Dies ist keine neue Funktion von MVC4, aber es ist ein großartiges Werkzeug, um bestimmte Teile von JavaScript zu rendern. In der Datei _layout.cshtml gibt es auch eine @RenderSection("scripts", required: false), die den in der Ansicht definierten Abschnitt rendert. Die <script/>-Elemente sind Unterstrichvorlagen zum Rendern von Inhalten. Sie folgen einer Ruby-ähnlichen Syntax, und alles innerhalb von <% %> wird als Anweisung ausgewertet. Alles innerhalb von <%= %> wird in HTML ausgegeben. Schleifen und bedingte Anweisungen können wie folgt verwendet werden:

1
<ul>
2
<script type="text/html" id="template">
3
<% for (var i = 0; i < list.length; i++) { %>
4
    <li><%= list[i] %></li>
5
<% } %>
6
</ul>
7
8
<% if (someCondition) { %>
9
    <%= output %>
10
<% } %>
11
</script>

Das ist die Vorlage. Um es zu verwenden, gehen Sie wie folgt vor:

1
var template = _.template($('#template').html());
2
var html = template({
3
    someCondition: true,
4
    output: 'hello world',
5
    list: ['foo', 'bar', 'bam']
6
});

Es gibt viele JavaScript-Templating-Frameworks im Web: Handlebars.js, mustache.js und Hogan.js sind sehr beliebt. Seien Sie sicher und überprüfen Sie sie und wählen Sie diejenige aus, die für Sie funktioniert.

Abschluss

WebAPI ist eine fantastische Ergänzung zum ASP.NET-Stack; eine funktionsreiche REST-basierte API war noch nie so einfach. Es gibt viele großartige neue Funktionen in MVC4. Seien Sie sicher und überprüfen Sie sie! Wie bereits erwähnt, ist der Code für dieses Beispiel auf Github verfügbar. Das war's!

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.