Table of Contents
Table of Contents ... 1
ASP.NET MVC – Introducere - (C#) ... 4
MVC aplicat la framework-ul Web ... 4
Istoric MVC ... 5
Exemplificari caracteristici MVC ... 6
Motor vizualizare Razor ... 6
Rezolvare dependente ... 7
Construire componente slab cuplate – Dependency Injection ... 8
Aplicare Dependency Injection in ASP.NET MVC ... 10
Structura unei aplicatii ASP.NET MVC ... 13
ASP.NET MVC si Conventii ... 14
Procesul de executie al unei aplicatii MVC (C#) ... 14
Etapele executiei unui proiect MVC Web sunt: ... 15
Modele, View-uri si Controller-e (C#) ... 15
Rutarea ... 15
Controllers... 16
Views ... 17
RenderBody() ... 20
RenderPage()... 20
RenderSection() ... 20
Creare Rute (C#) ... 20
ASP.NET MVC Controller (C#)... 39
Rezultatul actiunii – mostenite din clasa ActionResult ... 40
Creare “Action” in cadrul unui Controller (C#) ... 42
Atributul [NonAction] ... 43
ASP.NET MVC View (C#) ... 44
Ce sunt Views? ... 44
Adaugare continut la View ... 44
“HTML Helpers” - generare continut View ... 45
Pasare informatii la View... 47
Adaugare continut dinamic la View ... 49
Utilizare sectiuni ... 50
Teste pentru sectiuni ... 53
Redare optionala a sectiunilor ... 53
Utilizare vizualizari partiale – Html.Partial() ... 55
Utilizare actiuni descendente (Child Actions) ... 57
Metode Helper ... 59
Creare metode helper personalizate ... 60
Proprietati utile din HtmlHelper ... 63
Gestionarea codficarii stringurilor intr-o metoda helper ... 64
Custom helper ... 65
Creare elemente de tip Form - exmplu ... 67
Specificare ruta folosita de form ... 72
Generare element de intrare dintr-o proprietate a modelului ... 75
Utilizare helper pentru intrare, puternic tipizati ... 76
Creare elemente ce contin liste de valori ... 76
Metode helper – sabloane ... 78
Utilizare metode helper ... 79
Metode helper - sabloane ... 80
Utilizare metadata model ... 82
Excludere proprietate din scaffolding ... 83
ASP 5 - MVC 6 ... 84
Anatomia aplicatiei ... 84
Servicii ... 85
Middleware ... 85
Servere ... 86
Webroot... 86
Configurare ... 86
Pattern Options... 87
Controller ... 91
Creare proiect: nume ASPMVC6Demo... 91
Metode helper in ASP 5 ... 96
Ce sunt Tag Helpers? ... 96
Ce furnizeaza Tag Helpers? ... 96
Domeniul de vizibiltate – Tag Helpers ... 97
Suport IntelliSense pentru Tag Helpers ... 99
MVC 6 – Creare Tag Helpers personalizati ... 100
ASP.NET MVC – Introducere - (C#)
Pattern-ul Model-View-Controller (MVC) separa o aplicatie in trei componente principale:
M: model (Model).
V: vizualizare (View User Interface).
C: controler (Controller).
Observatie
Ceea ce am definit mai sus constituie pattern-ul MVC pentru UI – User Interface.
Pattern-ul MVC nu spune nimic despre modul cum accesam datele, cum interactioneaza serviciile, etc.
Interactiunea intr-o aplicatie ASP.NET MVC poate fi reprezentata astfel:
MVC aplicat la framework-ul Web
In ASP.NET MVC, MVC este translatat astfel:
Model. Model contine clase ce reprezinta domeniul aplicatiei. Aceste obiecte incapsuleaza adesea date memorate intr-o baza de date precum si cod folosit pentru a procesa datele si a executa actiuni specifice logicii aplicatiei. Cu ASP.NET MVC, acesta este vazut mai ales ca un Data Acces Layer – DAL – de un anumit tip, utilizand de exemplu Entity Framework sau NHibernate combinat cu cod specific logicii aplicatiei.
View. View defineste cum va arata interfata aplicatiei. View este un template pentru a genera in mod dinamic HTML.
Controller. Controller este o clasa speciala ce gestioneaza relatiile dintre View si Model. Controller-ul raspunde la actiunile utilizatorului, comunica cu modelul si decide ce vizualizare va afisa (daca exista una). In ASP.NET NVC, numele acestei clase contine sufixul Controller.
Istoric MVC
ASP.NET MVC 1 – 13 Martie 2009
Primele concepte de rutare si implementare MVC.
Conventii de realizare a aplicatiilor.
Structurarea directoarelor, etc.
ASP.NET MVC 2 – Martie 2010 Trasaturi principale incluse:
Model de validare bazat pe atribute.
Template-uri personalizabile pentru generare UI.
Tools-uri noi in VS IDE.
Suport pentru partitionarea aplicatiilor mari in “area”.
Suport pentru controller-i asincroni.
Noi functii ajutatoare, utilitare si API.
ASP.NET MVC 3 – Ianuraie 2011 Trasaturi noi:
Motorul de vizualizare Razor.
Suport pentru .NET 4 Data Annotations.
Imbunatatire model de validare.
Control mai mare si flexibilitate pentru rezolvarea dependentelor, filtre globale.
Suport mai bun pentru JavaScript, jQuery Validation si legatura cu JSON.
Utilizare NuGet pentru a distribui software si gestiona dependentele in toate platformele.
ASP.NET MVC 4 Trasaturi noi
ASP.NET Web API.
Template-uri pentru proiecte pe mobile.
Grupari si minimizari de fisiere.
ASP.NET Web API (referit ca Web API) este adaptat pentru a scrie servicii HTTP.
ASP.NET MVC 5 Trasaturi noi
Rutare bazata pe atribute.
ASP.NET Identity.
Generare cod automat din model.
Filtre locale, filtre globale.
ASP.NET MVC 6 ASP.NET 5
In summary, with ASP.NET 5 you gain the following foundational improvements:
New light-weight and modular HTTP request pipeline
Ability to host on IIS or self-host in your own process
Built on .NET Core, which supports true side-by-side app versioning
Ships entirely as NuGet packages
Integrated support for creating and using NuGet packages
Single aligned web stack for Web UI and Web APIs
Cloud-ready environment-based configuration
Built-in support for dependency injection
New tooling that simplifies modern web development
Build and run cross-platform ASP.NET apps on Windows, Mac and Linux
Open source and community focused
Exemplificari caracteristici MVC
Motor vizualizare Razor
In MVC 1 si 2 motorul de vizualizare se numea Web Form pentru ca folosea sintaxa din Web Forms.
Sa urmarim urmatorul exemplu cu vechea sintaxa si apoi in noua sintaxa data de Razor.
<%@ Page Language="C#" MasterPageFile="</Views/Shared/Site.Master"
Inherits="System.Web.Mvc.ViewPage<MvcMusicStore.ViewModels.StoreBrowseV iewModel>"
%>
<asp:Content ID="Content1" ContentPlaceHolderID="TitleContent"
runat="server">
Browse Albums
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="MainContent"
runat="server">
<div class="genre">
<h3><em><%: Model.Genre.Name %></em> Albums</h3>
<ul id="album-list">
<% foreach (var album in Model.Albums) { %>
<li>
<a href="<%: Url.Action("Details", new { id = album.AlbumId }) %>">
<img alt="<%: album.Title %>"
src="<%: album.AlbumArtUrl %>" />
<span><%: album.Title %></span>
</a>
</li>
<% } %>
</ul>
</div>
</asp:Content>
iar cu Razor
@model MvcMusicStore.Models.Genre
@{ViewBag.Title = "Browse Albums";}
<div class="genre">
<h3><em>@Model.Name</em> Albums</h3>
<ul id="album-list">
@foreach (var album in Model.Albums) {
<li>
<a href="@Url.Action("Details", new { id = album.AlbumId })">
<img alt="@album.Title" src="@album.AlbumArtUrl" />
<span>@album.Title</span>
</a>
</li>
}
</ul>
</div>
Observatie
Sinataxa <% %/> este inlocuita cu @. Razor vine cu o noua sintaxa. Razor nu e un limbaj de programare. Sintaxa Razor este asistata de Intellisense.
Rezolvare dependente
MVC 3 a introdus un nou concept numit dependency resolver ce simplifica utilizarea dependency injection in cadrul aplicatiei, fiind mai usor de decuplat componentele aplicatiei, facandu-le configurabile si mai usor de testat.
Suportul a fost adaugat pentru inregistrare si injectare la urmatoarele componente:
Controller
View
Action filter
Model binders
Model validation providers
Model metadata providers
Value providers.
O descriere in detaliu despre Depemdency Injection gasiti la adresa :
http://www.asp.net/mvc/overview/older-versions/hands-on-labs/aspnet-mvc-4- dependency-injection
Un alt articol despre DI/IoC gasiti la adresa : http://martinfowler.com/articles/injection.html
Rezolvare DI/IoC cu Autofac - free. Vezi http://docs.autofac.org/en/latest/index.html si un exemplu complex la adresa http://www.codeproject.com/Articles/25380/Dependency- Injection-with-Autofac
Ninject – pentru DI/IoC – free.
Integrare ASP.NET MVC cu Autofac :
http://docs.autofac.org/en/latest/integration/mvc.html
Construire componente slab cuplate – Dependency Injection
MVC permite crearea de componente care sa fie cat mai independente posibil si sa aiba cat mai putnie interdependente. Situatia ideala presupune ca fiecare componenta nu stie nimic despre alte componente din aplicatie si interactioneaza cu aceastea prin intermediul interfetelor.
Dependency Injection este un pattern ce ajuta o clasa sa separe logica crearii obiectelor dependente. Rezultatul acestei separari este un sistem slab cuplat unde nu exista o dependenta rigida intre doua implementari concrete.
Sa presupunem ca am definit o clasa in C# astfel:
public class Customer {
private DatabaseHelper helper;
public Customer() {
helper = new DatabaseHelper();
} ...
}
Clasa Customer declara o variabila de tip DatabaseHelper. Clasa DatabaseHelper se presupune ca executa anumite operatii (Select, Update, Insert, Delete) asupra unei baze de date. Constructorul clasei Customer creaza o instanta a clasei DatabaseHelper pe care o memoreaza in variabila locala helper. In acest caz clasa DatabaseHelper constituie o dependenta pentru clasa Customer, saul altfel spus clasa Customer depinde de clasa DatabaseHelper.
Proiectarea de mai sus a creat doua clase puternic cuplate. Daca in viitor vom dori sa inlocuim clasa DatabaseHelper cu o alta clasa (XMLHelper de exemplu), va trebui sa modificam codul din calasa Customer pentru ca aceasta instantiaza in mod direct clasa DatabaseHelper. Pentru a preveni aceasta problema vom folosi principiul DIP – Dependency Inversion Principle.
DIP precizeaza ca – Modulele de nivel (high level) inalt nu ar trebui sa depinda de modulele de nivel redus (low level). Ambele ar trebui sa depinda de abstractizari.
Acest lucru inseamna ca clasa Customer nu ar trebui sa depinda de o implementare concreta a clasei DatabaseHelper ci de o abstractizare a acesteia. La nivel de cod aceasta inseamna ca clasa Customer nu ar trebui sa aiba definita o variabila de tip DatabaseHelper, ci o variabila de tip interfata sau clasa abstracta.
public class Customer {
private IStorageHelper helper;
public Customer() {
helper = new DatabaseHelper();
} ...
}
Acum clasa Customer foloseste o variabila de tip IStorageHelper. IStorageHelper este o interfata implementata de clasa DatabaseHelper si alte clase de acest fel. Problema nu este inca rezolvata deoarece in constructorul clasei Customer se creaza o instanta a tipului DatabaseHelper. Rezolvarea consta in a folosi principiul IoC – Inversion of Control.
IoC precizeaza : Controlul crearii dependentelor ar trebui facut de un sistem extern si nu de clasa in cauza.
Aceasta inseamna ca in exemplul considerat, clasa Customer nu ar trebui sa creeze o instanta a tipului DatabaseHelper ci sa primeasca aceasta instanta de la un sistem extern.
Dependency Injection constituie o modalitate de a implementa IoC astfel incat
dependentele sunt « injectate » in clasa dintr-o sursa externa. Dependentele injectate pot fi primite ca parametru al constructorului clasei sau pot fi atribuite la proprietati din clasa, proprietati proiectate special in acest scop.
Exemplul devine (forma des intalnita in ASP.NET MVC):
public class Customer {
private IStorageHelper helper;
public Customer(IStorageHelper helper) {
this.helper = helper;
} ...
}
Aplicare Dependency Injection in ASP.NET MVC Exemplu despre cum este folosit DI in controller-e ASP.NET MVC.
(http://www.codeguru.com/csharp/.net/net_asp/mvc/understanding-dependency- injection.htm)
Consideram urmatoarea clasa controller:
public class HomeController : Controller {
ICustomerRepository repository = null;
public HomeController(ICustomerRepository repository) {
this.repository = repository;
}
public ActionResult Index() {
List<CustomerViewModel> data = repository.SelectAll();
return View(data);
} }
Clasa HomeController declara o variabila de tip ICustomerRepository. Interfata ICustomerRepository este proiectata sa implementeze pattern-ul Repository :
public interface ICustomerRepository {
List<CustomerViewModel> SelectAll();
CustomerViewModel SelectByID(string id);
void Insert(CustomerViewModel obj);
void Update(CustomerViewModel obj);
void Delete(CustomerViewModel obj);
}
In codul de mai sus implementarea interfetei ICustomerRepository este injectata in HomeController prin constructorul clasei. Metoda Index() apeleaza metoda SelectAll() din repository pentru a regasi o lista a tuturor obiectelor CustomerViewModel. Clasa CustomerViewModel poate avea urmatoarea definitie:
public class CustomerViewModel {
public string CustomerID { get; set; } public string CompanyName { get; set; } public string ContactName { get; set; } public string Country { get; set; } }
In codul de mai sus apare o problema in crearea instantei clasei HomeController.
ASP.NET MVC foloseste constructor fara parametri cand creaza o instanta a controller-ului. ASP.NET MVC permite de a specifica in mod explicit cum ar trebui instantiate controller-ele. Acest lucru poate fi facut prin crearea unui Controller Factory, adica a unei clase derivate din DefaultControllerFactory si care este reponsabila pentru crearea instantelor controller-elor. Dupa ce am creat o asemenea clasa trebuie sa o inregistram cu ASP.NET MVC framework.
public class MyControllerFactory:DefaultControllerFactory {
private Dictionary<string, Func<RequestContext, IController>>
controllers;
public MyControllerFactory(ICustomerRepository repository) {
controllers = new Dictionary<string,
Func<RequestContext,IController>>();
controllers["Home"] = controller =>
new HomeController(repository);
}
public override IController CreateController(
RequestContext requestContext, string controllerName)
{
if(controllers.ContainsKey(controllerName)) {
return controllers[controllerName](requestContext);
} else {
return null;
} } }
Constructorul clasei MyControllerFactory accepta o instanta de tip ICustomerRepository.
In acest mod MyControllerFactory nu depinde de nici o implementare concreta a interfetei ICustomerRepository. Clasa suprascrie metoda CreateController() din clasa de baza. Un dictionar de obiecte mentine o lista a controller-elor din aplicatie. Clasa HomeController este instantiata in constructorul clasei MyControllerFactory si este memorat cu cheia “Home”. Metoda CreateController() returneaza instanta clasei HomeController din acest dictionar.
Pentru a inregistra clasa MyControllerFactory cu ASP.NET MVC framework construim clasa urmatoare:
public class ControllerFactoryHelper {
public static IControllerFactory GetControllerFactory() {
string repositoryTypeName =
ConfigurationManager.AppSettings["repository"];
var repositoryType = Type.GetType(repositoryTypeName);
var repository = Activator.CreateInstance(repositoryType);
IControllerFactory factory = new MyControllerFactory(
repository as ICustomerRepository);
return factory;
} }
Clasa ControllerFactoryHelper are o metoda statica, GetControllerFactory(), care are ca sarcina sa instantieze clasa MyControllerFactory si sa pregateasca inregistrarea. In acest loc este nevoie de implementarea concreta a interfetei ICustomerRepository.
Constructorul clasei MyControllerFactory are nevoie de un obiect ce implementeaza ICustomerRepository. Codul de mai sus presupune ca aceste detalii sunt memorate in sectiunea <appSettings> din fisierul de configurare:
<appSettings>
...
<add key="repository"
value="DIDemo.Repositories.CustomerRepository"/>
</appSettings>
Metoda GetControllerFactory() citeste cheia pentru repository si creaza o instanta a tipului DIDemo.Repositories.CustomerRepository folosind reflection. In continuare se instantiaza MyControllerFactory prin pasarea acestui obiect. Obiectul factory este apoi returnat apelantului.
Ultima etapa consta in a inregistra factory controller, ceea ce inseamna adaugarea urmatorului cod in Glabal.asax:
protected void Application_Start() {
AreaRegistration.RegisterAllAreas();
RouteConfig.RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
ControllerFactoryHelper.GetControllerFactory());
}
In codul ce trateaza evenimentul Start al aplicatiei, Application_Start(), instanta ControllerBuilder apeleaza metoda SetControllerFactory(). Obiectul factory returnat de metoda GetControllerFactory() este pasat ca parametru la metoda
SetControllerFactory().
End DI
Structura unei aplicatii ASP.NET MVC
Director Scop
/Controllers Contine clasele pentru Controller ce gestioneaza cererile URL
/Models Contine clasele ce reprezinta datele modelului, gestionarea acestor obiecte.
/Views Contine fisiere template UI responsabile pentru afisarea rezultatului.
/Scripts Contine biblioteci JavaScript si scripturi (.js).
/Images Contine imaginile folosite in cadrul aplicatiei.
/Content Putem pune CSS sau alt continut dar nu scripturi si/sau imagini.
/Filters Contine codul pentru filtre.
/App_Data Contine fisierele de date Read/Write.
/App_Start Contine cod de configurare, grupari de fisiere si Web API.
Observatie
Aceasta structura este generata (nu in totalitate) de catre VS. Se poate folosi si o structura personalizata in sensul adaugarii de noi directoare.
ASP.NET MVC si Conventii
MVC foloseste o conventie bazata pe numele structurii de directoare cand rezolva template pentru View si acest lucru ne permite sa omitem calea cand referim un anumit View din clasa derivata din Controller. Implicit, ASP.NET MVC cauta in locatia
\Views\[ControllerName]\ vizualizarea pentru o anumita actiune.
Acest concept este numit “conventie peste configurare”.
Fiecare clasa controller are un nume ce termina cu Controller si clasa se gaseste in directorul /Controllers.
Exista un singur director /Views pentru toate vizualizarile din aplicatie.
Vizualizarile pe care le folosesc actiunile din controller se gasesc in directorul
\Views\[ControllerName]\.
Toate elementele UI reutilizabile sunt in directorul /Shared din folder-ul Views.
Procesul de executie al unei aplicatii MVC (C#)
Cererile catre o aplicatie Web bazata pe ASP.NET MVC trec printr-un obiect UrlRoutingModule, obiect ce este un modul HTTP. Modulul parseaza cererea si determina calea de urmat (route selection).
Un obiect “route” este o instanta a unei clase ce implementeaza RouteBase, si in mod obisnuit este o instanta a clasei Route. Daca obiectul UrlRoutingModule nu poate determina o cale pentru a continua, cererea este returnata catre procesul ASP.NET sau IIS. Din obiectul Route selectat, obiectul UrlRoutingModule obtine un obiect IRouteHandler, obiect ce este asociat cu obiectul Route. In mod obisnuit, intr-o aplicatie MVC, acesta va fi o instanta a clasei MvcRouteHandler. Instanta IRoutehandler creaza un obiect IHttpHandler si ii transmite obiectul IHttpContext. Implicit, instanta IHttpHandler pentru MVC este obiectul MvcHandler. Obiectul MvcHandler va selecta controller-ul ce va trata cererea.
Modulul si handler-ul sunt puncte de intrare in framework ASP.NET MVC. Sunt realizate urmatoarele actiuni:
Selectare controller.
Obtinere instanta pentru controller-ul specificat.
Apel metoda Execute() pe acea instanta.
Etapele executiei unui proiect MVC Web sunt:
Etapa Detalii
Primeste prima cerere In fisierul Global.asax, obiecte Route sunt adaugate la obiectul RouteTable.
Executa rutarea Modulul UrlRoutingModule foloseste primul obiect Route (ce se potriveste) din tabela RouteTable si creaza obiectul RouteData folosit pentru a crea obiectul RequestContext (HttpContext).
Creare handler cerere MVC
Obiectul MvcRouteHandler creaza o instanta a clasei MvcHandler.
Creare controller Obiectul MvcHandler foloseste instanta RequestContext pentru a identifica obiectul IControllerFactory (in general o instanta a clasei DefaultControllerFactory) si a crea instanta controller-ului.
Executa controller MvcHandler apeleaza din controller metoda Execute.
Invocare actiune Se determina ce actiune trebuie apelata (ce metoda din controller), se transfera parametrii (daca exista) la acea metoda si apoi se apeleaza acea metoda (actiune).
Execute result Dupa executarea cerererii se obtine rezultatul care poate fi:
ViewResult, RedirectToRouteResult, RedirectResult, ContentResult, JsonResult sau EmptyResult.
Modele, View-uri si Controller-e (C#) Un URL nu este egal cu o pagina.
In ASP.NET Web Forms este o corespondenta intre URL si pagina (pentru fiecare pagina se apeleaza fisierul *.aspx corespunzator).
In Asp MVC cererile din browser sunt mapate la actiuni din controller.
Rutarea
Se foloseste o tabela de rutare pentru a trata cererile ce apar. Rutarea din ASP.NET este folosita de ASP.NET MVC.
Metoda folosita pentru a inregistra o ruta este RegisterRoutes in care se adauga o intrare in RouteCollection folosind metoda MapRoute sau MapPageRoute. Codul se plaseaza in Global.asax in cadrul apelului metodei Application_Start.
O ruta este compusa din :
Nume
URL cu parametri : "{controller}/{action}/{id}"
Parametri dati sub forma unui obiect. Un exemplu este :
new { controller = "Home", action = "Index", id = "" }
Exemplu - Global.asax
...
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;
namespace MvcApplication1 {
public class MvcApplication : System.Web.HttpApplication {
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = "" }
// Parameter defaults );
}
protected void Application_Start() {
RegisterRoutes(RouteTable.Routes);
} } }
Ruta implicita din Global.asax include valori implicite pentru cei trei parametri:
"{controller}/{action}/{id}".
Din exemplul de mai sus avem:
controller = "Home", action = "Index", id = ""
Controllers
Controller-ul este responsabil cu tratarea input-ului de la utilizator (raspunde la interactiunile utilizatorului cu aplicatia). Controllerul contine logica desfasurarii aplicatiei. Un controller este o clasa al carei nume trebuie sa se termine (obligat) cu Controller si este derivat din clasa Controller.
HomeController.cs
...
using System.Web;
using System.Web.Mvc;
namespace MvcApplication1.Controllers {
[HandleError]
public class HomeController : Controller {
public ActionResult Index() {
ViewData["Title"] = "Home Page";
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return View();
}
public ActionResult About() {
ViewData["Title"] = "About Page";
return View();
} } }
Views
Actiunile expuse, in exemplul de mai sus, de catre controller sunt: Index() [ exact ca index.html ] si About().
View este echivalent cu o pagina.
View-urile trebuiesc create in locatia corecta. De exemplu, actiunii Index din controller Home ii corespunde vizualizarea Index.cshtml plasata in folderul \Views\Home\ si are numele Index.cshtml.
Regula generala:
Pentru fiecare actiune exista un view cu acelasi nume si extensia .cshtml, fisier plasat in directorul:
\Views\<nume_controller_fara_sufix_Controller>\
Pentru exemplul de mai sus avem:
\Views\Home\Index.cshtml
\Views\Home\About.cshtml
Sa urmarim un mic exemplu in care se evidentiaza cod in actiuni din controller si modelele folosite in vizualizari.
Optiunea About...
Cod partial Controller Home
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
namespace MVC3.Controllers {
public class HomeController : Controller {
public ActionResult Index() {
ViewBag.Message = "Welcome to ASP.NET MVC!";
ViewBag.AResult = MVC3.A.Ma();
ViewBag.TextIndex = "Un text plasat in metoda Index din controller Home";
return View();
}
public ActionResult About() {
// Asa pot aduce inregistrari din baza de date // de vazut directiva @model din About.cshtml
// Vom transmite aceasta colectie catre vizualizare // (About.cshtml)
List<string> str = new List<string>();
for (int i = 0; i < 10; i++) {
str.Add("Item " + i.ToString());
}
ViewBag.TextAbout = "Am transmis la View o colectie List<string>";
return View(str);
} } }
Vizualizarea pentru actiunea About (se gaseste in View -> Home->About.cshtml)
@model List<string>
@{
ViewBag.Title = "About Us";
}
<h2>@ViewBag.Message</h2>
<p>
Adaugam continut aici...
<br />
<h2>@ViewBag.TextAbout</h2>
<ul>
<li>@ViewBag.TextAbout</li>
@foreach (string s in Model) {
<li>@s</li>
} </ul>
</p>
Pagina de start este in _ViewStart.cshtml ce contine urmatoarea declaratie – pagina master folosita :
@{
Layout = "~/Views/Shared/_Layout.cshtml";
}
iar continutul paginii _Layout.cshtml este:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>@ViewBag.Title</title>
<link href="@Url.Content("~/Content/Site.css")" rel="stylesheet"
type="text/css" />
<script src="@Url.Content("~/Scripts/jquery-1.5.1.min.js")"
type="text/javascript"></script>
<script src="@Url.Content("~/Scripts/modernizr-1.7.min.js")"
type="text/javascript"></script>
</head>
<body>
<div class="page">
<!-- <header> -->
<div id="title">
<h1>My MVC Application</h1>
</div>
<div id="logindisplay">
@Html.Partial("_LogOnPartial")
</div>
<nav>
<ul id="menu">
<li>@Html.ActionLink("Home", "Index","Home")</li>
<li>@Html.ActionLink("Next","LogOn", "Account")</li>
<li>@Html.ActionLink("About", "About", "Home")</li>
<li>@Html.ActionLink("Default1 controller",
"Index", "Default1")</li>
<li>@Html.Label("expresie", "Text label")</li>
</ul>
</nav>
<!-- </header> -->
<section id="main">
@RenderBody()
</section>
<footer>
</footer>
</div>
</body>
</html>
RenderBody()
Metoda este referita ca pagina de Layout. Poate exista numai o metoda RenderBody per Layout pagina. Este asemanatoare cu controlul ContentPlaceHolder. Metoda indica unde va fi plasat template-ul vizualizarii in continutul elementului <body>.
RenderPage()
Paginile layout pot avea continut ce poate fi adus de pe alte pagini. RenderPage face exact acest lucru. Metoda are unul sau doi parametri. Primul parametru indica locatia fizica a fisierului, iar al doilea, ce este optional, contine un array de obiecte ce pot fi plasate pe pagina.
Exemplu
@RenderPage("~/Views/Shared/_AnotherPage.cshtml")
Observatie
ASP.NET nu afiseaza direct pagini ce incep cu _ (underscore) in denumire.
RenderSection()
Metoda are un parametru ce indica numele sectiunii si unul de tip bool ce semnifica daca sectiunea este optionala sau nu. Views-urile pot adauga sectiuni folosind urmatorul cod :
@section footer
{ <b> Pagina subsol aici </b> }
Creare Rute (C#)
O ruta informeaza ASP.NET Framework despre modul cum sa proceseze un URL ce nu corespunde la un fisier .aspx de pe hard disc.
Conventia pentru utilizarea rutarii este de a crea un folder App_Start si apoi crearea in acest folder a unei clase numita RouteConfig (fisier RouteConfig.cs), clasa ce va contine o metoda ce configureaza sistemul de rutare pentru aplicatia pe care o cream. Metoda din aceasta clasa o vom apela din clasa globala a aplicatiei (Global.asax). Continutul pentru App_Start/RouteConfig.cs este :
using System.Web.Routing;
namespace Routing // numele namespace-ului poate fi modificat {
public class RouteConfig
{
public static void RegisterRoutes(RouteCollection routes) {
} }
}
Observatie
Parametrul metodei RegisterRoutes este un obiect de tip RouteCollection, clasa definita in System.Web.Routing.
In aceasta metoda se definesc cai virtuale la aplicatia web prin adaugarea de « rute » la colectia RouteCollection.Cand se primeste o cerere de catre ASP.NET, se proceseaza rutele de catre un modul ce rescrie caile conform configurarii create. Codul pentru apelul configurarii rutelor este definit in Glabal.asax.cs in cadrul metodei
Application_Start astfel :
using System;
using System.Web.Routing;
namespace Routing
{ public class Global : System.Web.HttpApplication {
protected void Application_Start(object sender, EventArgs e) { // cod lipsa
RouteConfig.RegisterRoutes(RouteTable.Routes);
} }
}
Observatie
Framework-ul apeleaza metoda Application_Start pentru evenimentul Start al aplicatiei.
Rute fixe – metoda MapPageRoute
Ruta fixa nu inseamna altceva decat crearea unei cai virtuale fixe ce are ca efect afisarea unui formular Web al carui nume il stim in momentul crearii rutei.
Metoda folosita este MapPageRoute(...). Exemplu:
using System.Web.Routing;
namespace Routing {
public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes) { routes.MapPageRoute("default", "", "~/Default.aspx");
routes.MapPageRoute("cart1", "cart", "~/Store/Cart.aspx");
routes.MapPageRoute("cart2", "apps/shopping/finish", "~/Store/Cart.aspx");
} }
}
Descrierea metodei MapPageRoute in MSDN este data astfel :
Name Description
MapPageRoute(String, String, String) Provides a way to define routes for Web Forms applications.
MapPageRoute(String, String, String, Boolean)
Provides a way to define routes for Web Forms applications.
MapPageRoute(String, String, String, Boolean, RouteValueDictionary)
Provides a way to define routes for Web Forms applications.
MapPageRoute(String, String, String, Boolean, RouteValueDictionary, RouteValueDictionary)
Provides a way to define routes for Web Forms applications.
MapPageRoute(String, String, String, Boolean, RouteValueDictionary, RouteValueDictionary,
RouteValueDictionary)
Provides a way to define routes for Web Forms applications.
Observatie
Metoda este echivalenta cu apelul metodei Add pentru o colectie si pasand un obiect Route, obiect ce este creat de clasa PageRouteHandler.
Semnificatia parametrilor din aceasta metoda (ultimul prototip din lista de mai sus).
public Route MapPageRoute(
string routeName, string routeUrl, string physicalFile,
bool checkPhysicalUrlAccess, RouteValueDictionary defaults, RouteValueDictionary constraints, RouteValueDictionary dataTokens )
Parameters
routeName
Type: System.String
The name of the route.
routeUrl
Type: System.String
The URL pattern for the route.
physicalFile
Type: System.String
The physical URL for the route.
checkPhysicalUrlAccess Type: System.Boolean
A value that indicates whether ASP.NET should validate that the user has authority to access the physical URL (the route URL is always checked). This parameter sets the PageRouteHandler.CheckPhysicalUrlAccess property.
defaults
Type: System.Web.Routing.RouteValueDictionary Default values for the route parameters.
constraints
Type: System.Web.Routing.RouteValueDictionary
Constraints that a URL request must meet in order to be processed as this route.
dataTokens
Type: System.Web.Routing.RouteValueDictionary
Values that are associated with the route that are not used to determine whether a route matches a URL pattern.
Return Value
Type: System.Web.Routing.Route
The route that is added to the route collection.
Exemplu (MSDN)
routes.MapPageRoute("ExpenseDetailRoute",
"ExpenseReportDetail/{locale}/{year}/{*queryvalues}", "~/expenses.aspx",
false,
new RouteValueDictionary
{ { "locale", "US" }, { "year", DateTime.Now.Year.ToString() } }, new RouteValueDictionary
{ { "locale", "[a-z]{2}" }, { "year", @"\d{4}" } }, new RouteValueDictionary
{ { "account", "1234" }, { "subaccount", "5678" } } );
Observatie
1. Numele rutei trebuie sa fie unic la nivel de aplicatie si nu trebuie sa inceapa cu /.
2. URL fizic al rutei trebuie sa fie specificat relativ la radacina aplicatiei, adica folosirea notatiei ~.
3. Pattern-ul URL al rutei (al doilea argument) se mai numeste si cale virtuala.
Cateva explicatii pentru rutele create cu codul
using System.Web.Routing;
namespace Routing {
public class RouteConfig {
public static void RegisterRoutes(RouteCollection routes) { routes.MapPageRoute("default", "", "~/Default.aspx");
routes.MapPageRoute("cart1", "cart", "~/Store/Cart.aspx");
routes.MapPageRoute("cart2", "apps/shopping/finish", "~/Store/Cart.aspx");
} }
}
Rute indicate corect
Cale virtuala Pagina Web form
/ (the root URL) /Default.aspx
/cart /Store/Cart.aspx
/apps/shopping/finish /Store/Cart.aspx Rute indicate corect / incorect pentru ruta=”cart2”
Cale virtuala Se potriveste cu pattern-ul indicat?
/apps/shopping/finish Da. Cele trei segmente sunt identice.
/apps/shopping Nu. Prea putine segmente.
/apps/shopping/finish/cart Nu. Prea multe segmente.
/app/shopping/checkout Nu. Numar segmente corect, dar nu se potriveste cu pattern-ul de la adaugarea rutei.
Obtinere informatii despre rute
Se pot obtine informatii despre cum sistemul de rutare a procesat o cerere si a selectat pagina care se potriveste pattern-ului folosind proprietatea RouteData din clasa Page, proprietate ce returneaza un obiect System.Web.Routing.RouteData de unde obtinem informatiile necesare.
Proprietati importante din clasa RouteData
DataTokens Returneaza o colectie cheie/valoare asociata cu ruta ce a fost aplicata cererii curente.
Route Returneaza un obiect RouteBase ce furizeaza informatii despre ruta ce a fost aplicata cererii curente.
RouteHandler Returneaza IRouteHandler ce a mapat cererea cererea la un IHttpHandler.
Values Retruneaza o multime de valori ale parametrilor pentru ruta.
Ruland aplicatia cu diverse URL-uri obtinem urmatoarele:
URL: .../cart.aspx
URL: .../store/cart.apsx
Observatie
Proprietatea Url returneaza calea folosita pentru a defini ruta, nu URL-ul cererii curente.
RouteData returneaza un obiect Route. Clasa Route are mai multe proprietati dintre care amintim (MSDN) :
Constraints Gets or sets the constraints used to limit the range of URLs that the route will match.
DataTokens Gets or sets the data tokens associated with the route.
Defaults Gets or sets the default values for variable segments.
RouteExistingFiles Gets or sets whether the routing system should handle requests that match existing files.
RouteHandler Gets or sets the IRouteHandler implementation associated with the route.
Url Gets or sets the path used by the route.
Adaugare de segmente variabile
Segementul variabil permite ca o singura ruta sa se potriveasca cu URL-uri multiple.
Segementul variabil este notat intre { } si contine numele unei variabile.
Exemplu
routes.MapPageRoute("dall", "{app}/default", "~/Default.aspx");
routes.MapPageRoute("d4", "store/default", "~/Store/Cart.aspx");
cand sistemul de rutare intalneste un segment variabil va mapa orice valoare pentru acest segment si ca atare cererile /Iasi/default, /Copou/default vor fi toate mapate catre
Default.aspx.
Observatie
In exemplul de mai sus am creat o ruta care are doua segmente din care ultimul segment este default. Din cauza ca sistemul de rutare trateaza prima ruta pe care o intalneste in colectie, va rezulta ca ruta /store/default va fi tratata de catre Default.aspx si nu de /Store/cart.aspx.
Rezolvarea problemei consta din a plasa rutele fara segemente variabile in fata rutelor cu segmente variabile. In cazul nostru ordinea corecta ar fi:
routes.MapPageRoute("d4", "store/default", "~/Store/Cart.aspx");
routes.MapPageRoute("dall", "{app}/default", "~/Default.aspx");
Modelul de asociere la valorile segmentului din ruta
Acest model permite sa integram valorile din segmentul de rutare in cod.
Explicam acest lucru pe baza urmatorului exemplu.
In metoda RegisterRoutes adaugam urmatorul cod (o noua ruta):
routes.MapPageRoute("loop", "{count}", "~/Loop.aspx", false, new RouteValueDictionary { { "count", "3" } },
new RouteValueDictionary { { "count", "[0-9]*" } });
Segmentul variabil este {count}. Dorim ca valorile pe care le primeste count sa fie afisate in pagina Web numita Loop.aspx.
Loop.aspx este definita astfel:
<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Loop.aspx.cs"
Inherits="Routing.Loop" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
<title></title>
</head>
<body>
<p>This is the Loop.aspx Web Form</p>
<ul>
<asp:Repeater ID="Repeater1" ItemType="System.Int32"
SelectMethod="GetValues" runat="server">
<ItemTemplate>
<li><%# Item %></li>
</ItemTemplate>
</asp:Repeater>
</ul>
</body>
</html>
In controlul asp:Repeater vom afisa valorile furnizate de metoda GetValues indicata de atributul SelectMethod.
In fisierul Loop.aspx.cs avem urmatorul cod:
using System.Collections.Generic;
using System.Web.ModelBinding; // de adaugat aceasta referinta namespace Routing
{
public partial class Loop : System.Web.UI.Page {
public IEnumerable<int> GetValues([RouteData("count")] int? count) {
for (int i = 0; i < (count ?? 3); i++) {
yield return i;
} } } }
Parametrul count al metodei GetValues, definit ca Nullable, este adnotat cu atributul RouteData. In cadrul acestui atribut specificam numele segmentului pentru care dorim sa-i preluam valoarea, in cazul nostru count. Atributul RouteData este definit in System.Web.ModelBinding, si are rolul de a furniza valoarea argumentului luata din segmentul variabil numit count, din ruta.
Observatie
In bucla for avem i < (count ?? 3), ceea ce inseamna ca daca count nu are valoare, adica count.HasValue este false, se va utiliza valoarea implicita 3.
Rulam aplicatia si vom avea implicit URL: localhost:port. Modificam acest URL in localhost:port/5 si rezultatul va fi:
Exemplu : Ne propunem sa cream o ruta numita Blog si care sa trateze cereri de forma /Archive/entrydate, unde “entrydate” este de tip Data calendaristica.
Ruta se creaza in fisierul Global.asax.
Modificarile in Global.asax sunt urmatoarele:
Global.asax (cu custom route)
using System.Web.Mvc;
using System.Web.Routing;
namespace MvcApplication1 {
public class MvcApplication : System.Web.HttpApplication {
public static void RegisterRoutes(RouteCollection routes) {
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute( "Blog", // Route name
"Archive/{entryDate}", // URL with parameters new { controller = "Archive", action = "Entry" } // Parameter defaults
);
routes.MapRoute( "Default", // Route name
"{controller}/{action}/{id}", // URL with parameters new { controller = "Home", action = "Index", id = ""}
// Parameter defaults );
}
protected void Application_Start() {
RegisterRoutes(RouteTable.Routes);
} }
}
Ordinea rutelor pe care o adaugam la tabela de rutare este importanta.
Ruta Blog creata mai sus va fi folosita de orice cerere ce starteaza cu /Archive/. In concluzie aceasta se potriveste pentru orice cerere de forma :
/Archive/01-01-2011 /Archive/31-12-2010 /Archive/Iasi
Controller-ul va fi Archive si va fi invocata metoda Entry(’01-01-2011’), Entry(Iasi), etc., metoda al carei prototip este :
public string Entry(DateTime entryDate);
Observatie:
In exemplul de mai sus parametrul metodei Entry se presupune ca este de tip DateTime.
Conversia parametrului catre tipul DateTime este facuta de ASP. NET MVC.
Codul din controller poate fi urmatorul:
ArchiveController.cs
using System;
using System.Web.Mvc;
namespace MvcApplication1.Controllers {
public class ArchiveController : Controller {
public string Entry(DateTime entryDate) {
return "You requested the entry from " + entryDate.ToString();
} }
}
Observatie
Daca parametrul din metoda Entry nu poate fi convertit la DateTime, se genereaza o pagina ce contine o descriere a erorii.
Alte exemple de rute (MSDN) – codurile de mai jos se adauga in RegisterRoutes.
Se adauga o ruta fara nume ce are pattern URL care contine valoarea literala « SalesReportSummary » si un parametru URL (placeholder) numit year. Ruta mapeaza catre un fisier numit Sales.aspx.
routes.MapPageRoute("",
"SalesReportSummary/{year}", "~/sales.aspx");
2. Acest cod adauga o ruta numita SalesRoute. Aceasta ruta trebuie sa aiba un nume deoarece are aceeasi parametri ca ruta anterioara si altfel nu se poate face diferenta.
routes.MapPageRoute("SalesRoute", "SalesReport/{locale}/{year}", "~/sales.aspx");
3. Se adauga o ruta cu numele ExpensesRoute si include un parametru ce poate contine orice valoare, parametru numit extrainfo. Pe langa acest parametru mai sunt definiti alti doi parametri, locale cu valoarea implicita
"US" si year cu valoarea anul curent. Pattern-ul pentru acesti doi parametri sunt dati in (expresii regulate) :
new RouteValueDictionary { { "locale", "[a-z]{2}" }, { "year", @"\d{4}" } }
iar ruta se inregistreaza astfel:
routes.MapPageRoute("ExpensesRoute",
"ExpenseReport/{locale}/{year}/{*extrainfo}", "~/expenses.aspx", true,
new RouteValueDictionary { { "locale", "US" },
{ "year", DateTime.Now.Year.ToString() } }, new RouteValueDictionary {
{ "locale", "[a-z]{2}" }, { "year", @"\d{4}" } }
);
Creare hyperlink-uri folosind rute
Modalitati de creare
1. hard code
2. specificare nume parametri din route si valorile corespunzatoare, iar ASP.NET va genera URL ce corespunde cu acestia. Se poate specifica si numele rutei pentru a identifica in mod unic o ruta.
Metoda 1. (hard code)
<asp:HyperLink ID="HyperLink1" runat="server"
NavigateUrl="~/salesreportsummary/2010">
Sales Report - All locales, 2010 </asp:HyperLink>
<br />
<asp:HyperLink ID="HyperLink2" runat="server"
NavigateUrl="~/salesreport/WA/2011">
Sales Report - WA, 2011 </asp:HyperLink>
<br />
<asp:HyperLink ID="HyperLink3" runat="server"
NavigateUrl="~/expensereport">
Expense Report - Default Locale and Year (US, current year) </asp:HyperLink>
<br />
Metoda 2 In .aspx
<asp:HyperLink ID="HyperLink6" runat="server">
Expense Report - CA, 2008 </asp:HyperLink>
<br />
iar in cod (metoda Page_Load) trebuie sa scriem ceva de genul:
RouteValueDictionary parameters = new RouteValueDictionary {
{"locale", "CA" }, { "year", "2008" } ,
{ "category", "recreation" } };
Acest cod creaza o instanta a clasei RouteValueDictionary ce contine trei parametri.
Al treilea parametru este category si valoarea lui va fi redata ca un parametru “query string”.
VirtualPathData vpd =
RouteTable.Routes.GetVirtualPath(null, "ExpensesRoute", parameters);
HyperLink6.NavigateUrl = vpd.VirtualPath;
Accesare parametri din URL in .aspx
<h1>
Expense Report for
<asp:Literal ID="Literal1"
Text="<%$RouteValue:locale%>"
runat="server"></asp:Literal>, <asp:Literal ID="Literal2"
Text="<%$RouteValue:year%>"
runat="server"></asp:Literal>
</h1>
Accesare parametri din URL in cod
Valorile pentru Literal vor fi setate in cod.
<h1>
Sales Report for
<asp:Literal ID="LocaleLiteral" runat="server"></asp:Literal>, <asp:Literal ID="YearLiteral" runat="server"></asp:Literal>
</h1>
In Page_Load()
LocaleLiteral.Text = Page.RouteData.Values["locale"] == null ? "All locales" : Page.RouteData.Values["locale"].ToString();
YearLiteral.Text = Page.RouteData.Values["year"].ToString();
Exemple de creare rute Rute cu valori fixe
routes.MapRoute( "MyRoute", "{controller}/{action}");
Aceasta ruta are doua segmente: controller si action plasate intre { si }. “MyRoute” este numele rutei. Rutele care se potrivesc cu aceasta declaratie sunt:
http://www.partyplanner.com/party/index http://www.partyplanner.com/comment/post http://www.mysite.com/home/index
Rute care nu se potrivesc cu aceasta declaratie:
http://www.partyplanner.com/party http://www.partyplanner.com/comment http://www.mysite.com/home/
http://www.mysite.com
Rute cu valori implicite
routes.MapRoute(
"MyRoute",
"{controller}/{action}",
new{ controller = "party", action = "index"} );
Rute care se potrivesc cu aceasta declaratie
http://www.partyplanner.com/party/index http://www.partyplanner.com/comment/post http://www.mysite.com/home/index
http://www.mysite.com
Potrivire cu toate segmentele – {*param}
routes.MapRoute(
"CustomRoute", "product/{*param}",
new{ controller = "Product", action = "Index"} );
Rute care se potrivesc cu aceasta declaratie http://mysite.com/product/hello http://mysite.com/product/hello/a/b/c
Potrivire cu toate rutele – {*url}
routes.MapRoute(
"CatchAllRoute", {*url}",
new{ controller = "Home", action = "Index", url = UrlParameter.Optional }
);
Rute care se potrivesc cu aceasta declaratie http://mysite.com
http://mysite.com/product/hello
http://mysite.com/home/index/hello/text/1
Rute cu separatori diferiti - ~
routes.MapRoute(
"MyRoute",
"{controller}~{action}~{id}"
);
In loc de / separam segmentele cu ~.
http://mysite.com/product~list~1
Ignorare rute
Se instiinteaza infrastructura de rutare sa nu trateze anumite cereri prin ignorarea rutelor.
Implicit infrastructura de rutare nu trateaza cereri pentru resurse statice si acest lucru poate fi controlat de proprietatea RouteExistingFiles. Definitia pentru ignorarea rutelor este data astfel:
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
Declaratia de mai sus spune ca se vor ignora cererile ce contin extensia “.axd”. Un fisier cu extensia .axd este un fisier HTTP Handler. Metoda IgnoreRoute este suprascrisa si putem pasa constrangeri in al doilea parametru al metodei.
routes.IgnoreRoute("{resource}.axd/{*pathInfo}", new{ resource = newCustomConstraint() });
Proprietatea RouteExistingFiles are implicit valoarea false. Se seteaza pe true aceasta proprietate dar dupa metoda IgnoreRoute.
MVC 5 – Definire rute folosind atribute
Acest mecanism introdus in MVC 5 permite de a defini ruta in acelasi loc unde am definit si actiunea. Atributul folosit se numeste Route si poate fi atasat direct la metoda
(actiune) din controller.
[Route("Products/Electronics/{id}")]
public ActionResult GetElectronicItems(string id) {
ViewBag.Id = id;
return View();
}
In fisierul RouteConfig trebuie sa adaugam urmatorul cod:
public static void RegisterRoutes(RouteCollection routes) {
routes.MapMvcAttributeRoutes();
}
Parametrul optional se defineste in pattern-ul URL folosind notatia ?.
[Route("Products/Electronics/{id?}")]
public ActionResult GetElectronicItems(int? id) {
ViewBag.Id = id; return View();
}
id este un tip Nullable, si existenta valorii se testeaza folosind proprietatea HasValue.
Constrangeri : se specifica tipul exact al parametrului.
[Route("Products/Electronics/{id:int}")]
Alte constrangeri folosite in atributul Route Route
Constraint
Used To
x:bool Match a bool parameter
x:maxlength(n) Match a string parameter with maximum length of n characters
x:minlength(n) Match a string parameter with minimum length of n characters
x:max Match an integer parameter with a maximum value of n.
x:min Match an integer parameter with a minimum value of n.
x:range Match an integer parameter within a range of values.