asp.net mvc 3 y 4 - servicio de informática. … · obtener todos los registros de una tabla...

25
ÚLTIMA ACTUALIZACIÓN: 13 DE OCTUBRE DE 2012 SERVICIO DE INFORMÁTICA | UNIVERSIDAD DE ALICANTE ASP.NET MVC 3 y 4 PERSONALIZACIÓN Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es/

Upload: trandieu

Post on 26-Sep-2018

227 views

Category:

Documents


1 download

TRANSCRIPT

Page 1: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ÚLTIMA ACTUALIZACIÓN: 13 DE OCTUBRE DE 2012

SERVICIO DE INFORMÁTICA | UNIVERSIDAD DE ALICANTE

ASP.NET MVC 3 y 4 PERSONALIZACIÓN

Andrés Vallés Botella | Analista | Desarrollos propios

Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España

http://si.ua.es/es/

Page 2: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

2

3º DÍA – PERSONALIZACIÓN

ENTITY FRAMEWORK BÁSICO

Vamos a estudiar lo básico para hacer consultas con Entity Framework.

Nos creamos en la aplicación de libros con Oracle, en el controlador Home, el método BD.

Los pasos para hacer la consulta son los siguientes:

1. Preparar una variable con el listado de datos que vamos a pasar al modelo. Si queremos un

listado de libros, usaremos IEnumerable<Tabla> para que desde la vista podamos recorrer los

datos con las plantillas básicas que nos ofrece MVC.

2. Abrir la conexión a la Entidad. Dependerá del nombre que se le haya puesto a la Entidad cuando

hayamos importado el modelo. Lo más cómodo es usar using([declaración conexión]) {} porque

se elimina automáticamente al salir del using.

3. Hacer la consulta y almacenar los resultados en la variable del punto 1. Lo más sencillo es

obtener todos los registros de una tabla [conexión].[nombretabla].ToList(). Las tablas pasan a

ser unos objetos más con los que podemos trabajar.

4. Cerrar o liberar la conexión con la entidad. Si hemos usado using no será necesario, pero en

cualquier otro caso habrá que usar bd.Dispose()

5. Llamar a la vista con el modelo de datos que hemos almacenado en el punto 1 y 3. Igual que

hemos hecho en otras ocasiones View([Variable]).

Podría ser algo así.

public ActionResult BD() { IEnumerable<CSI_LIBRO> libros; using (var bd = new EntitiesBiblioteca()) { libros = bd.CSI_LIBRO.ToList(); } return View(libros); }

La nomenclatura usada para esta consulta es lambda, que para es la más sencilla, pero la tradicional

de consultas (Linq) sería:

libros = (from l in bd.CSI_LIBRO select l).ToList();

Como son muy sencilla ambas, que cada uno seleccione la que le sea más sencilla.

Ahora creamos una vista para este método y le indicamos el modelo CSI_LIBRO y la plantilla List.

Page 3: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

3

Si lo ejecutamos vemos el listado de libros. También incluye la creación de nuevos libros, edición,

borrado. Toda esta parte la podemos eliminar.

FILTRADO

Cuando hacemos consulta lo normal es mostrar aquellos que cumplen una condición.

La más básica podría ser la consulta por id

libros = bd.CSI_LIBRO.Where(l => l.ID == 1 ).ToList(); libros = (from l in bd.CSI_LIBRO where l.ID == 1 select l).ToList();

O que esté en un intervalo

libros = bd.CSI_LIBRO.Where(l => l.ID >= 1 && l.ID < 3).ToList(); libros = (from l in bd.CSI_LIBRO where l.ID >= 1 && l.ID <= 3 select l).ToList();

Si queremos consultar aquello libros que tienen en el título “El Principito”

libros = bd.CSI_LIBRO.Where(l => l.TITULO == "El Principito").ToList(); libros = (from l in bd.CSI_LIBRO where l.TITULO == "El Principito" select l).ToList();

O que contienen la palabra “Principito”

libros = bd.CSI_LIBRO.Where(l => l.TITULO.Contains("Principito") ).ToList(); libros = (from l in bd.CSI_LIBRO where l.TITULO.Contains("Principito") select l).ToList();

Page 4: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

4

Podemos hacer conversiones de mayúsculas o minúsculas de cualquier campo en la propia consulta

libros = bd.CSI_LIBRO.Where(l => l.TITULO.ToUpper().Contains("PRINCIPITO") ).ToList(); libros = (from l in bd.CSI_LIBRO where l.TITULO.ToUpper().Contains("PRINCIPITO") select l).ToList();

ORDENACIÓN

Disponemos de dos funciones en lambda OrderBy y OrderByDescending al que debemos indicar el

campo de ordenación

libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).ToList(); libros = (from l in bd.CSI_LIBRO orderby l.TITULO select l).ToList(); libros = bd.CSI_LIBRO.OrderByDescending(l => l.TITULO).ToList(); libros = (from l in bd.CSI_LIBRO orderby l.TITULO descending select l).ToList();

