.NET

Automapper y un buen articulo de diseño de software

Logo AutoMapper

La semana pasada descubrí AutoMapper, que es un proyecto de .NET gratis y open-source de Jimmy Bogard.

Un desarrollador de la oficina me preguntó si había una manera integrada en el framework de transformar las clases generadas por LINQ-To-SQL en clases sencillas (DTOs). No creí que lo hubiera…pero le comenté que seguramente lo podía hacer por Reflection. Yo ya habia hecho eso antes, pero supuse que debía haber alguna mejor manera. Después de buscar un poco en StackOverflow.com, alguna de las respuestas linkeaba a AutoMapper y descubrí que era exactamente lo que aquel desarrollador estaba buscando.

AutoMapper es descrito en su página principal de la siguiente manera:

AutoMapper uses a fluent configuration API to define an object-object mapping strategy. AutoMapper uses a convention-based matching algorithm to match up source to destination values. Currently, AutoMapper is geared towards model projection scenarios to flatten complex object models to DTOs and other simple objects, whose design is better suited for serialization, communication, messaging, or simply an anti-corruption layer between the domain and application layer.

Excelente…no solo utiliza interfaces fluidas, sino que también se basa en Convention Over Configuration. Es decir…solo tenemos que (y podemos!) configurar las situaciones excepcionales. Las ventajas que le veo a utilizar AutoMapper (a pesar de estar incorporando una referencia más a nuestros proyectos) es que es un proyecto público (de open source) y que además tiene a un muy buen desarrollador respaldándolo, que por ser su proyecto, se enfoca en mejorarlo y perfeccionarlo con nuevas funcionalidades, haciéndolo más eficiente, etc. Además, debido a que ya tiene una considerable base de usuarios (3,702 downloads), la mayoría de los bugs que pudiéramos toparnos, ya han sido resueltos.

Un ejemplo exagerádamente básico del uso de AutoMapper:


public class Class1
 {
   public string FullName { get; set; }
   public int Age { get; set; }
   public char Gender { get; set; }
   public void DoIt()
   {
     Console.WriteLine("Test");
   }

   public List<string> Addresses { get; set; }

   public Class1()
   {
     Addresses = new List<string>();
   }
 }

 public class Class2
 {
   public string FullName { get; set; }
   public int Age { get; set; }
   public char Gender { get; set; }
 }

private void Form1_Load(object sender, EventArgs e)
{
   Mapper.CreateMap<Class1, Class2>();

   var c2 = Mapper.Map<Class1, Class2>(c1);
   MessageBox.Show(c2.FullName);
}

Ok, de este ejemplo podemos ver:

  • Class1 es poco más compleja que Class2, ya que tiene una List<string> y un método definido (DoIt())
  • Class2 es una versión simple de Class1, con el objetivo de funcionar como un Data Transfer Object.

Como pueden ver, con la instrucción:

Mapper.CreateMap<Class1, Class2>();

estamos indicándole a AutoMapper que la Class1 y Class2 están relacionadas (mappeadas). Y la siguiente línea de código:

var c2 = Mapper.Map<Class1, Class2>(c1);</pre>

está creando una instancia de tipo Class2, basada en la instancia de c1 que ya teníamos, inicializada y asignando dinámicamente los valores de dicha instancia (c1) a la nueva instancia c2.

Class1, en este caso, sería un ejemplo de una clase de LINQ-To-SQL, mientras que Class2 sería la clase que queremos usar como DTO.

Esta funcionalidad es sólamente la más básica de AutoMapper (flattening). También soporta proyecciones (utilizar ciertas propiedades o sub-sets de un valor para mapear a otra propiedad), Listas y Arrays, etc. Para más información, visisten la documentación de AutoMapper.

Y sobre el buen artículo de diseño…para mi sorpresa lo encontré 2 dias después de haber descubierto AutoMapper, y el artículo lo escribió Jimmy Bogard.

El artículo se llama originalmente “Strengthening your domain: Encapsulated collections“. Jimmy habla de como debemos de diseñar nuestras clases para no exponer inadvertidamente funcionalidad de más (extra, innecesaria) que podría permitir operaciones no lógicas sobre nuestras entidades, y enfocarnos en soportar exclusiva y claramente sólo las operaciones que el dominio de negocio debe permitir sobre las entidades de nuestro proyecto. El artículo también me hizo pensar que los accesors son algo que mucha gente no considera a la hora de exponer sus propiedades. Léanlo y si gustan dejar sus comentarios…adelante.

Como formatear una cantidad a currency en .NET controlando el numero de decimales

.NET

Tantas opciones que hay para formatear strings en.NET, que es difícil memorizar todas.

