En futuros posts iremos entrando en mayor detalle, pero de momento vamos a echar un vistazo desde una cierta distancia para tener la idea general sobre dónde estamos y la evolución que vamos a encontrar en las nuevas versiones de tecnologías y plataformas, de forma que podamos ver en qué nos afectará como desarrolladores y, en definitiva, para qué tenemos que irnos preparando.
Disclaimer: estamos aún en una fase en la que algunas cosas pueden cambiar y aún no existe información exhaustiva de muchos detalles, por lo que pueden existir ausencias o imprecisiones. Pero bueno, digo yo que el grueso será más o menos correcto ;-)
En este post vamos a ver algunas técnicas muy básicas que nos permitirán ejecutar un código u otro en el lado cliente de aplicaciones ASP.NET MVC (o ASP.NET en general) en función del modo de compilación.
En general, es un enfoque que puede interesarnos siempre que se trate de realizar un trabajo pesado, que no requiera intervención alguna por parte del usuario ni siquiera para darle feedback del progreso o terminación, y que pueda ejecutarse de forma independiente a las peticiones HTTP que acceden a la aplicación.
System.Web.Optimization
traerá (aún está en beta) algunas novedades interesantes al sistema de bundling que se incluye de serie en los proyectos ASP.NET MVC y se distribuye a través de Nuget en el paquete Microsoft.AspNet.Web.Optimization.En particular, vamos a centrarnos en una característica muy práctica si queremos utilizar una Content Delivery Network (CDN) externa (como la de Microsoft, Google o incluso una propia) para delegar a ella la carga de bibliotecas de script comunes, pero queremos a la vez proporcionar una alternativa local por si acaso ésta fallase debido a cualquier motivo.
Publicado por José M. Aguilar a las 9:15 a. m.
Etiquetas: asp.net, aspnetmvc, novedades, optimización, trucos
Venga, lo confieso: yo también he generado desde mis aplicaciones contenidos HTML y los he enviado al cliente en un archivo con extensión XLS, incluso modificando el content-type, para que pareciera un documento de hoja de cálculo. Durante años. Y también le he dicho a mis clientes que el molesto mensaje que aparece al abrirlo desde Excel, el que indica que el contenido del archivo no coincide con la extensión del mismo, es algo normal.
Pero esto se acabó desde que descubrí ClosedXML, un magnífico componente para .NET basado en el estándar OpenXML que permite la generación de archivos Excel “de verdad”, con formato, estilos, fórmulas, rangos, filtros, y casi todo lo que se nos pueda ocurrir.
Publicado por José M. Aguilar a las 9:15 a. m.
Etiquetas: .net, asp.net, closedxml, componentes, desarrollo
Url.Action()
o Html.ActionLink()
, y cómo usando una simple línea de código podíamos hacer que las rutas se generaran usando sólo minúsculas.
Url.Action()
o Html.ActionLink()
, éstas son generadas usando mayúsculas y minúsculas según hubiéramos indicado en los parámetros de las llamadas:Helper | URL generada |
---|---|
@Url.Action("index", "home") | /home/index |
@Url.Action("List","Products", new{ Category="PC" }) | /Products/List?Category=PC |
@Url.Action("VIEWALL", "PRODUCTS") | /PRODUCTS/VIEWALL |
Como podemos ver, la URL resultante queda a criterio del desarrollador o, lo que es peor, al puro azar. A veces incluso no es algo que podamos elegir fácilmente, puesto que son generadas por otros componentes como T4MVC. Y no sé si desde el punto de vista del SEO tendrá su impacto, pero desde luego el ofrecer estas direcciones sin un aspecto unificado no da buena impresión.
En versiones anteriores a ASP.NET 4.5, esto podíamos solucionarlo creando helpers, o rutas personalizadas que, heredando de
Route
, realizaran esta conversión a minúsculas. Sin embargo, ahora disponemos de un mecanismo más cómodo para forzar que las URLs generadas sean normalizadas a minúsculas, así:public class RouteConfig { public static void RegisterRoutes(RouteCollection routes) { routes.LowercaseUrls = true; routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); [...] } }
Un detalle, sin duda interesante, para tener en cuenta en nuestros desarrollos.
Publicado en: Variable not found.
DefaultDisplayMode
proporcionada por el framework, con la que podíamos cubrir la mayoría de necesidades comunes.Así, veíamos cómo el siguiente código era suficiente para registrar un nuevo Display Mode llamado “iPhone”, que sería activado cuando en el identificador del agente de usuario (encabezado user-agent de la petición) incluyera el texto “iPhone”:
DisplayModeProvider.Instance.Modes.Insert(0, new DefaultDisplayMode("iPhone") { ContextCondition = (context => context.Request.UserAgent.IndexOf ("iPhone", StringComparison.OrdinalIgnoreCase) >= 0) });Hecho esto, ya podíamos definir vistas alternativas a las habituales específicas para este dispositivo, cuyos nombres de archivo acabarían siempre en “.iphone.cshtml”. Observad que estamos usando la clase
DefaultDisplayMode
, a la que estamos facilitando la condición que debe cumplirse para que se active este Display mode.Sin embargo, si pensamos crear muchas vistas específicas para dispositivos, podríamos encontrarnos con un maremagnum de archivos como el que veis en la captura de pantalla adjunta.
Obviamente, no es una situación fácilmente manejable, así que ¿por qué no cambiar la forma de nombrar los archivos dependiendo del Display Mode actual? Pues dicho y hecho, vamos a conseguir que cada dispositivo disponga de una carpeta específica para guardar sus vistas.
Heredando de DefaultDisplayMode
Si analizamos el código fuente de la clase DefaultDisplayMode
, veremos que hay varios miembros virtuales que podemos sobrescribir para tomar el control, y uno de ellos es el método TransformPath()
, encargado de transformar la ruta hacia el archivo físico donde está definida la vista teniendo en cuenta el nombre del Display Mode actual.El código por defecto del método es el siguiente:
protected virtual string TransformPath(string virtualPath, string suffix)
{
if (string.IsNullOrEmpty(suffix))
return virtualPath;
string extension = Path.GetExtension(virtualPath);
return Path.ChangeExtension(virtualPath, suffix + extension);
}
O sea, que se reemplaza la extensión del archivo, normalmente “.cshtml” por el resultado de concatenar el sufijo suministrado (el nombre del Display Mode) a dicha extensión. Por esta razón, el comportamiento por defecto del framework es utilizar construcciones como “nombrevista.iphone.cshtml”.Si, como es el caso, queremos cambiar la ruta donde van a intentar localizarse las vistas, lo único que tenemos que hacer es crear una clase descendiente de
DefaultDisplayMode
, sobrescribir la forma de “montar” la ruta hacia la vista, y utilizar esta nueva clase para registrar los modos de visualización que nos interesen. Una posible implementación podría ser la siguiente: public class OrganizedDisplayMode: DefaultDisplayMode
{
public OrganizedDisplayMode(string displayModeId): base(displayModeId)
{
}
protected override string TransformPath(string virtualPath, string suffix)
{
if (string.IsNullOrEmpty(suffix))
return virtualPath;
// Transforms /index.cshtml --> /suffix/index.cshtml
int lastSeparator = virtualPath.LastIndexOf('/');
virtualPath = virtualPath.Substring(0, lastSeparator) +
"/" + suffix +
virtualPath.Substring(lastSeparator);
return virtualPath;
}
}
Y en la inicialización de la aplicación ya podríamos registrar este Display Mode y asociarlo a la condición que esperamos que cumpla la petición: DisplayModeProvider.Instance.Modes.Insert(0,
new OrganizedDisplayMode("iPhone")
{
ContextCondition =
context => context.Request.UserAgent.Contains("iPhone")
});
De esta forma, ya podemos organizar las vistas como podéis observar en la captura de pantalla de la derecha: cada dispositivo (o Display Mode registrado) dispondría de una carpeta en cuyo interior se encontrarían las vistas específicas para el mismo.Y observad que su funcionamiento no se limita a las vistas asociadas a controladores concretos, la solución también sería válida en vistas compartidas (Shared) y con aquellas incluidas en áreas :-)
Publicado en Variable not found.
Publicado por José M. Aguilar a las 9:18 a. m.
Etiquetas: asp.net, aspnetmvc, desarrollo, novedades, trucos
El cartel oficial del evento es el siguiente:
SHAREPOINT PARA APLICACIONES EN ASP.NET DESDE CERO
¿Eres programador ASP.NET y no sabes nada acerca de SharePoint? No te preocupes, en AUGES hemos preparado este evento para que aprendas desde cero sobre está plataforma de Microsoft que está pegando muy fuerte y que ya usan millones de clientes en todo el mundo. Veremos las bases de la plataforma, los servicios, buenas practicas, el IDE de desarrollo… No puedes perdértelo!!!Ponente
Edin Kapic que es Key Consultant en Pasiona consulting, con más de 6 años de experiencia en Sharepoint, ponente en la European Sharepoint Conference de Berlín y miembro de Grupo de Usuarios de SharePoint de Cataluña – SUG.CAT.
Os recuerdo que se trata de un Webcast, o sea, que podéis asistir a este evento desde vuestro sillón favorito, acompañados de una buena cerveza y un saco de palomitas. Y por supuesto, es totalmente gratuito.
Lo único que debéis hacer para poder asistir es registraros:
- Día: miércoles 27 de junio de 2012, 19:00h (hora peninsular española)
- URL del registro: https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032514556&Culture=es-ES
Publicado en Variable not found.
Lo más habitual es utilizarlas para crear editores o visualizadores para tipos de datos concretos, en cuyo caso las vistas disponibles en las carpetas citadas anteriormente se llaman igual que el tipo de dato a editar o visualizar.
Por ejemplo, si tenemos una vista llamada int.cshtml en la carpeta /Views/Shared/EditorTemplates , será la utilizada como editor de propiedades de tipo entero. Algo parecido utilizamos por aquí, hace ya bastante tiempo, para integrar jQuery datepicker en nuestras aplicaciones.
También es habitual encontrar en estas carpetas plantillas vinculadas a propiedades u objetos de forma directa mediante el atributo
[UIHint]
(como en este post), mediante el cual se indica explícitamente qué plantilla debe utilizarse: [DisplayName("Long description")]
[StringLength(140), Required]
[UIHint("LimitedTextArea")] // Use the template "LimitedTextArea.cshtml"
public string Description { get; set; }
Y otra alternativa, aunque quizás algo menos utilizada, es la posibilidad de indicar la plantilla a utilizar en la misma llamada al helper
EditorFor()
o DisplayFor()
que provoca su renderizado: @Html.EditorFor(m=>m.Description, "LimitedTextArea")
Sin embargo, hay una posibilidad adicional, que curiosamente no es demasiado conocida, consistente en utilizar el atributo DataTypeAttribute
para indicar la plantilla de edición o visualización a emplear para la propiedad a la que se aplica.El funcionamiento es trivial: decoramos las propiedades con
[DataType]
suministrándole como parámetro el tipo de información que va a contener, y el sistema intentará localizar una plantilla con ese nombre. Veamos un ejemplo: [DataType(DataType.EmailAddress)]
public string Email { get; set; }
[DataType(DataType.Time)]
public DateTime StartDate { get; set; }
[DataType(DataType.Html)]
public string LongDescription { get; set; }
En el momento de renderizar un editor (o un display) template para la primera propiedad, el sistema utilizará, si existe, el archivo EmailAddress.cshtml; en caso de no existir, intentará localizar un string.cshtml, y si tampoco lo encuentra generará un editor por defecto. En el segundo caso se realizará la misma operación con Time.cshtml, y el último de ellos usará la plantilla Html.cshtml como primera opción.Podemos utilizar como nombre de plantilla todos los valores que encontramos en la enumeración
DataType
:DateTime
Date
Time
Duration
PhoneNumber
Currency
Text
Html
MultilineText
EmailAddress
Password
Url
ImageUrl
Y como viene siendo costumbre, vamos a dar un repaso a todo lo que encontramos en esta nueva entrega, que presumiblemente será la última (bueno, o penúltima, nunca se sabe) antes de la versión definitiva. Eso sí, para no hacer el post demasiado largo nos centraremos exclusivamente en los cambios más destacables y visibles que se han introducido respecto a la versión beta.
Esto tiene mucha utilidad directa, por ejemplo, a la hora de construir aplicaciones web que adapten su interfaz en función de si el cliente es un dispositivo móvil o desktop, pero como casi siembre ocurre en el framework, se trata de un mecanismo muy extensible y fácil de adaptar a otras necesidades.
- www.acme.org, que sería el sitio principal.
- crm.acme.org, con el sistema CRM de la empresa.
- erp.acme.org, con un sistema de gestión empresarial.
- administration.acme.org con las herramientas de administración del sistema.
- etc.
No es una tarea complicada en ASP.NET, aunque hay que hacer algunos ajustillos para que todo funcione correctamente. Veámoslos.
En este post vamos a ver otros comportamientos de Razor en distintos escenarios.
System.Web.Optimizations
que se distribuía con la developer preview de MVC 4, aunque también decíamos que este paquete era igualmente descargable a través de Nuget, y esto hacía posible su uso con MVC 3 o incluso con WebForms. En este post ascenderemos a un nivel de abstracción mucho mayor que el proporcionado por las conexiones persistentes y veremos cómo utilizar los Hubs, otro mecanismo proporcionado por SignalR que nos permitirá lograr una integración increíble entre el código cliente y de servidor, y hará aún más sencilla la implementación de este tipo de servicios.
Por tanto, esta tercera entrega de la serie vamos a dedicarla (otra vez ;-)) a las conexiones persistentes, veremos qué cosas han cambiado con la llegada de la revisión 0.4, y desarrollaremos un nuevo ejemplo que ilustre las novedades que podemos encontrar a la hora de trabajar a bajo nivel con SignalR.
Url.Action()
y Html.ActionLink()
son capaces de generar URLs hacia acciones partiendo de la información disponible en la tabla de rutas y de los parámetros que les suministramos. Por ejemplo, el siguiente código genera un enlace hacia la acción
Edit
del controlador Friends
, suministrándole el valor 3 al parámetro de ruta id
:@Html.ActionLink("Edit friend", "Edit", "Friends", new {id = 3}, null)
Sin embargo, estos helpers no nos ayudan demasiado cuando desconocemos a priori el valor de los parámetros. De hecho, si en lugar del 3 que hemos suministrado quisiéramos enviar un valor obtenido a través de scripting tendríamos un problema, puesto que el bloque de código anterior se ejecuta en servidor y el enlace sería generado antes incluso de ejecutarse el script para obtener el valor a enviar.Esta es una pregunta que suelo ver muy a menudo en comunidades y foros (como el de ASP.NET MVC en MSDN) y me han realizado más de una vez en el curso de ASP.NET MVC 3 que tutorizo en CampusMVP, así que vamos a ver un par de enfoques para enfrentarnos a este escenario, bastante frecuente al trabajar en sistemas web y, sobre todo, en aquellos muy basados en scripting y Ajax.
Publicado por José M. Aguilar a las 10:06 a. m.
Etiquetas: asp.net, aspnetmvc, consultas, desarrollo, trucos