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!
Mostrando entradas con la etiqueta .net. Mostrar todas las entradas
Mostrando entradas con la etiqueta .net. Mostrar todas las entradas
miércoles, 21 de abril de 2010
¿Sómos los primeros en llegar al estadio? Casualmente encuentro en el post de Chris Eargle “Any() versus Count()” un tema del que pensaba escribir hace tiempo y al final dejé en el tintero: ¿cómo podemos determinar si una enumeración está vacía? Vale, es bien fácil, una enumeración está vacía si tiene cero elementos.

Si trabajamos con un array, podemos consultar la propiedad Length; si se trata de una colección, podemos utilizar la propiedad Count, que también nos devolverá el mismo dato de forma directa.

Sin embargo, cuando procesamos información es frecuente tratar con tipos de datos enumerables cuya implementación exacta desconocemos, y en los que no tenemos acceso a ninguna propiedad que nos permita conocer el número de elementos exactos que tiene almacenados. Por ejemplo, esto ocurre cuando obtenemos un conjunto de datos en forma de IEnumerable o trabajamos con LINQ sobre alguna fuente de información.

Pues bien, en estos casos hay que ser algo prudentes con la forma de consultar el número de elementos. Me he topado con código propio en el que, simplemente por despiste, utilizaba el método Count(), facilitado por LINQ para las enumeraciones y otras colecciones de datos, con objeto de saber si una lista estaba vacía:

var prods = services.GetProductsByCategory(category);
if (prods.Count() > 0)
{
   // Hacer algo con los elementos de la lista
}
else
{
   // No hay elementos
}

Seguro que ya os habréis percatado de que eso está mal, muy mal. El método Count() recorrerá uno por uno los elementos para contarlos, retornando el número exacto de ellos. En escenarios de un gran número de elementos, o cuando es costoso obtener cada elemento (como al traerlos de una base de datos) puede suponer un consumo de recursos enorme.

Y justo por esto existe el método Any(), que comprueba únicamente si existe al menos un elemento en la colección. Internamente este método itera sobre la colección retornando true cuando encuentra el primer elemento, por lo que es mucho más eficiente que el anterior:

var prods = services.GetProductsByCategory(category);
if (prods.Any()) // Mucho mejor :-)
{
  // Hacer algo con los elementos de la lista
}
else
{
  // No hay elementos
}

La utilización de Any() también es interesante para comprobar la existencia de elementos que cumplan una condición, expresada en forma de predicado; retornará true cuando encuentre el primer elemento que lo haga cierto:

if (pedidos.Any(p => p.Pendiente))
{
   // Mostrar alerta, hay al menos un pedido pendiente
}

Estamos en la puerta de un Estadio y queremos saber si vamos a ser los primeros en entrar al recinto… ¿le preguntaríamos al portero el número exacto de aficionados que ya están dentro para, si es cero, saber que somos los primeros? ¿O nos bastaría simplemente con preguntarle si hay alguien? ;-P

Publicado en: Variable not found
Hey: ¡estoy en Twitter!
domingo, 4 de abril de 2010
Hace casi un año hablaba de la segunda versión de NDepend, una herramienta capaz de ayudaros a mejorar nuestro código, analizando cientos de aspectos, métricas y reglas a nivel de fuentes y ensamblados.

Recientemente se ha publicado la tercera versión de NDepend, que ofrece interesantes novedades respecto a las anteriores, como la integración absoluta con Visual Studio, el soporte para soluciones multi-proyecto, potentes mecanismos de búsqueda, edición múltiple de CQL, o el seguimiento de cambios, además de las tradicionales características del producto.

Los 10 métodos más extensos de ASP.NET MVC 2
Pero sin duda, lo que me ha parecido más espectacular es su magnífica integración con Visual Studio (2005, 2008 y 2010), que permite disfrutar de toda la potencia de análisis de NDepend, y sus facilidades para la navegación desde el mismo entorno de desarrollo.

Para habilitar esta característica es necesario instalar el plugin en el IDE, que se realiza desde el propio entorno visual de NDepend:

Instalar plugin NDepend en Visual Studio
De esta forma, ya no es necesario acudir a la herramienta Visual NDepend para realizar búsquedas, comprobar reglas o navegar a través de la base de código: lo haremos directamente desde VS, utilizando los menús contextuales. Y gracias a ello, podemos disfrutar de las nuevas opciones de navegación, que nos permitirá surcar el código utilizando rutas distintas a las habituales:

Navegar por el código con NDepend
U obtener diagramas de dependencias de componentes, utilizando el botón derecho del ratón:

Dependencias con NDepend 3

Además, como comenta Patrick Smacchia, padre de la criatura, el rendimiento del entorno prácticamente no se resiente, dado que los análisis se ejecutan en segundo plano de forma incremental.

Recordar, por último, que NDepend es una aplicación comercial, pero dispone de una versión limitada gratuita utilizable por universidades, desarrolladores open source e incluso, durante un tiempo determinado, de prueba en proyectos comerciales.

Página del producto: http://www.ndepend.com/
Publicado en: Variable not found
Hey, ¡estoy en twitter!
miércoles, 16 de diciembre de 2009

Validación de formularios ASP.NET desde javascript Cada vez que tengo que forzar la validación de los datos de un formulario Webforms mediante javascript me veo obligado a preguntarle a Google, ese que todo lo sabe, cómo era el nombre de la función. Cosas de la edad, supongo ;-)

Así que, a modo de auto-recordatorio y con la intención de que pueda ser útil a alguien más, ahí va: la función se llama Page_ClientValidate(). Retorna “true” si, una vez evaluados todos los validadores de la página, el valor de los campos es correcto (al menos en cliente; otra cosa son las comprobaciones en servidor, p.e., las definidas en un CustomValidator).

Y como ejemplo de uso, puede valer el siguiente. Se trata de un botón de envío en un formulario donde se compone un correo electrónico:

   1: ...
   2: <asp:Button ID="btnEnviar" runat="server" Text="Enviar mail" 
   3:      OnClick="btnEnviar_Click"
   4:      OnClientClick="return confirmar();"
   5: />
   6: ...
   7:  
   8: <script type="text/javascript">
   9:     function confirmar() {
  10:         if (!Page_ClientValidate())   // Fuerza la validación en cliente
  11:             return false;             
  12:             
  13:         return confirm('¿Seguro que desea realizar el envío?');
  14:     }
  15: </script>

Como se puede observar, en el atributo OnClientClick del botón incluye un script en el que se retorna el valor devuelto por la función confirmar. Si el retorno es false, se cancela el Postback, evitando así que se invoque al evento btnEnviar_Click que es el que realiza el envío del mail propiamente dicho.

En el cuerpo de la función confirmar(), en primer lugar, validamos la página; si ésta no supera el proceso, los validadores habrán mostrado sus mensajes de error y retornamos falso, haciendo que se anule el postback. Si la validación es correcta, solicitamos al usuario que confirme la acción y retornamos su decisión.

Publicado en: Variable not found.

miércoles, 4 de noviembre de 2009

Cadenas Encuentro en el blog de Gunnar Peipman un post sobre el nuevo método string.IsNullOrWhiteSpace, aparecido en .NET Framework 4.0 Beta 2, cuya misión es retornar un booleano indicando si la cadena pasada como parámetro contiene un nulo, está vacío, o exclusivamente caracteres de espaciado (espacios, saltos de línea, tabulaciones, etc.), un escenario bastante frecuente cuando estamos, por ejemplo, validando formularios o cualquier tipo de datos de entrada.

Por si no podemos esperar hasta la llegada de la nueva versión del framework, Gunnar propone una solución temporal basada en crear un método de extensión sobre la clase string que realice la misma tarea:

 
public static class StringHelper
{
    public static bool IsNullOrWhiteSpace(this string s)
    {
        if (s == null)
            return true;
 
        return (s.Trim() == string.Empty);
    }
}
 
Como ya comenté por aquí hace tiempo, la ventaja que tiene utilizar esta técnica es que permite invocar el método sobre una instancia de cadena, aunque ésta sea nula, sin riesgo a errores:
 
string a = null;
string b = "    ";
string c = "\n";
string d = "Hey!";
Console.Write (a.IsNullOrWhiteSpace()); // True
Console.Write (b.IsNullOrWhiteSpace()); // True
Console.Write (c.IsNullOrWhiteSpace()); // True
Console.Write (d.IsNullOrWhiteSpace()); // False

Y para los fieles a Visual Basic .NET, ahí va el código equivalente:

Imports System.Runtime.CompilerServices
Public Module StringHelper
    <Extension()> _
    Public Function IsNullOrWhiteSpace(ByVal s As String) As Boolean
        If s Is Nothing Then
            Return True
        End If
        Return s.Trim() = String.Empty
    End Function
End Module

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.

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

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.