Saltar al contenido

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript... y lo que venga ;)

17 años online

el blog de José M. Aguilar

Inicio El autor Contactar

Artículos, tutoriales, trucos, curiosidades, reflexiones y links sobre programación web
ASP.NET Core, MVC, Blazor, SignalR, Entity Framework, C#, Azure, Javascript...

¡Microsoft MVP!
miércoles, 23 de septiembre de 2009

Si observamos las respuestas enviadas al cliente que solicita una página de un sitio web creado con el framework MVC, veremos que, además de los encabezados HTTP estándar, el sistema añade información sobre las versiones de ASP.NET y del propio framework MVC que estamos utilizando:

Encabezados X-ASPNet*

Esta información, además de consumir ancho de banda (poco, todo hay que decirlo, pero nada despreciable en sitios con tráfico muy alto), aportan información que posibles atacantes podrían utilizar para sacar provecho a vulnerabilidades conocidas de estos productos. En mi opinión, lo mejor es mostrar los menores detalles posibles al respecto.

Si queremos eliminar la pista correspondiente a la versión de ASP.NET sobre la que corre el sistema (X-AspNet-Version), bastaría con introducir en el web.config la siguiente declaración:

<configuration>
    <system.web>
        <httpRuntime enableVersionHeader="false" />
    </system.web>
</configuration>

Para eliminar, además, la información sobre la versión de ASP.NET MVC framework en ejecución (X-AspNetMvc-Version), tendremos que introducir el siguiente código para que se ejecute durante la inicialización de la aplicación, en el global.asax, indicando así que deseamos deshabilitar el envío de este encabezado:

protected void Application_Start()
{
    [...] // Otro código de inicialización
    MvcHandler.DisableMvcResponseHeader = true;
}

Y con eso basta. El resultado, el que sigue:

Encabezados sin información extra

Publicado en: Variable not found.

lunes, 21 de septiembre de 2009

A finales del agosto, James Gregory anunció la publicación de la versión 1.0 de Fluent NHibernate, una librería que ofrece una ágil alternativa a los espesos archivos de configuración de NHibernate.

Su API permite configurar desde el código de una aplicación, de forma fluida la mayoría de las veces, los mapeos entre la estructura de una base de datos relacional y el modelo de objetos que utiliza. Así, evitaremos la manipulación de grandes archivos XML, a la vez que podremos beneficiarnos de la validación en tiempo de compilación y, por supuesto, de posibilidades como la refactorización y el intellisense durante el desarrollo.

El siguiente código muestra el mapeo de la entidad Cat con sus propiedades, algunas de ellas con restricciones, y relaciones con otras entidades a uno (References) y a varios (HasMany); el nombre de la tabla y campos en el modelo relacional es el mismo que el de las propiedades, gracias a la convención sobre configuración, lo que permite simplificar código respecto a su equivalente XML:

public class CatMap : ClassMap<Cat>
{
  public CatMap()
  {
    Id(x => x.Id);
    Map(x => x.Name)
      .Length(16)
      .Not.Nullable();
    Map(x => x.Sex);
    References(x => x.Mate);
    HasMany(x => x.Kittens);
  }
}

Como podemos observar, el equivalente XML es mucho más verboso:

<?xml version="1.0" encoding="utf-8" ?>  
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2"  
  namespace="QuickStart" assembly="QuickStart">  
 
  <class name="Cat" table="Cat">  
    <id name="Id">  
      <generator class="identity" />  
    </id>  
 
    <property name="Name">  
      <column name="Name" length="16" not-null="true" />  
    </property>  
    <property name="Sex" />  
    <many-to-one name="Mate" />  
    <bag name="Kittens">  
      <key column="mother_id" />  
        <one-to-many class="Cat" />  
      </bag>  
  </class>  
</hibernate-mapping>

Otra de las ventajas que aporta es el auto-mapping, que hace utilización intensiva del principio de convención sobre configuración para generar de forma automática mapeos de aquellas entidades que atiendan a unas normas preestablecidas (aunque modificables). El siguiente código muestra la forma tan sencilla de crear los mapeos de todas las entidades definidas en el espacio de nombres MiApp.Entidades, dentro del ensamblado donde se definió la entidad Producto:

var autoMappings = AutoMap.AssemblyOf<Producto>()
  .Where(t => t.Namespace == "MiApp.Entidades");

Además del mapeo objeto-relacional, el software abarca también la configuración del acceso a datos de NHibernate a través de su interfaz de programación. El siguiente código muestra la inicialización de la conexión a una base de datos SQL Server 2005, tomando la cadena de conexión del AppSettings y mapeando automáticamente las entidades que se encuentren definidas en un namespace concreto:
 
var sessionFactory = Fluently.Configure()
    .Database(MsSqlConfiguration.MsSql2005
      .ConnectionString(c => c.FromAppSetting("connectionString"))
    .Mappings(m => m.AutoMappings.Add(
      AutoMap.AssemblyOf<Producto>(type => type.Namspace.EndsWith("Entidades"))))
  .BuildSessionFactory();

Y por último, hay otra característica muy interesante vistas a la realización de pruebas unitarias sobre los mecanismos de persistencia. El código mostrado a continuación crea una instancia de la clase Empleado, la inserta en la base de datos, realiza una lectura de la entidad y la compara con la original, de forma automática:

[Test]
public void EmpleadoMapeaCorrectamente()
{
    new PersistenceSpecification<Empleado>(session)
        .CheckProperty(emp => emp.Id, 1)
        .CheckProperty(emp => emp.Nombre, "José")
        .CheckProperty(emp => emp.Apellidos, "Aguilar")
        .VerifyTheMappings();
}

Más información en la wiki del proyecto. Y las descargas, desde aquí.

Publicado en: Variable not found.

miércoles, 16 de septiembre de 2009

Logo de ASP.NET Ajax Hace unos meses comentaba la posibilidad de utilizar la infraestructura de Google para alojar las librerías javascript de nuestras aplicaciones. Pues bien, ahora es Microsoft la que ha lanzado un servicio similar, Microsoft Ajax CDN, una red de distribución de contenidos desde donde podemos descargar en tiempo de ejecución las librerías de scripts que utilicemos en nuestras aplicaciones.

O en otras palabras, que podemos hacer uso de forma gratuita de estas librerías, sin limitación de ancho de banda e independientemente de si es para fines comerciales o no. Basta con referenciarlas desde nuestro código:

<script src="http://ajax.microsoft.com/ajax/jquery-1.3.2.min.js" 
type="text/javascript"></script>

La principal ventaja que ofrece este método es la velocidad con la que estos archivos serán servidos, puesto que se usa la infraestructura del gigante de Redmon, a la vez que se comparte la caché con otros sitios web que también las estén utilizando. También brinda la posibilidad de utilizar scripting a sitios web que no disponen de permisos para subir archivos (como la plataforma blogger)

A diferencia del servicio de Google, desde esta CDN sólo podemos encontrar de momento las librerías que oficialmente forman parte de la plataforma de desarrollo de Microsoft, como las propias de ASP.NET Ajax, jQuery y aquellos plugins que vayan añadiéndose. En la dirección http://www.asp.net/ajax/cdn/ pueden consultarse la lista completa de librerías, con sus correspondientes direcciones de descarga.EnableCdn en ScriptManager

Adicionalmente, Scott Guthrie comentaba en su blog que el nuevo control ScriptManager que vendrá con ASP.NET 4.0 incluye una propiedad llamada EnableCdn que permitirá activar la descarga de las librerías Ajax y todas aquellas necesarias para el funcionamiento de controles, directamente desde sus servidores.

Los inconvenientes, pues los mismos que los del servicio de Google: si no disponemos de conexión a la red en tiempo de desarrollo, podemos tenerlo realmente crudo.

Más información en: www.asp.net/ajax/cdn

Publicado en: Variable not found.

martes, 15 de septiembre de 2009

No es algo que ocurra muy frecuentemente, pero en determinadas ocasiones puede ser útil inicializar una propiedad de un tipo anónimo con el valor nulo, por ejemplo:

var conductor = new { Nombre = ”Marisa”, Edad = 34, Auto = “Renault Megane” };
var peaton = new { Nombre = “Juan”, Edad = 43, Auto = null };

En el código anterior se entiende que lo que queremos indicar estableciendo la propiedad Auto a null es que la persona que estamos representando no dispone de automóvil, o bien no conocemos este dato.

Algo muy normal… sino fuera porque el compilador se empeña en abofetearnos con el siguiente error:

Cannot assign <null> to anonymous type property

Y la verdad, en cuanto lo pensamos un poco, tiene sentido. En los casos anteriores, se está instanciando un tipo anónimo cuyas propiedades son generadas en tiempo de compilación. En el caso anterior, el compilador crearía para el tipo anónimo una propiedad “Nombre” de tipo string, una “Edad” de tipo integer, y una “Auto”, de tipo… ¿qué tipo podría asignar el compilador a esta propiedad? Ese es precisamente el problema.

Lo mismo ocurre al intentar inicializar variables implícitamente tipadas con el valor nulo; aunque cambia el error, el concepto idéntico, el compilador no es capaz de inferir el tipo de la variable:

var persona = null; // Error: Cannot assign <null> to an implicitly-typed local variable

La forma de solucionarlo en ambos casos es muy sencilla: basta con informar explícitamente al compilador del tipo de la propiedad, para que pueda crearla sin problema, por ejemplo así:

var peaton = new { Nombre = "Juan", Edad = 43, Auto = (string)null };
var persona = null as Persona;

Publicado en: Variable not found

domingo, 13 de septiembre de 2009

Juan Palacio, en su genial Navegápolis, publicó semanas atrás unos buenos materiales de un Curso de Scrum en los que he podido recordar una historieta que encontré hace tiempo sobre la diferencia entre la implicación y el compromiso en la realización de un proyecto:

La historia de la gallina y el cerdo

Aunque esta metáfora se utiliza en Scrum para agrupar los roles principales distinguiendo entre aquellos que ponen “la carne en el asador” (cerdos), y el resto de afectados y participantes (gallinas), es igualmente aplicable a las actitudes de los componentes de un equipo de trabajo, independientemente de la metodología utilizada.

Publicado en: Variable not found.

miércoles, 9 de septiembre de 2009

Logo de NHibernate Hace unas semanas, Oren Eini (o Ayende Raihen, como se le suele conocer) comunicaba la liberación de la versión 1.0 del proveedor de Linq para NHibernate, una característica altamente demandada por los usuarios desde la aparición del lenguaje de consulta integrado en .NET.

Aunque será incluido como parte del producto NHibernate en versiones venideras, han decidido liberar la actual release del proveedor como paquete independiente para que pueda comenzar a utilizarse desde este momento. Está siendo testado en multitud de aplicaciones en producción desde hace varios años, y al parecer el funcionamiento es más que correcto.

¿Y cómo puede ayudarte este proveedor, si eres usuario de NHibernate? El siguiente ejemplo, tomado de Caffeinated Coder muestra cómo una consulta a base de datos puede simplificarse y hacerse mucho más legible utilizando Linq, además de beneficiarse del tipado fuerte, intellisense y comprobaciones en tiempo de compilación:

Utilizando el API de NHibernate:

public IList<Call> GetCallsByDate(DateTime beginDate, int interpreterId)   
{   
    ICriteria criteria = Session.CreateCriteria(typeof(Call))
        .CreateAlias("Customer", "Customer")
        .Add(Restrictions.Gt("StartTime", beginDate))
        .Add(
            Restrictions.Or(
                Restrictions.Lt("EndTime", DateTime.Now), Restrictions.IsNull("EndTime"))
            )
        .Add(Restrictions.Eq("Interpreter.Id", interpreterId))
        .AddOrder(Order.Desc("StartTime"))
        .AddOrder(Order.Desc("Customer.Name")); 
        return criteria.List<Call>() as List<Call>;
}

Utilizando Linq:

public IList<Call> GetCallsByDateWithLinq(DateTime beginDate, int interpreterId)  
{   
    var query = from call in Session.Linq<Call>()
        where call.StartTime > beginDate
            && (call.EndTime == null || call.EndTime < DateTime.Now )   
            && call.Interpreter.Id == interpreterId
        orderby call.StartTime descending, call.Customer.Name
        select call;
 
    return query.ToList();
}

Podéis descargar tanto los binarios como el código fuente desde la página del proyecto en SourceForge.

Publicado en: Variable not found.