Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
ASP.NET MVC 3 y 4
PERSONALIZACIN
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es/
3 DA PERSONALIZACIN
ENTITY FRAMEWORK BSICO
Vamos a estudiar lo bsico para hacer consultas con Entity Framework. Nos creamos en la aplicacin de libros con Oracle, en el controlador Home, el mtodo 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 bsicas que nos ofrece MVC. 2. Abrir la conexin a la Entidad. Depender del nombre que se le haya puesto a la Entidad cuando hayamos importado el modelo. Lo ms cmodo es usar using([declaracin conexin]) {} porque se elimina automticamente al salir del using. 3. Hacer la consulta y almacenar los resultados en la variable del punto 1. Lo ms sencillo es obtener todos los registros de una tabla [conexin].[nombretabla].ToList(). Las tablas pasan a ser unos objetos ms con los que podemos trabajar. 4. Cerrar o liberar la conexin 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]). Podra 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 ms sencilla, pero la tradicional de consultas (Linq) sera:
libros = (from l in bd.CSI_LIBRO select l).ToList();
Como son muy sencilla ambas, que cada uno seleccione la que le sea ms sencilla. Ahora creamos una vista para este mtodo y le indicamos el modelo CSI_LIBRO y la plantilla List.
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
Si lo ejecutamos vemos el listado de libros. Tambin incluye la creacin de nuevos libros, edicin, borrado. Toda esta parte la podemos eliminar.
FILTRADO
Cuando hacemos consulta lo normal es mostrar aquellos que cumplen una condicin. La ms bsica podra 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();
ORDENACIN
Disponemos de dos funciones en lambda OrderBy y OrderByDescending al que debemos indicar el campo de ordenacin
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 ms 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();
PAGINACIN
Uno de los problemas que nos encontramos cuando trabajamos con Oracle es la paginacin. Obtener un nmero 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();
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
HELPERS (BSICO)
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 cdigo HTML. Aunque el listado es mucho ms amplio que el de URLHelpers, lo cierto es que todos son muy parecidos. Vamos a comenzar con el elemento bsico que es el formulario que recoge todos los campos con los que trabajamos. Nos creamos una mtodo / accin en nuestro controlador para ir jugando. Le podemos llamar helpers. No le vamos a pasar ningn modelo por el momento. Editamos su vista y lo bsico 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()) { }
Por defecto nos genera una accin a nosotros mismos y hace la llamada con mtodo post Con los parmetros podemos indicar que controlador / accin del action y el mtodo
Html.BeginForm("Create", "Libro", FormMethod.Get)
Y generara
<form action="/Libro/Create" method="get"></form>
Todos los HTMLHelpers incluyen un ltimo parmetro abierto a aadir 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 adems quisiramos aadir 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"})
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
En caso de que no usemos using en la declaracin 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 cdigo ms 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 parmetro.
@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 parmetro.
@Html.TextBox("Nombre", "Alberto")
genera
<input id="Nombre" name="Nombre" type="text" value="Alberto" />
Todo el contenido que se asigne en el valor por defecto (en cualquier HTMLHelper) se codifica automticamente para evitar que se produzcan ataques XSS injection.
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
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
ListBox(s:name, list:selectlistitems) Genera una lista de valores en la que podemos seleccionar ms 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 Informtica", "Seleccin y Formacin", "Servicio de Personal"})) }
Genera
El siguiente paso para poder hacer modificaciones a la plantilla es cambiar el parmetro Herramienta personalizada a las 6 plantillas. Por defecto tiene el valor TextTemplatingFileGenerator. Lo dejamos vaco.
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
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 cdigo 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 cdigo y no como descripcin. 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 relacin].[campo descripcin] (slo en el caso que hayamos realizado el include porque si no es inaccesible).
<td> @Html.DisplayFor(modelItem => item.CSI_TIPOLIBRO.DESCRIPCION) </td>
HELPERS
HTMLHELPERS (CONTINUACIN)
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 ejecucin con la propiedad AddModelError(campo, mensaje de error) del objeto ModelState. En caso de que el campo lo dejemos vaco estamos lanzando un error a nivel de modelo
ModelState.AddModelError("", "Prueba de un error general");
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
El resultado
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 visualizarn en este punto.
@Html.ValidationMessage("Nombre")
Action(s: [nombre accin]) Nos permite llamar a un mtodo / accin 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 estn pensadas para escribir bloques de cdigo, action est orientada a ejecutar el proceso completo de la accin de un controlador (que incluye la generacin del cdigo con la Vista). El resultado es una cadena de texto con todo el contenido generado.
@Html.Action("Cabecera")
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
10
ActionLink(s: [descripcin], s: [accin], s: [controlador]) Permite generar enlaces a acciones determinadas de un controlador. Por ejemplo si queremos poner un enlace a la accin Index del Home usaramos
@Html.ActionLink("Pgina principal", "Index", "Home")
Se usa en las plantillas para realizar cualquier accin con el modelo del controlador, alta, baja, edicin o borrado. Dispone de muchas sobrecargas este Helper, permitiendo desde indicar protocolor, servidor y ancla, hasta definir los atributos HTML. RouteLink(s: [descripcin], d: [valores ruta]) Es parecido alterior, porque genera un enlace a una ruta o una accin de un controlador. Es algo ms 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 accin Acerca de usaramos
@Html.RouteLink("Acerca de", new { controller = "Home", action="About"})
RenderAction(s: [nombre accin]) Es idntica 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. Si deseamos que una accin sea slo llamada desde Action o RenderAction pero no directamente como una direccin ms en el navegador podemos usar la anotacin ChildActionOnly antes de la declaracin [ChildActionOnly] public ActionResult Cabecera() { }
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
11
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 ms cmodo) Si necesitamos mostrar la etiqueta, la caja de texto y la validacin
@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 maysculas. MVC incluye data annotations en los campos lo que permite personalizar la informacin que luego se ver con estos Helpers. Por el momento slo vamos a ver el de la descripcin, pero maana nos centraremos en todo el tema de validacin, que es donde realmente se saca el potencial. Las anotaciones se ponen con corchetes antes de la definicin de la propiedad.
[Display(Name = "Ttulo del libro")] public string Titulo { get; set; }
Display permite personalizar aspectos de la visualizacin. Con Name le indicamos el nombre o label que tendr esta propiedad. El resultado es:
PERSONALIZADOS
Como es lgico MVC nos permite crear nuestros propios Helpers para darle mayor potencia a stos. Se comportan como una funcin a la que se pasan parmetros si los necesita, y dentro genera el cdigo que queremos mostrar. El formato es @Helper [Nombre Funcin]( [parmetros] )
@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> }
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
12
El resultado es el siguiente
Es costumbre a la hora de poner los parmetros, anteponer el nombre de cada parmetro y luego : (dos puntos).
@BreadCumb(elementos: new[] {"Inicio", "Administracin", "Secciones"})
Si deseamos que es helper sea reutilizables desde cualquier vista, aadimos la carpeta App_Code a nuestro proyecto (no aparece como opcin en las carpetas de ASP.NET) y creamos el fichero BreadCumbHelpers.cshtml. Copiamos el cdigo del Helper y lo guardamos. Ahora para hacerle referencia desde la vista llamaremos al helper de la siguiente manera @[Nombre fichero (sin extensin)].[Nombre del helper o funcin]( [parmetros])
@BreadcumbHelpers.BreadCumb(elementos: new[] { "Inicio", "Administracin", "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 cdigo o dejarlo ms estructurado, si que es cierto que cada uno tiene su uso. Helpers personalizado est pensado para pequeos trozos de cdigo, que generan una programacin sencilla y que se comparte con diferentes vistas de tu proyecto o incluso entre varios. Partial views estn orientadas a secciones de cdigo, con el objetivo de hacer ms clara la estructura. Puede contener una programacin 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 ejecucin de la vista parcial
@Html.Partial("_Cabecera")
RenderPartial(s: nombrevista) Es idntica 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"); }
13
Hay otra forma de crear Helpers personalizados en el que construimos el contenido que queremos generar. Ser una clase, con mtodos 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 aaden con MergeAttribute. Un ejemplo muy til, y que encontramos en muchas pginas, es crear nuestro propio ActionLink para trabajar con imgenes.
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.[Mtodo] ([Parmetros]). Si queremos aadir una imagen 012.jpg que al pulsar sobre ella vaya a la accin Index usaramos:
@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 ms comunes con el usuario es mostrar listados. Puede ser un catlogo de enlaces (por ejemplo en una tienda de libros, de msica), un listado de preguntas (para poder consultar las respuestas), resultado de buscar por algn termino, etc. Por tanto hay que ser verstiles a la hora de mostrar los datos porque si no es operativa el usuario dejar de usar nuestra aplicacin.
PAGINACIN Y ORDENACIN
Los listados que hemos generado con las plantillas de ASP.NET MVC son muy bsicos si lo comparamos con los GridViews de los WebForms.
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
14
El 5 da 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 mayora de los casos nos sobrar. Explicarlo con detalle es muy complicado, pero aplicarlo en ejemplo es muy sencillo. Vamos a crear una nueva accin 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 vaca) y hacemos una definicin 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", "Ttulo"), 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 accin los que gestionemos la ordenacin, porque la paginacin si que la realiza el propio componente. Actualizar la accin 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)) Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
15
Detectamos que nos llegue un campo por el que ordenar. Por cada campo miramos si la ordenacin es ascendente (por defecto) o descendente y actualizamos los libros a mostrar.
PERSONALIZACIN
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 quedarn con el siguiente formato
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
16
IDIOMTICAS,
CASTELLANO,
RECURSOS
El elemento base para gestionar las traducciones son los recursos. Una buena prctica 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 aplicacin si nuestro proyecto es muy bsico. Sobre el proyectos pulsamos botn 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
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
17
Es importante que marquemos el fichero como pblico para que se pueda acceder desde el resto de los elementos de MVC
Para crear la versin de recursos para otro idioma, copiamos y pegamos el que usemos de base y luego le aadimo .[dos digitos del idioma] antes de la extensin del fichero. Es nuestro caso para crear el fichero de recursos en ingls renombraramos copia de modelos.resx por modelo.en.resx y traduciramos los valores de cada una de las etiquetas.
VISTAS
La manera ms cmoda 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 extensin].etiqueta.
ViewBag.Titulo = Resources.modelos.tituloLabel;
18
Si la vista contiene muchos elementos a traducir, lo ms 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;
/// <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. Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
19
El siguiente paso es crear nuestro propio controlador que detectar el idioma y personalizar la vista. Requerimos hacer uso de dos mtodos de la clase Controller: ExecuteCore y OnActionExecuted. El primero normaliza el idioma, lo almacena en una variable sesin 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; Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
20
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) Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
21
En caso de que nos enven un idioma que no se corresponda con el listado admitido, no habr problemas porque en la siguiente llamada a Index (o la pgina 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 descripcin del ttulo salga del fichero de recursos remplazamos
[Display(Name = "Ttulo 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 pblico 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 aadir las anotaciones.
public partial class CSI_LIBRO { .... }
Todos los campos se declaran de tipo virtual para no interferir con la declaracin de EF
public partial class CSI_LIBRO { public virtual decimal ID { get; set; } public virtual string ISBN { get; set; }
22
Generar cdigo automtico El proceso anterior se puede gestionar cuando tenemos tablas bsicas como con las que estamos trabajando estas semanas, pero es inviable con una aplicacin con varias tablas y con muchos campos cada un de stas. Por eso EF incluye una opcin de generar este cdigo de forma automtica. SI pulsamos el botn derecho sobre una tabla veremos que disponemos de la opcin Agregar elemento de generacin de cdigo.
Nos abre una ventana que tiene seleccionado por defecto la plantilla de Datos. Seleccionamos Cdigo 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 aadido una referencias y nos ha creado el fichero ModelBiblioteca.Context.tt y ModelBiblioteca.tt.
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
23
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 aadir los atributos que veamos necesarios. Si por ejemplo a los tipos de libros le inclumos:
[Required] [Display(Name = "Descripcin")] public virtual string DESCRIPCION
Veremos los resultados cuando creemos o modifiquemos un tipo de libro. No nos permite dejarlo en blanco y la decripcin del campo pasa de DESCRIPCION a Descripcin.
Aunque hemos visto que el proceso es muy sencillo y que se crea de forma muy rpida, 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 cdigo generado con POCO. Lo ms comn y viendo su sencillez es borrarlo y volverlo a crear. Todas nuestras anotaciones se perdern en este proceso.
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
24
PROYECTO
CREAR TABLAS BSICAS, CONTROLADORES BSICOS Y MASTER PAGE
Andrs Valls Botella | Analista | Desarrollos propios Servicio de Informtica | Universidad de Alicante Campus de Sant Vicent del Raspeig | 03690 | Espaa http://si.ua.es/es
25