Si queremos ordenar por más de un campo, en lambda debemos usar ThenBy o ThenByDescending,

mientras que en las consultas tradicionales lo hacemos separando los campos por comas.

libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).ThenBy(l => l.ISBN).ToList(); libros = (from l in bd.CSI_LIBRO orderby l.TITULO, l.ISBN select l).ToList();

PAGINACIÓN

Uno de los problemas que nos encontramos cuando trabajamos con Oracle es la paginación. Obtener

un número limitado de registros y saltar hasta un determinado registro es trivial en EF.

Para obtener los 2 primeros registos de una consulta

libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).Take(2).ToList(); libros = (from l in bd.CSI_LIBRO orderby l.TITULO, l.ISBN select l).Take(2).ToList();

Si queremos saltar los primeros 10 registros y obtener los 2 primeros registros

libros = bd.CSI_LIBRO.OrderBy(l => l.TITULO).Skip(10).Take(2).ToList(); libros = (from l in bd.CSI_LIBRO orderby l.TITULO, l.ISBN select l).Skip(10).Take(2).ToList();

Page 5: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

5

HELPERS (BÁSICO)

HTMLHELPERS

Ayer vimos las funciones relacionadas con las direcciones. Hoy nos centramos en las que realmente

nos van a ayudar en el trabajo diario, las que nos generan código HTML.

Aunque el listado es mucho más amplio que el de URLHelpers, lo cierto es que todos son muy

parecidos.

Vamos a comenzar con el elemento básico que es el formulario que recoge todos los campos con los

que trabajamos.

Nos creamos una método / acción en nuestro controlador para ir jugando. Le podemos llamar

helpers. No le vamos a pasar ningún modelo por el momento.

Editamos su vista y lo básico para un formulario en HTML es <form …></form>.

La idea es que no lo escribamos directamente sino que usemos los helpers.

BeginForm(s:action, s:controller, o:values)

Si escribimos

@using (Html.BeginForm()) { }

Nos generaría el siguiente código

<form action="/Home/helpers" method="post"></form>

Por defecto nos genera una acción a nosotros mismos y hace la llamada con método post

Con los parámetros podemos indicar que controlador / acción del action y el método

Html.BeginForm("Create", "Libro", FormMethod.Get)

Y generaría

<form action="/Libro/Create" method="get"></form>

Todos los HTMLHelpers incluyen un último parámetro abierto a añadir cualquier atributo HTML, por

ejemplo id, class, etc. En el caso de que queramos indicar el id

Html.BeginForm("Create", "Libro", FormMethod.Get, new {id="fTest"}))

Si además quisiéramos añadir el estilo con class al ser una palabra reservadas del sistema debemos

escribir la propiedad con @class.

Html.BeginForm("Create", "Libro", FormMethod.Get, new {id="fTest", @class="formulario-ua"})

El resultado final sería

<form action="/Libro/Create" class="formulario-ua" id="fTest" method="get"></form>

Page 6: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

6

En caso de que no usemos using en la declaración de beginform deberemos usar endform para

indicar donde acaba.

@{ Html.BeginForm("Create", "Libro", FormMethod.Get, new {id = "fTest", @class = "formulario-ua"}); } … @{ Html.EndForm();}

Yo usaré en todos los ejemplos using porque queda el código más agrupado.

Ahora es el momento de incluir elementos en el formulario

Label(s:name, o:text)

Un etiqueta con texto que hace referencia a un campo (name). En caso de que no se indique el texto

pondrá por defecto el nombre del campo que hayamos indicado en el primer parámetro.

@using(Html.BeginForm("Create", "Libro", FormMethod.Get, new {id = "fTest", @class = "formulario-ua"})) { @Html.Label("Nombre", "Nombre:") … }

En este ejemplo mostrará una etiqueta “Nombre:” que hace referencia a un campo “Nombre”.

<label for="Nombre">Nombre:</label>

TextBox(s:name, o:value)

Crea una caja de texto con el nombre que le indiquemos y con un valor por defecto en el segundo

parámetro.

@Html.TextBox("Nombre", "Alberto")

genera

<input id="Nombre" name="Nombre" type="text" value="Alberto" />

CheckBox(s:name, b:checked)

Genera una elemento checkbox.

DropDownList(s:name, list:selectlistitems)

Genera una lista desplegable en la que podemos seleccionar un único elemento.

@Html.Label("Sexo") @Html.DropDownList("Sexo", new MultiSelectList(new[] {"Hombre", "Mujer"}))

Genera

Todo el contenido que se asigne en el valor por defecto (en cualquier HTMLHelper) se codifica

automáticamente para evitar que se produzcan ataques XSS injection.

Page 7: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

7