Hoy tuve que formatear una cifra numérica que en la base de datos está como decimal, pero en el UI no quería desplegar ninguno de los decimales. Casi todo el mundo ha utilizado el string.Format(“{0:C”), el cual formatearía como si la cantidad fuera una cifra monetaria, pero eso implica tener decimales.

Algua persona podría pensar en realizar esto desde el stored procedure o el query que trae la información, pero personalmente creo que eso es un error, ya que los datos deben estar siempre  intactos y lo unico que debemos modificar es el formato en el que los queremos desplegar. Además, eso implicaría tener que estar controlando también el formato del símbolo separador de miles. Esto es algo que simplemente no se debe hacer en base de datos.

Googleando, encontré en los forums de ASP.NET la siguiente manera de hacerlo, pero tampoco es la más correcta:


System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo();
nfi.CurrencyDecimalDigits = 0;
nfi.CurrencySymbol ="$";
myLabel.Text =string.Format(nfi,"{0:C}", ad.Price);

Ya que estoy utilizando esto en un control Repeater, tendría que crear un método con las primeras 3 líneas de texto anteriores, y eso implicaría estar generando varias instancias de NumberFormatInfo.

Para ya no hacer más largo este post, les dejo la solución correcta.


string.Format("0:C0", ad.Price);

Así de sencillo…y no lo especifican en ningun lugar de MSDN…lo vine a encontrar aqui: .NET Format String 101. Obvio decirlo, basta cambiar el C0 por C1, C2, C3 o Cx para determinar el número de decimales que queremos desplegar. Esto también hace mucho más facil poner ese format string en el archivo de configuración y poder reutilizarlo en varias pantallas sin tener que estar cambiando código.

Les dejo también otra buena referencia de string.Format: String Formatting in C#

Aplicar formatos condicionales a numeros con string.Format

.NET

Esto es algo que descubrí hoy referente a formateo de números en .NET y que por más simple que parezca, tiene mucha utilidad, ya que puede reducir un bloque de 3 o 6 líneas de código, a una sola línea.

En lugar de necesitar varios if’s para evaluar el valor del número que vamos a formatear y poder determinar el formato que se va a aplicar (desplegar el número entre paréntesis si es negativo, desplegar la palabra Cero si el número es 0, etc), se puede hacer todo el una sola línea, ya que el metodo ToString() permite formatos condicionales de 2 o 3 condiciones…es decir, podemos hacer esto:


const string formato = "000;(##);Cero";

 Console.WriteLine(7.ToString(formato)); // va a desplegar "007"
 Console.WriteLine((-5).ToString(formato)); // va a desplegar "(5)"
 Console.WriteLine(0.ToString(formato)); // va a desplegar "Cero"

Formatos condicionales en .NET usando ToString()

Formatos condicionales en .NET usando ToString()

El formato entonces es:  “positivo;negativo;cero”.

También es posible especificar solo el formato positivo y negativo.

Espero les sirva.

Usar el operator ternario de C# con nullables

.NET

.NET

Todos los días se aprende algo…

Desde hace mucho tenemos disponible el operador ternario (o condicional) en C#, también conocido como el operador “?“.

El operador ternario sirve para regresar/usar uno de dos posibles valores, dependiendo de una condición boleana.

La estructura es:

condicion-boleana ? valor-si-se-cumple : valor-si-no-se-cumple

Es decir, permite reducir de esto:


int? myVar; //variable int nullable

if(string.IsNullOrEmpty(textbox1.Text))

{ myVar = null; }

else

{ myVar = Convert.ToInt32(textbox1.Text); }

a esto:


myVar =string.IsNullOrEmpty(textbox1.Text) ? null : Convert.ToInt32(textbox1.Text);

Y uno esperaría que el código del segundo bloque compile, pero no es así. En lugar de que funcione, el compilador nos marca el siguiente error:

Type of conditional expression cannot be determined because there is no implicit conversion between ‘<null>’ and ‘int’

Yo llevaba años usando el operador ternario y siempre me pareció ilógico que no se permitiera esto. Y probablemente fue más ilógico el hecho de nunca haber googleado como solucionar esto..ya que la solución es simple.


myVar =string.IsNullOrEmpty(textbox1.Text) ? (int?)null : Convert.ToInt32(textbox1.Text);

o


myVar =string.IsNullOrEmpty(textbox1.Text) ? null : (int?)Convert.ToInt32(textbox1.Text);

Simple…el compilador tenía razon. No hay una conversión implícita entre null e int, pero sí entre null y Nullable<int> (a.k.a. int?). Casteando cualquiera de los lados de la expresión, va a compilar sin problemas.

Espero les sirva.

1 2 3 4 5 8  Scroll to top