asp.net mvc stefan lieser email: stefan@lieser-online.destefan@lieser-online.de web: :

Post on 06-Apr-2015

113 Views

Category:

Documents

0 Downloads

Preview:

Click to see full reader

TRANSCRIPT

ASP.NET MVC

Stefan Lieser

Email: stefan@lieser-online.deWeb: http://www.lieser-online.de

WAS IST LEGACY CODE?

Code ohne Tests

ist Legacy Code

Michael Feathers

Code without tests is bad code. It doesn't matter how well written it is; it doesn't matter how pretty or object-oriented or well-encapsulated it is. With tests, we can change the behavior of our code quickly and verifiably. Without them, we really don't know if our code is getting better or worse.

Agenda

Einführung MVC Pattern Routing Dependency Injection Tests

Controller, View, Routing

Einführung

ZieleFunktionsweiseWas ändert sich gegenüber WebForms?

Einführung

Erste Präsentation durch Scott GuthrieALT.NET Conference Oktober 2007 Austinhttp://www.hanselman.com/silverlight/ScottGuAtAltnetConf/

Bisher CTP1, CTP2, Source Drop,sozusagen pre-CTP3

Ziele

Testability Interfaces, abstract base classes (Mock

Objects) Designed with TDD in mind

Extensibility Keine sealed classes

Pluggability z.B. Austausch der ViewEngine

Einführung

Map URL to Class Front Controller

Einführung

Basierend auf ASP.NET Standard ViewEngine verwendet .aspx,

MasterPages und CSS Kein PostBack Kein ViewState Kein WebForms Keine UserControls Kein ASP.NET AJAX

Auf Wiedersehen

Ok, jetzt geht‘s los

Verwendete Komponenten

ASP.NET MVC (Microsoft) http://www.codeplex.com/aspnet

MVCContrib (Community) http://www.codeplex.com/MVCContrib

Castle Windsor http://www.castleproject.org/container/

Verwendete Komponenten

Rhino.Mocks http://www.ayende.com/projects/rhino-

mocks.aspx

NUnit http://www.nunit.org

ReSharper 4.0 http://www.jetbrains.com/resharper/ http://www.jetbrains.net/confluence/display/

ReSharper/ReSharper+4.0+Nightly+Builds

Model View Controller

und andere Patterns

Model View Controller

Ziele Entkopplung Separation of concerns Single responsibility principle

Model: enthält die Daten View: präsentiert die Daten Controller: vermittelt zwischen Model

und View

Abgrenzung zu MVP

MVP geht weiter als MVC MVC Model: nicht zwangsläufig ein

Business Domain Modell sondern häufig ein Application Modell

MVC Varianten

Front Controller Supervising Controller Passive View

Routing

http://localhost/was/kommt/jetzt

Wie wird eine URL interpretiert?

Routing ASP.NET WebForms

Routing ASP.NET MVC

public class ShopController : Controller{ public RenderViewResult Artikel(string id) { Artikel artikel = repository.Get(id); return RenderView("Artikel", artikel); }}

Der Unterschied

ASP.NET WebFormsView first

ASP.NET MVCController first

Routing ASP.NET MVC

Keine fixe Zuordnung von URL zu Verzeichnissen und Dateien

Stattdessen bestimmen Routingregeln wie eine URL auf Controller und Action abgebildet wird.

Routing Tabelle wird in Global.asax initialisiert.

System.Web.Routing ist Bestandteil des .NET Framework 3.5 SP1

Routing Map