Hidden(s:name, o:value)

Genera un campo oculto.

ListBox(s:name, list:selectlistitems)

Genera una lista de valores en la que podemos seleccionar más de un elemento.

@using(Html.BeginForm("Create", "Libro", FormMethod.Get, new {id = "fTest", @class = "formulario-ua"})) { @Html.Label("Unidad") @Html.ListBox("Unidad", new MultiSelectList(new[] {"Servicio de Informática", "Selección y Formación", "Servicio de Personal"})) }

Genera

RadioButton(s:name, o:value, b:checked)

Genera una elemento radiobutton

TextArea(s:name, s:value)

Crea una caja de texto de tipo textarea.

INCLUIR BUSCADOR EN NUESTRA APLICACIÓN

Con lo visto anteriormente preparar una caja de búsqueda totalmente funcional que busque en el

listado de libros. Se podrá buscar por título o por Isbn.

En caso de que se deje en blanco el término de búsqueda mostrará todos los libros.

Crearemos los modelos que necesitemos, un nuevo método para el controlador Home y una vista

con la caja de búsqueda.

Luego crearemos una 2º versión que incluiremos el buscador como una sección dentro de la plantilla

_layout.cshtml. De esta manera podremos indicar en las vistas si queremos incluirlo o no.

Page 8: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

8

VISTAS POR DEFECTO

Como ya se comentó el primer día las plantillas que usa el Visual Studio son personalizables. Se

almacena por defecto en Visual Studio 2010 en la carpeta

C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\ItemTemplates\

CSharp\Web\MVC 3\CodeTemplates\AddView\CSHTML

Si accedemos veremos 6 ficheros con la extensión tt. Lo primero que podemos pensar es en

modificarlo directamente, pero esto afecta a todos los proyectos. Además está en una carpeta que es

muchas ocasiones, y por medidas de seguridad, nos pide permisos de administrador.

Lo mejor es coger la carpeta CodeTemplates y copiarla a la raíz de nuestra aplicación (podemos

borrar todo lo que no sea AddView\CSHTML).

El siguiente paso para poder hacer modificaciones a la plantilla es cambiar el parámetro Herramienta

personalizada a las 6 plantillas. Por defecto tiene el valor TextTemplatingFileGenerator. Lo dejamos

vacío.

Page 9: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

9

¿Podemos crear nuevas plantillas y que aparezcan al crear una vista?

Por supuesto. Vamos a duplicar la plantilla para a vista de listado (list.tt) para que no incluya ni dar

de alta, ni poder gestionar los registros.

Aunque el código no es tan legible como en Razor, buscamos los bloques de HTML y vamos

eliminando los contenidos que nos interesen. El objetivo es que se vea un listado con este formato.

Un problema que nos podemos encontrar es que en los listados personalizados el tipo de libro

aparezca como código y no como descripción. Debemos realizar unos cambios.

En el controlador, hacer un include la tabla relacionada.

public ActionResult Buscar(string palabra) { IEnumerable<CSI_LIBRO> libros; using (var bd = new EntitiesBiblioteca()) { libros = bd.CSI_LIBRO.Include("CSI_TIPOLIBRO"); if (!String.IsNullOrEmpty(palabra)) { libros = libros.Where(l => l.TITULO.ToUpper().Contains(palabra.ToUpper())); } libros = libros.ToList(); } return View(libros); }

En la vista cambiar el campo que es clave ajena por la [tabla relación].[campo descripción] (sólo en el

caso que hayamos realizado el include porque si no es inaccesible).

<td> @Html.DisplayFor(modelItem => item.CSI_TIPOLIBRO.DESCRIPCION) </td>

HELPERS

HTMLHELPERS (CONTINUACIÓN)

ValidationSummary([Exclude property-level error])

Muestra una lista no ordenada de todos los errores que se producen al validar el formulario.

Se puede validar todo o excluir los errores a nivel de las propiedades del modelo

Los errores se pueden lanzar en tiempo de ejecución con la propiedad AddModelError(campo,

mensaje de error) del objeto ModelState.

En caso de que el campo lo dejemos vacío estamos lanzando un error a nivel de modelo

ModelState.AddModelError("", "Prueba de un error general");

y si indicamos el campo lo hacemos a nivel de propiedad.

ModelState.AddModelError("Nombre", "El nombre no cumple con los requisitos");

Page 10: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

10

Añadimos a la vista un sumario de validación

@using(Html.BeginForm()) { @Html.ValidationSummary(false) }

El resultado

Por defecto asigna el estilo "validation-summary-errors”.

ValidationMessage (s:[Nombre del campo], o:[Mensaje de error])

En caso de que no queramos que sea un sumario el que recoja todos los mensajes, si no que cada

mensaje aparezca en el punto que indiquemos (normalmente a la derecha del campo), usaremos

este Helper.

