16 December 2007 .NET 3.5, ASP.NET, ASP.NET 3.5 Extensions, ASP.NET AJAX, ASP.NET MVC, Source Robert Muehsig

Die ASP.NET Extension CTP ist schon eine kleine Weile verfügbar, jedoch bin ich erst heute dazu gekommen, mir dies mal anzuschauen.

Da ich auf meinem privat Notebook kein Visual Studio 2008 Standard (oder höher) hab, benutze ich den Visual Web Developer 2008 (was wahrscheinlich bei vielen Leuten zutrifft).

Vorbereitung: 

ASP.NET 3.5 Extensions installieren und Visual Web Developer Web Site "MVC tauglich" machen

Nachdem man die CTP installiert hat, findet man im Visual Web Developer zwar eine Vorlage für "ASP.NET 3.5 Extensions Web Site", allerdings unterstützt die CTP momentan nur Web Projects - daher nützt uns das nur sehr wenig.

Eine Installationsanleitung befindet sich hier. Insbesondere muss man da auch die Kommentare lesen, dann wird es klarer. Aber nochmal zusammengefasst:

  • web.config unter dem Punkt "system.web"-"pages" anpassen:
         <namespaces>
            <add namespace="System.Web.Mvc"/>
            <add namespace="System.Linq"/>
         </namespaces>

(warum weiß ich auch noch nicht so genau, das bekommen wir bestimmt noch raus ;) )

  • global.asax und Default Routing unter "Application_Start()" hinzufügen:
    void Application_Start(object sender, EventArgs e) 
    {
        RouteTable.Routes.Add(new Route
        {
            Url = "[controller]/[action]/[id]",
            Defaults = new { action = "Index", id = (string)null },
            RouteHandler = typeof(MvcRouteHandler)
        }); 

    }
  • Ordnersturktur anpassen:

image

Erklärung: Unter "App_Code" befinden sich unsere Controller und Models, die Views kommen ins Root Verzeichnis.

Die Default.aspx im Rootverzeichnis ist komplett leer.

Der Ordner "Shared" ist für Masterpages, User Controls etc. gedacht.

Mein .ASPX Seiten haben kein Codefile mehr, sondern erben direkt von "System.Web.Mvc.ViewPage", z.B.:

<%@ Page Language="C#" 
MasterPageFile="~/Views/Shared/Site.master"
AutoEventWireup="true"
Inherits="System.Web.Mvc.ViewPage"
Title="Bookstorage | Search" %>

Die Masterseite ist normal, enthält aber kein "form" Tag und auch keinen ScriptManager für ASP.NET AJAX - da müsste man nochmal genau nachschauen wie  man das geschickt macht (darauf komme ich später nochmal).

Die Webapplikation strukturieren

Insgesamt ist die Website sehr simpel gehalten, daher bitte nicht wundern.

Das Model

Es gibt eine einfache "Book" Klasse sowie eine Klasse "BookCollection", welche von "List<Book>" erbt.
Der "BookCollectionManager" hat eine Methode "GetBookCollection", welche einfach so eine Collection zurückgibt.

Die Controller

Der HomeController macht nix großes bzw. sieht man das auch gut am SearchController wie ich diesen aufgebaut habe:

public class SearchController : Controller
{

    [ControllerAction]
    public void Index()
    {
        RenderView("Index");
    }

    [ControllerAction]
   public void Results(string query)
   {
        BookCollectionManager man = new BookCollectionManager();
        BookCollection data = man.GetBookCollection(query, 1);
        ViewData["BookCollection"] = data;
        RenderView("Results");
   }
}

In dem Controller geibts einmal den Index (der laut unserer Routingtabelle in der globals.asax der Default Controller ist) und sagt nur, dass er den View "Index(.aspx)" Rendern soll. Dabei sucht er genau in dieser Ordnerstruktur nach "Search" etc.