routes.MapRoute( "Default", "{controller}/{action}/{id}“, new { controller = "Home", action = "Index", id = "" }, new { controller = @"[^\.]*" });

Name URL mit Parametern Parameter Defaults Parameter Constraints

Controller, Action und Parameter

{controller}/{action}/{id} URL -> Routing ->

Controller.Action(…) /Backlog/Item/5public class BacklogController : Controller {

public RenderViewResult Item(string id) { BacklogItem item = repository.Get(id); return RenderView("Item", item); }}

Routing

/BlogsBlogsController.List("Lieser", "01.01.2008", "")

/Blogs/Meier/05.07.2006/ein-artikelBlogsController.List("Meier", "05.07.2006", "")

routes.MapRoute( "Blogs", "Blogs/{username}/{date}/{article}", new { controller = "Blogs", action="List", username = "Lieser", date = "01.01.2008", article = "" });

Routing

Signatur MapRoute:(string name, string url, object defaults, object constraints)

Warum sind defaults und constraints vom Typ object? Parameter!!

Beachte den anonymen Typ!new { controller = "…", action = "…", myParameter = "42" }

controller und action müssen vorhanden sein, sonst Laufzeitfehler.

Routing

DemoÄndern der Routing Map

BlogsController

Routing „rückwärts“

Erzeugen einer URL aus Controller und Action:

Erzeugt die URL /Backlog/Add gemäß der Routingregel

DRY – Don‘t repeat yourself Refactoring Intellisense

<%= Html.ActionLink<BacklogController>( a => a.Add(), "Hinzufügen")%>

Controller

Controller

Implementiert IController Alle public methods sind Actions [NonAction] verhindert das Jede Action liefert ein ActionResult

zurück Das ActionResult bestimmt was als

nächstes passiert

Vom Controller zum View

Controller Action liefert ein ActionResult

ExecuteResult(ControllerContext context) wird aufgerufen und rendert den View.

public BacklogController : Controller { public ActionResult Index() { return RenderView("Index"); }}

RenderView

Verzeichnisstruktur bei RenderViewResult

ViewData

ViewPage<T> Property ViewData vom Typ T

ViewPage ViewData vom Typ

System.Web.Mvc.ViewData Dictionary: ViewData["Artikel"] = artikel

ViewData verwenden

<h1>Backlog Item</h1>

Id: <%= ViewData.BacklogItem.Id %><br />Name: <%= ViewData.BacklogItem.Name %><br />

public BacklogController : Controller { public ActionResult Index() { BacklogItem backlogItem = … return RenderView("Index", backlogItem); }}

Vom View zum Controller

Form

Führt zu folgendem Methodenaufruf:BacklogController.Save(string name)

<form action="/Backlog/Save" method="post" > <input type="text" name="name" id="name“ value="<%= ViewData.Backlog.Name %>" /> <input type="submit" value="Ok" name="submit“ id="submit" /></form>

Form mit Html Helper

<% using (Html.Form("Backlog", "SaveItem")) { %> <%= Html.TextBox("name", ViewData.BacklogItem.Name) %> <%= Html.SubmitButton("submit", "Ok") %><% } %>

TestsModelRouting ControllerView

Routing Tests

GetHttpContext ist eine Helper Methode (siehe Demo)

using (mocks.Record()) { context = GetHttpContext(mocks, "~/Backlog/List");}using (mocks.Playback()) { RouteData routeData = routes.GetRouteData(context); Assert.That(routeData.Values["Controller"], Is.EqualTo("Backlog")); Assert.That(routeData.Values["Action"], Is.EqualTo("List"));}

Controller Tests

ActionResult der Actions können gut getestet werden

RenderViewResult renderViewResult = backlogController.List();

Assert.That(renderViewResult.ViewName, Is.EqualTo("List"));

Assert.That(renderViewResult.ViewData, Is.TypeOf(typeof(BacklogViewData)));

Assert.That(((IViewData)renderViewResult.ViewData).ErrorMessage, Is.EqualTo(expectedErrorMessage));

View Tests

Im View steckt Code! -> Unit Tests

ASP.NET Engine erforderlich um einen View zu rendern

Bislang keine explizite Unterstützung für View Tests

Integrationstest z.B. mit WatiN möglich

Alternative View Engines

Im MVCContrib Projekt: Brail NVelocity NHaml Xslt

Dependency Injection

Controller benötigt Repository

public class BacklogController : Controller {

private IRepository<Backlog> m_Repository;

public BacklogController(IRepository<Backlog> repository) { m_Repsitory = repository; }

public RenderViewResult Show(string id) { Backlog backlog = m_Repository.Get(id); return RenderViewResult("Show", backlog); }}

Dependency Injection (DI)

ASP.NET MVC unterstützt den Einsatz beliebiger IoC Container zur Dependency Injection.

Vorteil der Dependency Injection: die Konstruktoren der Controller können Parameter erhalten um darüber die Abhängigkeiten zu definieren.

ControllerBuilder.Current.SetControllerFactory(...)

MvcContrib

Bietet Factories für Castle Windsor StructureMap Spring.NET ObjectBuilder Unity NInject

DI am Beispiel Castle Windsor

Zusätzliche Referenzen: Castle.Core.dll Castle.MicroKernel.dll Castle.Windsor.dll MvcContrib.Castle.dll

Castle

MVCContrib

DI am Beispiel Castle Windsor

protected virtual void InitializeWindsor() { if (m_Container == null) { m_Container = new WindsorContainer();

m_Container.AddComponentWithLifestyle( "repository", typeof(IRepository<>), typeof(Repository<>), LifestyleType.Transient);

m_Container.RegisterControllers( Assembly.GetExecutingAssembly());

ControllerBuilder.Current.SetControllerFactory( typeof(WindsorControllerFactory)); }}

Fragen?

Email: stefan@lieser-online.deBlog: http://www.lieser-online.de

Links

Michael Feathers,Working Effectively With Legacy Codehttp://www.objectmentor.com/resources/articles/WorkingEffectivelyWithLegacyCode.pdf

Vielen Dank!

top related