Si no se especifica el mensaje de error, todos aquellos errores que se produzcan (o provoquemos) se

visualizarán en este punto.

@Html.ValidationMessage("Nombre")

Se visualizará de la siguiente manera

Por defecto asigna el estilo “field-validation-error”.

Action(s: [nombre acción])

Nos permite llamar a un método / acción de un controlador. Puede parecerse mucho a las vistas

parciales que vimos ayer y que recordaremos luego, pero lo cierto es que mientras las vistas parciales

están pensadas para escribir bloques de código, action está orientada a ejecutar el proceso completo

de la acción de un controlador (que incluye la generación del código con la Vista).

El resultado es una cadena de texto con todo el contenido generado.

@Html.Action("Cabecera")

Page 11: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

11

ActionLink(s: [descripción], s: [acción], s: [controlador])

Permite generar enlaces a acciones determinadas de un controlador. Por ejemplo si queremos poner

un enlace a la acción Index del Home usaríamos

@Html.ActionLink("Página principal", "Index", "Home")

Se usa en las plantillas para realizar cualquier acción con el modelo del controlador, alta, baja,

edición o borrado.

Dispone de muchas sobrecargas este Helper, permitiendo desde indicar protocolor, servidor y ancla,

hasta definir los atributos HTML.

RouteLink(s: [descripción], d: [valores ruta])

Es parecido alterior, porque genera un enlace a una ruta o una acción de un controlador. Es algo más

artesanal ya que no dispone de tantas sobrecargas y todos los valores se meten en un campo o se

llama a la routa por su nombre (en caso que se haya definido previamente). Si queremos enlazar con

la acción “Acerca de” usaríamos

@Html.RouteLink("Acerca de", new { controller = "Home", action="About"})

RenderAction(s: [nombre acción])

Es idéntica a Action con la diferencia de que no almacena el contenido en una cadena de texto si no

que la escribe directamente al objeto Response, con lo que se visualiza por el navegador.

HELPERS CON EL MODELO

Lo normal no será generar todo el código HTML sino que se genere a partir de un modelo, por lo

tanto MVC ofrece tdo lo que hemos visto anteriormente para trabajar con modelos.

Analizamos un par, pero será lo mismo para el resto de compontes.

Partimos de que la vista incluye referencia al modelo por ejemplo al Libro que contiene una

propiedad título

@model Proyecto.Libro

Si deseamos que una acción sea sólo llamada desde Action o RenderAction pero no directamente

como una dirección más en el navegador podemos usar la anotación ChildActionOnly antes de la

declaración

[ChildActionOnly]

public ActionResult Cabecera() {

}

Page 12: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

12

Todos los Helpers se llaman como antes pero postponiendo For al nombre, es decir TextBox pasa a

ser TextBoxFor o Label pasa a ser LabelFor. Luego para hacer referencia a una propiedad del modelo

se usa un alias por ejemplo m => m.propiedad o l => l.propiedad (lo que os sea más cómodo)

Si necesitamos mostrar la etiqueta, la caja de texto y la validación

@Html.LabelFor(l => l.Titulo) @Html.TextBoxFor(l => l.Titulo) @Html.ValidationMessageFor(l => l.Titulo)

Si lo ejecutamos lo primero que vemos es que el LabelFor de un modelo no es muy útil porque es el

nombre del campo. Muchas veces siglas de un campo de la base de datos, o todo en mayúsculas.

MVC incluye data annotations en los campos lo que permite personalizar la información que luego se

verá con estos Helpers. Por el momento sólo vamos a ver el de la descripción, pero mañana nos

centraremos en todo el tema de validación, que es donde realmente se saca el potencial.

Las anotaciones se ponen con corchetes antes de la definición de la propiedad.

[Display(Name = "Título del libro")] public string Titulo { get; set; }

Display permite personalizar aspectos de la visualización. Con Name le indicamos el nombre o label

que tendrá esta propiedad. El resultado es:

PERSONALIZADOS

Como es lógico MVC nos permite crear nuestros propios Helpers para darle mayor potencia a éstos.

Se comportan como una función a la que se pasan parámetros si los necesita, y dentro genera el

código que queremos mostrar.

El formato es @Helper [Nombre Función]( [parámetros] )

@helper BreadCumb(string[] elementos) { <div> for(int i=0; i < elementos.Count(); i++) { <span>@elementos[i]</span> if (i < elementos.Count() - 1) { <text>></text> } } </div> }

Luego para llamarla @NombreFunción(parámetros)

@BreadCumb(new[] {"Inicio", "Administración", "Secciones"})

Page 13: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

13

El resultado es el siguiente

Es costumbre a la hora de poner los parámetros, anteponer el nombre de cada parámetro y luego :

(dos puntos).

@BreadCumb(elementos: new[] {"Inicio", "Administración", "Secciones"})

Si deseamos que es helper sea reutilizables desde cualquier vista, añadimos la carpeta App_Code a