Unsere "Results" Methode ist eigentlich unsere Suchmethode und spricht unser Backend (was einfach nur eine Collection an 25 Einträgen zurück gibt) an. Die Daten werden dann in ViewData gespeichert und autoamtisch übergeben. Scott hat auch noch andere Methoden beschrieben - dies war erstmal die Einfachste. Dannach wird der View "Results" gerendert.

Daten an den Controller übergeben

Zwar haben wir jetzt unseren Controller, aber wie übergeben wir diesem was?

Ganz einfach über ein HTML Formular:

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolderContent" Runat="Server">
<form id="searchform" action="/Intro/Search/Results" method="post">
    <span>Search:</span>
    <input type="text" name="query" />
    <button type="submit">Suchen</button>
</form>

</asp:Content>

Die Form verweisst auf unsere "Results" Methode - wichtig dabei ist, dass der name des übergebenen Parameter mit dem der Methode übereinstimmt. Dies stößt unseren Controller an, welcher dann wiederrum den View rendert.

Problem mit ASP.NET AJAX:
Hier will ich nochmal kurz mein Problem mit dem ScriptManager erläutern. Da ich in der Masterpage kein "form" hab (wie im normalen ASP.NET) üblich, sondern es nur dort einsetzen möchte, wo ich es für sinnvoll halte (wie z.B. hier) und die action URL je nachdem darauf ausrichten möchte, gibt es leider ein Problem mit ASP.NET AJAX.
Dadurch kann man den ScriptManager nur unschön in die MasterPage hinzufügen, da dieser ein "<form runat="server">... verlangt. Eine Lösung gibt es bestimmt (vielleicht über das MVCToolkit) oder man fügt den ScriptManager auf den Seiten hinzu, wo man ihn benötigt. 2 Formuale (eine in der Masterpage & eine auf der Contentpage) ist irgendwie "unschön", aber da müsste man nochmal nachschauen. Microsoft macht sich selbst auch Gedanken und ich denke, da kommt noch eine bessere Integration insgesamt, weil man zwar den ScriptManager einsetzen könnte, aber dann wiederrum die Controller etc. übergeht - und das ist ja nicht Sinn der Sache.

Die übergebenen Daten darstellen

In jedem View benutze ich meine simple Masterpage, daher ist dies der wesentliche Code in der "Result.aspx":

<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolderContent" Runat="Server">
Search Results:

<table>
<%foreach (var returnBook in (BookCollection)ViewData["BookCollection"])  { %>
<tr>
    <td><%= returnBook.Title %></td>
    <td><%= returnBook.Description %></td>
    <td><%= returnBook.PicUrl %></td>
</tr>
<% } %>
</table>
</asp:Content>

Über "ViewData["BookCollection"]" greife ich auf meine Daten zu und gebe sie einfach so aus. Ob nun "var" (also ein anonymer Typ) da richtig ist, weiß ich noch nicht ganz ;) , es funktioniert jedenfalls:

image

Das Demoprojekt testen/download & Fazit

Startet nicht das Projekt wenn ihr gerade die ASPX Seiten geöffnet habt, da dann z.B. bei der Result.aspx eine Exception geworfen wird ;) - öffnet die leere Default.aspx und gebt dann manuell die jeweiligen URLs (siehe oben) ein.

Wenn man sich erstmal eingearbeitet hat, macht es sehr viel Freude, zu sehen, dass man diesmal volle Kontrolle über den HTML Code hat und es (jedenfalls für mich) klarer ist, wie man bestimmte HTML Elemente dynamisch rendert. Das es noch hier und da (ASP.NET AJAX) Schwierigkeiten gibt, ist natürlich bei so einer Preview verständlich.
Dies waren auch nur meine ersten Schritte, sodass ich wahrscheinlich noch nicht alles optimal gemacht habe ;)

[ Download Source Code ]


Written by Robert Muehsig

Software Developer - from Saxony, Germany - working on primedocs.io. Microsoft MVP & Web Geek.
Other Projects: KnowYourStack.com | ExpensiveMeeting | EinKofferVollerReisen.de