asp.net mvc stefan lieser email: [email protected]@lieser-online.de web: :

52
ASP.NET MVC Stefan Lieser Email: [email protected] Web: http://www.lieser-online.de

Upload: berend-bockrath

Post on 06-Apr-2015

113 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

ASP.NET MVC

Stefan Lieser

Email: [email protected]: http://www.lieser-online.de

Page 2: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

WAS IST LEGACY CODE?

Page 3: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Code ohne Tests

ist Legacy Code

Page 4: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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.

Page 5: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Agenda

Einführung MVC Pattern Routing Dependency Injection Tests

Controller, View, Routing

Page 6: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Einführung

ZieleFunktionsweiseWas ändert sich gegenüber WebForms?

Page 7: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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

Page 8: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Ziele

Testability Interfaces, abstract base classes (Mock

Objects) Designed with TDD in mind

Extensibility Keine sealed classes

Pluggability z.B. Austausch der ViewEngine

Page 9: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Einführung

Map URL to Class Front Controller

Page 10: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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

Page 11: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Auf Wiedersehen

Page 12: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Ok, jetzt geht‘s los

Page 13: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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/

Page 14: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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

Page 15: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Model View Controller

und andere Patterns

Page 16: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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

Page 17: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Abgrenzung zu MVP

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

Business Domain Modell sondern häufig ein Application Modell

Page 18: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

MVC Varianten

Front Controller Supervising Controller Passive View

Page 19: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Routing

http://localhost/was/kommt/jetzt

Wie wird eine URL interpretiert?

Page 20: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Routing ASP.NET WebForms

Page 21: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Routing ASP.NET MVC

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

Page 22: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Der Unterschied

ASP.NET WebFormsView first

ASP.NET MVCController first

Page 23: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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

Page 24: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Routing Map

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

Name URL mit Parametern Parameter Defaults Parameter Constraints

Page 25: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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); }}

Page 26: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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 = "" });

Page 27: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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.

Page 28: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Routing

DemoÄndern der Routing Map

BlogsController

Page 29: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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")%>

Page 30: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Controller

Page 31: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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

Page 32: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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"); }}

Page 33: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

RenderView

Verzeichnisstruktur bei RenderViewResult

Page 34: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

ViewData

ViewPage<T> Property ViewData vom Typ T

ViewPage ViewData vom Typ

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

Page 35: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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); }}

Page 36: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Vom View zum Controller

Page 37: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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>

Page 38: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Form mit Html Helper

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

Page 39: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

TestsModelRouting ControllerView

Page 40: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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"));}

Page 41: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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));

Page 42: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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

Page 43: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Alternative View Engines

Im MVCContrib Projekt: Brail NVelocity NHaml Xslt

Page 44: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Dependency Injection

Page 45: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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); }}

Page 46: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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(...)

Page 47: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

MvcContrib

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

Page 48: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

DI am Beispiel Castle Windsor

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

Castle

MVCContrib

Page 49: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

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)); }}

Page 50: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Fragen?

Email: [email protected]: http://www.lieser-online.de

Page 51: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Links

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

Page 52: ASP.NET MVC Stefan Lieser Email: stefan@lieser-online.destefan@lieser-online.de Web: :

Vielen Dank!