nuestro proyecto (no aparece como opción en las carpetas de ASP.NET) y creamos el fichero

BreadCumbHelpers.cshtml. Copiamos el código del Helper y lo guardamos.

Ahora para hacerle referencia desde la vista llamaremos al helper de la siguiente manera

@[Nombre fichero (sin extensión)].[Nombre del helper o función]( [parámetros])

@BreadcumbHelpers.BreadCumb(elementos: new[] { "Inicio", "Administración", "Secciones" })

Aunque puede parecer que crear un Helper es lo mismo que llamar a vistas parciales (que vimos

ayer) porque ambas se usan para reaprovechar código o dejarlo más estructurado, si que es cierto

que cada uno tiene su uso.

Helpers personalizado está pensado para pequeños trozos de código, que generan una programación

sencilla y que se comparte con diferentes vistas de tu proyecto o incluso entre varios.

Partial views están orientadas a secciones de código, con el objetivo de hacer más clara la estructura.

Puede contener una programación tan complicada como la vista que les llama.

Disponemos de dos HTMLHelpers dedicados a trabajar con Partial views (ayer usamos el comando

RenderPage).

Partial(s: nombrevista)

Genera una cadena de texto con la ejecución de la vista parcial

@Html.Partial("_Cabecera")

RenderPartial(s: nombrevista)

Es idéntica a Partial con la diferencia de que no almacena el contenido en una cadena de texto si no

que la escribe directamente al objeto Response, con lo que se visualiza por el navegador.

@{ Html.RenderPartial("_Cabecera"); }

PERSONALIZADOS CON HTMLHELPER

Page 14: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

14

Hay otra forma de crear Helpers personalizados en el que “construimos el contenido” que queremos

generar. Será una clase, con métodos que no generan HTML directamente como una vista parcial, si

no que devuelve un objeto de tipo IHtmlString. Las etiquetas se generan con TagBuildery los

atributos se añaden con MergeAttribute.

Un ejemplo muy útil, y que encontramos en muchas páginas, es crear nuestro propio ActionLink para

trabajar con imágenes.

using System.Web; using System.Web.Mvc; using System.Web.Mvc.Html; using System.Web.Routing; namespace _3_MVCHelpers.Helpers { public static class HtmlImageActionLinkHelper { public static IHtmlString ImageActionLink( this HtmlHelper helper, string imageUrl, string actionName, object routeValues, object htmlAttributes ) { var builder = new TagBuilder("img"); builder.MergeAttribute("src", imageUrl); builder.MergeAttributes(new RouteValueDictionary(htmlAttributes)); var link = helper.ActionLink("[replaceme]", actionName, routeValues); var html = link.ToHtmlString().Replace("[replaceme]", builder.ToString(TagRenderMode.SelfClosing)); return new HtmlString(html); } } }

Para referenciarlo en nuestra propia vista, usaremos @Html.[Método] ([Parámetros]).

Si queremos añadir una imagen 012.jpg que al pulsar sobre ella vaya a la acción Index usaríamos: @Html.ImageActionLink( Url.Content("~/Fotos/012.jpg"), "Index", new { id = 5 }, new { id = "imgnb", width = "100px", height = "150px", alt = "Foto playa de Alicante" } )

LISTADOS

Uno de las interacciones más comunes con el usuario es mostrar listados. Puede ser un catálogo de

enlaces (por ejemplo en una tienda de libros, de música), un listado de preguntas (para poder

consultar las respuestas), resultado de buscar por algún termino, etc.

Por tanto hay que ser versátiles a la hora de mostrar los datos porque si no es operativa el usuario

dejará de usar nuestra aplicación.

PAGINACIÓN Y ORDENACIÓN

Los listados que hemos generado con las plantillas de ASP.NET MVC son muy básicos si lo

comparamos con los GridViews de los WebForms.

Page 15: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

15

El 5º día usaremos componentes externos que nos permite generar listados muy potentes, pero hoy

nos centramos en el helper WebGrid que está a medio camino entre uno y otro y que en la mayoría

de los casos nos sobrará.

Explicarlo con detalle es muy complicado, pero aplicarlo en ejemplo es muy sencillo. Vamos a crear

una nueva acción Grid que se consulte como el listado de Index

public ViewResult Grid() { return View(db.VCSI_LIBRO.ToList()); }

Ahora creamos la vista (podemos usar la plantilla de listado para ver los campos o vacía) y hacemos

una definición del Grid y luego lo mostramos.

@model IEnumerable<CSI_BibliotecaBDOracle.VCSI_LIBRO> @{ ViewBag.Title = "Grid"; var grid = new WebGrid( source: Model, rowsPerPage: 4); } <h2>Grid</h2> @grid.GetHtml( columns: grid.Columns ( grid.Column("Isbn", "Isbn"), grid.Column("Titulo", "Título"), grid.Column("Descripcion_TipoLibro", "Tipo de libro") ) )

El resultado en pantalla es

Lo primero que haremos será pulsar en la cabecera para ordenar. No obtendremos el resultado

deseado, pero si que veremos que va cambiando la URL Grid?sort=Titulo&sortdir=ASC,

sort=Titulo&sortdir=DESC&page=2, dependiendo de si pulsamos ordenar o paginar.

Seremos nosotros desde la acción los que gestionemos la ordenación, porque la paginación si que la

realiza el propio componente.

Actualizar la acción para mostrar los resultados es bastante sencill

public ViewResult Grid(string sort, string sortdir) { IEnumerable<VCSI_LIBRO> libros = db.VCSI_LIBRO; if (!String.IsNullOrEmpty(sort))

Page 16: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

16

{ switch (sort.ToLower()) { case "titulo": libros = (sortdir == "DESC" ? libros.OrderByDescending(l => l.TITULO) : libros.OrderBy(l => l.TITULO)); break; } } else libros = libros.OrderBy(l => l.TITULO); return View(libros.ToList()); }

Detectamos que nos llegue un campo por el que ordenar. Por cada campo miramos si la ordenación

es ascendente (por defecto) o descendente y actualizamos los libros a mostrar.

PERSONALIZACIÓN

Para comenzar con listados o grids el formato que nos ofrece es bastante atractivo. Si deseamos

modificar la apariencia debemos recurrir a los estilos CSS.

Por ejemplo WebGrid permite asignar el estilo a todos los elementos tableStyle, headerStyle,

rowStyle y footerStyle. Incluso permite alternar el estilo de cada fila con alternatingRowStyle.

Cuando veamos la plantilla de las aplicaciones de la UA, los listados se quedarán con el siguiente

formato

Page 17: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

17

GENERACIÓN DE VERSIONES IDIOMÁTICAS, CASTELLANO, VALENCIANO E INGLÉS

CONFIGURAR Y DETECTAR EL IDIOMA POR DEFECTO

La forma más cómoda de gestionar el idioma es definir el método Application_AcquireRequestState

en Golbal.asax.cs. Almacenamos en la variable Session[“idioma”] el idioma por defecto. Luego la

asignamos a CurrentCulture del proceso actual. En cada llamada que hagamos se llamará a este

método.

protected void Application_AcquireRequestState(object sender, EventArgs e) { const string idiomaDefecto = "es"; if (HttpContext.Current.Session != null) { CultureInfo ci = (CultureInfo)this.Session["idioma"]; if (ci == null) { ci = new CultureInfo(idiomaDefecto); this.Session["idioma"] = ci; } Thread.CurrentThread.CurrentUICulture = ci; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name); } else { CultureInfo ci = new CultureInfo(idiomaDefecto); Thread.CurrentThread.CurrentUICulture = ci; Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(ci.Name); } }

RECURSOS

El elemento base para gestionar las traducciones son los recursos.

Una buena práctica es crearnos una carpeta Resources y meter todos aquellos ficheros que

necesitemos. Se pueden crear a nivel de controlador o modelo o a nivel de aplicación si nuestro

proyecto es muy básico.

Sobre el proyectos pulsamos botón derecho Agregar > Nueva carpeta y le ponemos el

nombre .

Ahora sobre esta nueva carpeta Agregar > Nuevo elemento … y seleccionamos (o filtramos) Archivo

de recursos. Le llamamos modelos.resx

Page 18: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

18

Añadimos las descripciones que queremos que estén en varios idiomas

Es importante que marquemos el fichero como público para que se pueda acceder desde el resto de

los elementos de MVC

Para crear la versión de recursos para otro idioma, copiamos y pegamos el que usemos de base y

luego le añadimo .[dos digitos del idioma] antes de la extensión del fichero.

Es nuestro caso para crear el fichero de recursos en inglés renombraríamos copia de modelos.resx

por modelo.en.resx y traduciríamos los valores de cada una de las etiquetas.

VISTAS

La manera más cómoda de pasar textos traducidos a las vistas, en caso de que sean pocos, es usar el

controlador y el objeto ViewBag. Para acceder a un recurso escribimos el [nombre carpeta de

recursos].[ nombre recurso (sin extensión].etiqueta.

ViewBag.Titulo = Resources.modelos.tituloLabel;

Luego en la propia vista lo referenciamos

Page 19: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

19

Texto del fichero de recursos: @ViewBag.Titulo<br />

Si la vista contiene muchos elementos a traducir, lo más sencillo es crear dos versiones de la vista de

la misma manera que lo hemos hecho con el fichero de recursos. Cogemos la vista base index.cshtml

la copiamos y la pegamos y renombramos por index.en.cshml. Hacemos los cambios que

corresponda

Creamos una clase para gestionar los idiomas CultureHelper. La variable Cultures almacena los

idiomas con los que trabajemos. El primero de ellos será el que se usará por defecto en caso de que

detectemos otro que no se corresponda con nuestro listado.

using System; using System.Collections.Generic; using System.Linq; namespace _3_MvcGlobalization.Helpers { public static class CultureHelper { // Include ONLY cultures you are implementing as views private static readonly Dictionary<String, bool> Cultures = new Dictionary<string, bool> { {"es", true}, // first culture is the DEFAULT {"en", true}, {"ca", true} }; /// <summary> /// Returns a valid culture name based on "name" parameter. If "name" is not valid, it returns the default culture "en-US" /// </summary> /// <param name="name">Culture's name (e.g. en-US)</param> public static string GetValidCulture(string name) { if (string.IsNullOrEmpty(name)) return GetDefaultCulture(); // return Default culture if (Cultures.ContainsKey(name)) return name; // Find a close match. For example, if you have "en-US" defined and the user requests "en-GB", // the function will return closes match that is "en-US" because at least the language is the same (ie English) foreach (var c in Cultures.Keys) if (c.StartsWith(name.Substring(0, 2))) return c; // else return GetDefaultCulture(); // return Default culture as no match found } /// <summary> /// Returns default culture name which is the first name decalared (e.g. en-US) /// </summary> /// <returns></returns> public static string GetDefaultCulture() { return Cultures.Keys.ElementAt(0); // return Default culture } /// <summary> /// Returns "true" if view is implemented separatley, and "false" if not.

Page 20: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

20

/// For example, if "es-CL" is true, then separate views must exist e.g. Index.es-cl.cshtml, About.es-cl.cshtml /// </summary> /// <param name="name">Culture's name</param> /// <returns></returns> public static bool IsViewSeparate(string name) { if (Cultures.ContainsKey(name)) return Cultures[name]; return false; } } }

El siguiente paso es crear nuestro propio controlador que detectará el idioma y personalizará la vista.

Requerimos hacer uso de dos métodos de la clase Controller: ExecuteCore y OnActionExecuted.

El primero normaliza el idioma, lo almacena en una variable sesión y establece CurrentCulture.

En Session[“idioma”] vamos a almacenar el idioma que se haya detectado o el que se seleccione (lo

veremos luego). De esa manera lo podremos usar en cualquier vista o controlador.

El segundo establece la vista que debe abrir el controlador dependiendo del idioma que se haya

seleccionado.

using System.Globalization; using System.Threading; using System.Web.Mvc; using _3_MvcGlobalization.Helpers; namespace _3_MvcGlobalization.Controllers { public class UaController : Controller { protected override void OnActionExecuted(ActionExecutedContext filterContext) { // Detectamos si llamamos desde una vista var view = filterContext.Result as ViewResultBase; if (view == null) // En caso de que no sea, salims return; string cultureName = Thread.CurrentThread.CurrentCulture.Name; // Buscamos si if (cultureName == CultureHelper.GetDefaultCulture()) return; // Are views implemented separately for this culture? if not exit bool viewImplemented = CultureHelper.IsViewSeparate(cultureName); if (viewImplemented == false) return; string viewName = view.ViewName; int i; if (string.IsNullOrEmpty(viewName)) viewName = filterContext.RouteData.Values["action"] + "." + cultureName; // Index.en-US else if ((i = viewName.IndexOf('.')) > 0) { // contains . like "Index.cshtml" viewName = viewName.Substring(0, i + 1) + cultureName + viewName.Substring(i); } else viewName += "." + cultureName; // e.g. "Index" ==> "Index.en-Us" view.ViewName = viewName;

Page 21: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

21

filterContext.Controller.ViewBag._culture = "." + cultureName; base.OnActionExecuted(filterContext); } protected override void ExecuteCore() { var idioma = Session["idioma"]; string cultureName; if (idioma != null) cultureName = idioma.ToString(); else { cultureName = (Request.UserLanguages == null ? CultureHelper.GetDefaultCulture() : Request.UserLanguages[0]); if (cultureName.IndexOf("-") > 0) cultureName = cultureName.Substring(0, cultureName.IndexOf("-")); Session["idioma"] = cultureName; } // Normalizamos var normalizedCultureName = CultureHelper.GetValidCulture(cultureName); if (normalizedCultureName != cultureName) { cultureName = normalizedCultureName; Session["idioma"] = cultureName; } // Actualizamos el idioma Thread.CurrentThread.CurrentCulture = CultureInfo.CreateSpecificCulture(cultureName); Thread.CurrentThread.CurrentUICulture = CultureInfo.CreateSpecificCulture(cultureName); base.ExecuteCore(); } } }

PLANTILLA

Por defecto la plantilla que se usa es la que se configure en /Views/ _ViewStart.cshtml.

Por seguir con este criterio, si modificamos este fichero y usamos la variable Session[“idioma”]

tendremos la plantilla que se debe usar.

@{ Layout = "~/Views/Shared/_Layout." + @Session["idioma"] + ".cshtml"; }

En clase lo optimizaremos para que no se produzcan errores cuando no esté definida esta variable.

CAMBIAR DE IDIOMA

Nuestra plantilla permite cambiar el icioma

public ActionResult ChangeLanguage(string language) { Session["idioma"] = language; if (Request.UrlReferrer != null)

Page 22: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

22

return Redirect(Request.UrlReferrer.ToString()); return Redirect("Index"); }

[ @Html.ActionLink("English", "ChangeLanguage", "Home", new { language = "en" }, null) ] [ @Html.ActionLink("Español", "ChangeLanguage", "Home", new { language = "es" }, null) ] [ @Html.ActionLink("Valencià", "ChangeLanguage", "Home", new { language = "ca" }, null) ]

En caso de que nos envíen un idioma que no se corresponda con el listado admitido, no habrá

problemas porque en la siguiente llamada a Index (o la página desde donde se llamó) se detectará

que no se corresponde con uno de los admitidos y se asignará el que tengamos por defecto.

MODELO

Siguiendo el ejemplo del libro de antes, si queremos que la descripción del título salga del fichero de

recursos remplazamos

[Display(Name = "Título del libro")] public string Titulo { get; set; }

por

[Display(Name = "tituloLabel", ResourceType = typeof(Resources.modelos))] public string Titulo { get; set; }

En caso de que nos de un error de que no se encuentra un recurso público con ese nombre, recordar

lo de marcar Public en el fichero de recursos.

POCO

Cuando el modelo de los datos lo gestionamos desde EF se complica la posibilidad de incluir data

annotations porque no disponemos de forma sencilla el modelo y los atributos.

Lo vamos a hacer de forma manual para comprender el proceso. Creamos una clase partial donde

indiquemos los campos y podamos añadir las anotaciones.

public partial class CSI_LIBRO { .... }

Todos los campos se declaran de tipo virtual para no interferir con la declaración de EF

public partial class CSI_LIBRO { public virtual decimal ID { get; set; } public virtual string ISBN { get; set; }

Ahora ya podemos incluir las anotaciones sin problemas.

Page 23: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

23

Generar código automático

El proceso anterior se puede gestionar cuando tenemos tablas básicas como con las que estamos

trabajando estas semanas, pero es inviable con una aplicación con varias tablas y con muchos

campos cada un de éstas.

Por eso EF incluye una opción de generar este código de forma automática. SI pulsamos el botón

derecho sobre una tabla veremos que disponemos de la opción “Agregar elemento de generación de

código”.

Nos abre una ventana que tiene seleccionado por defecto la plantilla de Datos. Seleccionamos

Código y nos saldrá una lista de plantillas. Seleccionamos EF 4.X POCO Entity Generator. Le ponemos

un nombre en la parte inferior, ModelBiblioteca.tt y pulsamos Agregar.

Volveremos al EF y aparentemente no veremos nada nuevo. Ha añadido una referencias y nos ha

creado el fichero ModelBiblioteca.Context.tt y ModelBiblioteca.tt.

Page 24: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

24

Si pulsamos sobre la flecha que hay a la izquierda de ModelBiblioteca.tt veremos que aparecen las

tablas y/o vistas que tengamos en nuestro EF.

Si abrimos una de estas clases veremos que es de tipo parcial y con todos los campos definidos como

virtual. Ahora le podemos añadir los atributos que veamos necesarios.

Si por ejemplo a los tipos de libros le incluímos:

[Required]

[Display(Name = "Descripción")]

public virtual string DESCRIPCION

Veremos los resultados cuando creemos o modifiquemos un tipo de libro. No nos permite dejarlo en

blanco y la decripción del campo pasa de DESCRIPCION a Descripción.

Aunque hemos visto que el proceso es muy sencillo y que se crea de forma muy rápida, debemos

tener mucho cuidado con las actualizaciones en la base de datos y en el modelo EF. Un cambio en el

EF implicará un cambio en el código generado con POCO. Lo más común y viendo su sencillez es

borrarlo y volverlo a crear. Todas nuestras anotaciones se perderán en este proceso.

Page 25: ASP.NET MVC 3 y 4 - Servicio de Informática. … · obtener todos los registros de una tabla [conexión].[nombretabla ... where l.TITULO == "El Principito" ... Todo el contenido

ASP.NET MVC 3 Y 4 | DESARROLLO DE APLICACIONES CON EL FRAMEWORK MVC 3 Y 4

Andrés Vallés Botella | Analista | Desarrollos propios Servicio de Informática | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | España http://si.ua.es/es

25

PROYECTO

CREAR TABLAS BÁSICAS, CONTROLADORES BÁSICOS Y MASTER PAGE