lunes, 30 de junio de 2014

[Patrones] Implementando Patrón adaptador - Adapter Pattern en C#

El patrón adaptador nos permite convertir o transformar una interface existente en otra interface esperada por un cliente, para realizar determinada operación, es decir en muchas ocasiones necesitamos hacer alguna operación desde nuestras aplicaciones (cliente), y en muchos casos ya tenemos esta operación implementada en una librería de clases por ejemplo. Pero también nos podemos encontrar que la interface que usa nuestro cliente no sea compatible con la interface que usa este componente genérico que tenemos. Es allí donde cobra importancia el patrón adaptador o adapter pattern, el cual nos permite a través de un componente intermedio entre las dos interfaces lograr que sean compatibles y lograr reutilizar nuestro código sin necesidad de repetirlo o acoplarlo fuertemente a alguna implementación en específico.

Veamos para comprender mejor en primera instancia el ejemplo más usado de este patrón con una imagen de la vida real:


Ahora veamos cómo sería el diagrama de clases de este patrón y analicémoslo un poco antes de implementarlo en C#:

Imagen tomada de Wikipedia (http://es.wikipedia.org/wiki/Adapter_(patr%C3%B3n_de_dise%C3%B1o))

Sin lugar a duda comprender en primera instancia un patrón de diseño suele ser algo complejo, por eso vamos a describir los diferentes componentes que vemos en el diagrama.

Target: Es una interface, la cual usa el cliente en el cual se quiere reutilizar el componente genérico que tenemos (Adaptee), es allí donde radica el problema que soluciona el patrón adaptador, ya que Target es diferente de la interface usada por el componente genérico que tenemos (Adaptee).

Adaptee: Es el componente genérico que tenemos, en el cual tenemos implementada cierta funcionalidad que queremos reutilizar desde nuestro cliente, al ser un componente genérico y pensado en cierta ocasión para algún escenario su interface es incompatible con Target, es decir la firma de su método puede retornar un valor diferente y recibir parámetros diferentes, es por esto que son incompatibles.

Adapter: Es nuestro adaptador o también conocido como Wrapper, el cual nos permite convertir en compatibles estas dos interfaces que son incompatibles por naturaleza, y como vemos en el diagrama, Adapter es una clase que hereda de Adaptee y que implementa la interface Target, de este modo puede cumplir con el contrato esperado por el cliente y a su vez utilizar la implementación existente en Adaptee.

Y tenemos un componente más que no se encuentra en el diagrama que es el Cliente, y es la aplicación u otro componente, desde la cual deseamos reutilizar la funcionalidad que tenemos en nuestro Adaptee, es por esto que anteriormente nos referimos mucho al cliente.

Ahora que conocemos bien que hace cada componente, comprendemos que nuestro Adapter invocará nuestro Adaptee y operará de modo que cumpla con el contrato esperado por nuestro cliente, es decir Target.

Probablemente lo primero que se nos venga a la cabeza es: ¿Y por qué no crear otro componente basado en el existente copiando el código que ya tengo? ó ¿por qué no envío un parámetro adicional a mi Adaptee de modo que pueda indicar cómo comportarse dependiendo del cliente que lo invoca? ó ¿Por qué no creo una sobrecarga de mi método en el Adaptee que si cumpla con el contrato esperado por mi nuevo cliente?

Pues bueno, la verdad es posible resolver el problema planteado a través de las alternativas anteriores, pero si nos detenemos a pensar, no estaríamos reutilizando componentes si no copiando y pegando, es decir replicando código que ya tenemos, también estaríamos acoplando fuertemente nuestro componente "genérico" a implementaciones concretas, o estaríamos modificando el comportamiento de nuestro componente creado inicialmente, lo cual iría en contra del principio solid "Open / Close" que nos indica que nuestro componente debería estar abierto a ser extendido y debería estar cerrado a ser modificado.

Es por esto que el patrón adaptador nos permite una solución, que sea desacoplada y que permita hacer extensible nuestro componente, sin tener que modificarlo directamente, y nos permite hacer múltiples adaptaciones de un componente para diferentes requerimientos de nuevos clientes.

Y bueno después de comprender el propósito y aplicación de este patrón, que en mi opinión es lo más complejo e importante, ahora vamos a ver como lo podemos implementar a través del lenguaje C#, aplicándolo en el siguiente escenario:

En nuestro ejemplo vamos a hacer una adaptación de un componente que nos permite guardar un error en archivo txt este es nuestro Adaptee, el cual en un principio solo fue pensado para esto. Pero lo deseamos reutilizar en otra aplicación que requiere otro comportamiento diferente.

    public class HelperLog
    {
        public void GuardarError(Exception ex)
        {
            using (var w = File.AppendText("log.txt"))
            {
                w.Write("\r\nLog Entry : ");
                w.WriteLine("{0} {1}"DateTime.Now.ToLongTimeString(), DateTime.Now.ToLongDateString());
                w.WriteLine("  :");
                w.WriteLine("  :{0}", ex.Message);
                w.WriteLine("-------------------------------");
                w.Write("\r\nError StackTrace : {0}", ex.StackTrace);
            }
        }
    }

Como vemos, en esta firma recibimos una excepción, la almacenamos en el archivo de log y no retornamos ningún valor, pero para nuestra nueva aplicación se requiere que se envíe solo el mensaje de error y un código de error personalizado, con el cual se obtendrá un mensaje amigable para el usuario de un archivo Xml y por último se le mostrará al mismo. Por lo tanto requerimos de un adaptador que nos ayude a reutilizar este componente volviendo compatibles las interfaces. Ahora veamos la interface que se requiere.

    public interface IClienteLog
    {
        string GuardarErrorLog(string error, string codigoError);
    }

Como vemos tenemos incompatibilidad, y es allí donde entra a jugar nuestro adaptador, el cual debe heredar de nuestra clase HelperLog e implementar nuestra interface IClienteLog.

    public class LogAdapter : HelperLogIClienteLog
    {
 
        public string GuardarErrorLog(string error, string codigoError)
        {
            GuardarError(new Exception(error));
 
            // Se obtiene el mensaje amigable de error basado en su código
            var root = XElement.Load("Mensajes.xml");
 
            return (from c in root.Elements("Mensaje")
                   where (string)c.Attribute("codigo"== codigoError
                   select c.Value).First();
        }
    }

Como vemos reutilizamos nuestro helper de log, y a la vez cumplimos con el contrato que requiere nuestro nuevo cliente, observemos como lo usamos desde una aplicación de consola por ejemplo:

    class Program
    {
        static void Main(string[] args)
        {
            IClienteLog clienteLog = new LogAdapter();
 
            try
            {
                Console.Write("Digite el número a convertir...");
                var cadena = Console.ReadLine();
                var numero = Convert.ToInt16(cadena);
            }
            catch (Exception ex)
            {
                Console.Write(clienteLog.GuardarErrorLog(ex.Message, "2"));
                Console.ReadLine();
            }
        }
    }

En este escenario utilizamos el la adaptación del helper log, para almacenar un error que corresponde a un error de conversión de tipos, el parámetro "2" corresponde al mensaje de este error en el xml, como podrás ver en la solución de código fuente que indico al final.

Bueno amigos eso es todo de esta muestra acerca del patrón de diseño adaptador o Adapter pattern, espero sea de utilidad y de interés para ustedes.

Este ejemplo lo puedes descargar de mi repositorio en GitHub



Saludos, y buena suerte!

martes, 17 de junio de 2014

¿Qué hay de los patrones de diseño?

En muchas ocasiones hemos escuchado hablar acerca de los famosos patrones de diseño, escuchamos que nos facilitan la vida al desarrollar o al plantear una arquitectura de software. Escuchamos que son soluciones probadas y certificadas a problemas comunes y conocidos en el desarrollo de software, y la verdad aunque escuchamos mucho acerca de ellos y los utilizamos a diario (Aunque a veces no nos demos cuenta) en ocasiones pasamos por alto la importancia que tienen. En mi opinión los patrones de diseño hacen parte de las buenas prácticas que garantizan resolver un problema de una manera óptima y además de eso de una manera que ya se sabe que funciona correctamente. Ahora no quiere decir que en todos los casos de nuestros proyectos apliquen todos los patrones de diseño, o que siempre un patrón de diseño será infalible en todos los casos, pues no, recordemos que en el desarrollo de software no existen balas de plata como dicen por ahí, y que no todo recurso es efectivo para atacar o resolver todos los problemas, es allí donde es importante tener conocimiento de gran parte de los recursos con los que contamos, en este caso los patrones de diseño, para echar mano de alguno en específico en un problema determinado.

Adicional cabe destacar que como en todo, no hay que abusar de ellos, pues es bueno analizar si en un determinado caso agregamos solo complejidad innecesaria a nuestras implementaciones, dando lugar a anti patrones con los cuales lo único que lograremos es tener un gran y complejo proyecto el cual se dificulte de comprender y mantener por otros desarrolladores, que en una etapa de mantenimiento que suele ser la más costosa del ciclo de desarrollo, no queremos.

Ahora después de esta introducción tan personal, entremos un poco en materia y veamos algunos aspectos básicos e importantes de los patrones de diseño:

"Los patrones de diseño son la base para la búsqueda de soluciones a problemas comunes en el desarrollo de software y otros ámbitos referentes al diseño de interacción o interfaces.

Un patrón de diseño resulta ser una solución a un problema de diseño. Para que una solución sea considerada un patrón debe poseer ciertas características. Una de ellas es que debe haber comprobado su efectividad resolviendo problemas similares en ocasiones anteriores. Otra es que debe ser reutilizable, lo que significa que es aplicable a diferentes problemas de diseño en distintas circunstancias."


Esta, la definición de Wikipedia y el link por si quieren profundizar un poco más en el tema. Y como vemos la importancia de que un patrón compruebe que efectivamente soluciona un problema y que es reusable para múltiples escenarios que se puedan parecer. Como es el caso del patrón Iterador por ejemplo, cuyo propósito es permitir acceder secuencialmente a un grupo de objetos de una colección, y que es un patrón que usamos a diario en nuestras labores, y como mencioné anteriormente muchas veces no nos percatamos de ello, y sin contar un sin fin de patrones que son utilizados por los FrameWorks que usamos y que nos facilitan la vida.

Para terminar con este breve artículo sobre patrones de diseño, vamos a ver algunos de los patrones de diseño más conocidos y por ende más usados, y cómo estos se clasifican dependiendo de su propósito:

Patrones Creacionales:
  • Abstract Factory
  • Builder
  • Factory Method
  • Prototype
  • Singleton
Patrones estructurales:

  • Adapter
  • Bridge
  • Composite
  • Decorator
  • Proxy

Patrones de comportamiento:
  • Chain of Responsibility
  • Command
  • Iterator
  • Observer
  • Strategy
Bueno amigos eso es todo de esta introducción y opinión personal sobre los patrones de diseño, espero sea de utilidad y de interés para ustedes, en próximos artículos iniciaremos una serie de post sobre este tema, en los cuales hablaremos acerca de algunos patrones (Propósitos, ventajas, desventajas, escenarios de aplicabilidad, etc) y veremos cómo implementarlos a través del lenguaje C#.

Saludos, y buena suerte!

viernes, 13 de junio de 2014

Html5 JavaScript Apis

Hola amigos, hace algunos días, para ser más exacto el pasado 31 de mayo de 2014, estuve dictando una charla en el Html Day Medellín, en donde tuve la oportunidad de compartir acerca de las Apis JavaScript que nos ofrece Html5. Y cómo suelo hacer después de cada charla comparto la presentación y la demo que realizo. Y bueno esta no será la excepción, es por esto que voy a dar una breve introducción y vamos a implementar algunos ejemplos con algunas de las apis de JavaScript que tenemos con Html5. Para empezar en un artículo anterior les compartí la presentación de mi charla, y ahora vamos a ver las demos de las Apis que se detallan en ella.

Introducción

Son una serie de funcionalidades JavaScript que nos permiten realizar diferentes operaciones de forma nativa, sin necesidad de referenciar en nuestras aplicaciones librerías externas, como por ejemplo lograr Drag & Drop en nuestro controles Html, sin necesidad de referenciar un plugin de Jquery u otro Framework para lograrlo.

Ahora observemos en detalle algunas de estas Apis:

Geolocation:

Este Api nos permite obtener la ubicación geográfica actual de nuestro usuario, en coordenadas de latitud y longitud, ya que esta acción tiene incidencia en la privacidad del usuario, se requiere de su autorización para lograr obtener esta información.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Geolocation Html5 Api</title>
    <script>
    function ObtenerUbicacion() {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(MostrarCoordenadas);
        } else {
            document.getElementById("divCoordenadas").innerHTML = "Actualiza tu browser, no soporta geolocalización.";
        }
    }
 
    function MostrarCoordenadas(posicion) {
        document.getElementById("divCoordenadas").innerHTML = "Latitud: " + posicion.coords.latitude + "\t Longitud: " + posicion.coords.longitude;
    }
    </script>
</head>
<body>
    <div id="divCoordenadas"></div>
    <script>
        ObtenerUbicacion();
    </script>
</body>
</html>

Como vemos en el Demo anterior es bastante sencillo usar este Api de Geolocalización, en cual obtenemos la ubicación del usuario y la mostramos en un Div, como podemos ver en el método ObtenerUbicaicon usamos el Api navigator.geolocation, para validar en primera instancia si el navegador del usuario soporta esta característica (Esta validación se las recomiendo siempre para cualquier Api de html5), y como vemos a través del método getCurrentPosition especificamos el método o función en el cual vamos a recibir las coordenadas en su único parámetro, en nuestro caso es el método MostrarCoordenadas y para finalizar obtenemos el parámetro posición del cual accedemos a sus propiedades coords.Longitude o latitude respectivamente, para al final mostrar las coordenadas.

Drag & Drop

Este Api nos permite habilitar determinados controles para que puedan ser arrastrados y ubicados en otro lugar, en Html5 cualquier elemento puede ser dragable.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Drag & Drop Demo</title>
    <script>
        function drag(event) {
            event.dataTransfer.setData("Text", event.target.id);
        }
 
        function PermitirDrop(event) {
            event.preventDefault();
        }
 
        function drop(event) {
            event.preventDefault();
            var idObjeto = event.dataTransfer.getData("Text");
            event.target.appendChild(document.getElementById(idObjeto));
        }
 
    </script>
</head>
    <body>
        <div id="divIzquierda" ondragover="PermitirDrop(event)" ondrop="drop(event)" style="background-colorbluewidth50pxheight50px;"></div>
        <div id="divCentro" draggable="true" ondragstart="drag(event)" style="background-colorredwidth30pxheight30px;"></div>
        <div id="divDerecha" ondragover="PermitirDrop(event)" ondrop="drop(event)" style="background-colordarkkhakiwidth50pxheight50px;"></div>
    </body>
</html>

En la demo anterior vemos tres div, uno de color azul, otro de color dorado y otro de color rojo, en este demo se logra que el Div de color rojo pueda ser arrastrado y soltado en alguno de los otros dos Div. Observemos que de igual forma su implementación es bastante sencilla, y debemos manejar varios aspectos clave:
  • draggable = "true": como vemos el Div de color rojo, tiene este atributo en true, lo cual lo habilita para que el usuario lo pueda arrastrar
  • OnDragStart Event: es esencial manejar este evento, ya que se ejecuta cuando el usuario hace clic sobre el control y lo empieza a arrastrar. Si observamos la demo, el Div rojo es quien maneja este evento, ya que es el único dragable, y si vemos la implementación del evento simplemente establecemos en el DataTransfer un dato de tipo texto(Text) en el cual almacenamos el id del Div rojo en este caso. Esto para saber en los siguientes eventos cual fue el control que arrastró el usuario.
  • OnDragOver Event: Este evento se maneja para implementar alguna funcionalidad justo cuando el usuario arrastra el control y esta sobre el control destino, pero aún no lo ha soltado.
  • OnDrop Event: Este evento se ejecuta cuando el usuario suelta el control que arrastró sobre el control destino, y es aquí donde debemos recuperar el id del control almacenado en el DataTransfer para agregarlo en el control destino, tal y como se ve en la función drop de la demo anterior.
Web Storage

Este Api nos permite almacenar información en el browser del usuario, mucho más rápido que las cookies. Permite almacenar grandes cantidades de información sin afectar el performance del sitio web. Su almacenamiento se hace en Key / Value pairs.

Existen dos tipos de almacenamiento:
  • localStorage: Su durabilidad no tiene fecha de expiración.
  • SessionStorage: La información se pierde una vez se cierra el browser.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Web Storage Demo</title>
    <script>
        function AlmacenarLocalStorage(nombre) {
            if (typeof(Storage) !== "undefined") {
                localStorage.Nombre = nombre;
            }
            else {
                document.getElementById("localStorage").innerHTML = "Su navegador no soporta web storage.";
            }
        }
 
        function ObtenerLocalStorage() {
            document.getElementById("localStorage").innerHTML = localStorage.Nombre;
            // localStorage.removeItem("Nombre");
        }
        
        function AlmacenarSessionStorage(apellido) {
            if (typeof (Storage) !== "undefined") {
                sessionStorage.Apellido = apellido;
            }
            else {
                document.getElementById("SessionStorage").innerHTML = "Su navegador no soporta web storage.";
            }
        }
 
        function ObtenerSessionStorage() {
            document.getElementById("SessionStorage").innerHTML = sessionStorage.Apellido;
        }
 
    </script>
</head>
    <body>
        <h2>Local Storage</h2>
        <form>
            <input type="text" id="txtNombre"/>
            <input type="button" id="btnAlmacenar" value="Almacenar en Local Storage" onclick="AlmacenarLocalStorage(document.getElementById('txtNombre').value);" />
            <button id="btnSubmit">Generar Submit</button>
            <input type="button" id="btnObtener" value="Recuperar Local Storage" onclick="ObtenerLocalStorage();"/>
            <a href="PersistenciaWebStorageDemo.html">Ir a Drag & Drop Demo</a>
            <div id="localStorage"></div>
        </form>
        
        <h2>Session Storage</h2>
        <form>
            <input type="text" id="txtApellido" />
            <input type="button" id="btnAlmacenar2" value="Almacenar en Session Storage" onclick="AlmacenarSessionStorage(document.getElementById('txtApellido').value);" />
            <button id="btnSubmit2">Generar Submit</button>
            <input type="button" id="btnObtener2" value="Recuperar Session Storage" onclick="ObtenerSessionStorage();" />
            <a href="PersistenciaWebStorageDemo.html">Ir a Drag & Drop Demo</a>
            <div id="SessionStorage"></div>
        </form>
 
    </body>
</html>

Cómo vemos en el demo anterior implementamos un ejemplo para cada tipo de almacenamiento, que de hecho se manejan de la misma forma, la única diferencia como vimos es la durabilidad de los datos en el browser del usuario. Y la clave del trabajo con el web storage es el manejo de LocalStorage y SessionStorage para obtener y crear propiedades, adicional podemos observar los datos almacenados en el navegador a través de las herramientas de desarrollo, y debemos tener en cuenta que los datos almacenados persisten entre los llamados a diferentes páginas.


Web Workers

Este Api nos permite ejecutar tareas en segundo plano, para evitar bloquear la interacción del usuario con el sistema, y que este pueda seguir normalmente mientras ejecutamos otro tipo de tareas.


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Html5 Web Workers Demo</title>
    <script>
 
        function EjecutarWorker() {
            if (typeof (workerTask) == "undefined") {
                workerTask = new Worker("AdministrarContador.js");
            }
 
            workerTask.onmessage = function (event) {
                document.getElementById("contador").innerHTML = event.data;
            };
        }
    </script>
</head>
<body>
    <div id="contador"></div>
    <script>
        EjecutarWorker();
    </script>
    <input type="button" value="Detener Contador" onclick="workerTask.terminate();">
</body>
</html>

Con los WebWorkers trabajamos con la clase Woeker y con un archivo JS externo, en el cual implementamos el proceso que deseamos ejecutar en segundo plano, si observamos en este caso usamos el JS AdministrarContador.js en el cual realizamos el siguiente proceso:

var contador = 0;
function AumentarContador() {
    contador++;
    postMessage(contador);
    setTimeout("AumentarContador()", 1000);
}
 
AumentarContador();

Cómo vemos es simplemente una función recursiva que aumenta un contador, y a través del método postMessage enviamos una actualización al Worker. Y en la función onmessage que delegamos al worker recibimos esa actualización enviada desde el archivo JS (AdministrarContador.js).

Server Sent

Este Api nos permite recibir actualizaciones automáticamente por parte de código escrito de lado del servidor


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
    <script>
        function ActualizarDesdeServidor() {
            if (typeof (EventSource) !== "undefined") {
                var source = new EventSource("ServerSentDemo.aspx");
                source.onmessage = function (event) {
                    document.getElementById("result").innerHTML += event.data + "<br>";
                };
            }
            else {
                alert('Su navegador no soporta Server Sent');
            }
        }
    </script>
</head>
<body>
    <div id="result"></div>
    <script>
        ActualizarDesdeServidor();
    </script>
</body>
</html>

Cómo vemos usamos el objeto EventSource para especificar el archivo de código de servidor que enviará las actualizaciones, y manejamos el evento onmessage al igual que en los WebWorkers para recibir las actualizaciones, bastante simple. Y veamos por ejemplo cómo trabajaríamos del lado del servidor con Asp.Net:

        protected void Page_Load(object sender, EventArgs e)
        {
            Response.ContentType = "text/event-stream";
            Response.Expires = -1;
            Response.Write(string.Format("data: hora del servidor: {0}"DateTime.Now));
            Response.Flush();
        }

Importante el ContentType, en el cual especificamos event-stream para lograr las actualizaciones en tiempo real, y en este caso enviamos actualizaciones de la fecha y hora del servidor a nuestro cliente a través del Response.Write.

Bueno amigos eso es todo de esta muestra de cómo usar las Apis JavaScript de Html5, espero les sea de utilidad y de interés. Y no duden en compartirlo.

Saludos, y buena suerte!

lunes, 2 de junio de 2014

[EntityFramework] Agregando propiedades y metadatos a nuestras clases autogeneradas en DB First

Hola amigos, ya que estamos dando por terminada esta serie de post sobre Entity FrameWork no quería dejar a un lado una importante característica que debemos conocer cuando trabajamos con el enfoque Data Base First y que seguro vamos a necesitar en cualquier proyecto en el que usemos este enfoque, y quiero antes que nada plantearles una necesidad para que comprendan con mayor facilidad la utilidad del tema que vamos a tratar en este artículo:

"Recordemos que un principio que debemos tener presente como desarrolladores es, nunca debemos modificar código autogenerado puesto que una vez se vuelva a autogenerar por cualquier motivo, perderemos los cambios que hicimos en el archivo en cuestión"

Bueno, ahora teniendo clara esta premisa, por si aún no se habían topado con algún caso de este tipo, surgen un par de interrogantes cuando trabajamos con Entity Framework DataBase First y son las siguientes:
  1. ¿Cómo puedo yo entonces agregar propiedades a mi entidad de domino directamente que no necesariamente quiero que persistan a mi base de datos?
  2. ¿Cómo puedo yo agregar metadata (Atributtes Data Anottations) adicional a mis entidades de domino?
Pues bien, creo que con estas dos interrogantes ya nos queda claro cuál es la necesidad de usar clases parciales para extender nuestras entidades autogeneradas. Ahora veamos como lo podemos lograr.

En primera instancia vamos a crear un modelo de Entity Framework usando el enfoque DataBase First el cual podemos observar en detalle en este artículo:  Creando nuestro modelo conceptual con DataBase First y como es normal veremos que se autogeneran nuestras entidades con todos sus metadatos, a este código es el que me refiero, no se puede modificar!

En nuestro caso se generó la entidad producto, como vemos a continuación. Si leemos el comentario en el encabezado de la clase veremos que se advierte de las consecuencias de modificarla:

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated from a template.
//
//     Manual changes to this file may cause unexpected behavior in your application.
//     Manual changes to this file will be overwritten if the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
 
namespace DataBaseFirstDemo.Dominio
{
    using System;
    using System.Collections.Generic;
    
    public partial class Producto
    {
        public Producto()
        {
            this.Categorias = new HashSet<Categoria>();
        }
    
        public string Nombre { getset; }
        public int Codigo { getset; }
    
        public virtual ICollection<Categoria> Categorias { getset; }
    }
}

Ahora bien, que tal si queremos agregar una nueva propiedad a la entidad en la cual podamos obtener el código y el nombre del producto en una sola cadena? para esto creamos una nueva clase parcial llamada producto, la cual debe tener el mismo NameSpace que la clase producto autogenerada, y cabe aclarar algo, el archivo .cs debe tener un nombre diferente a Producto.cs, pero la clase en su interior debe tener el nombre de Producto, para que el FrameWork las trate como una sola. Tal y como vemos a continuación:


Como podemos ver tenemos las dos clases que físicamente son diferentes, es decir en dos archivos diferentes pero lógicamente son una sola, ya que desde la clase que hemos creado podemos ver las propiedades Codigo y Nombre que fueron autogeneradas.

Bastante sencillo, ahora vamos a ver cómo podemos agregar metadata adicional a las propiedades autogeneradas. Ya que como sabemos Entity Framework genera la metadata que obtiene desde la base de datos, como la longitud del campo, constrains y demás.

Lo primero que debemos hacer es crear una clase que puede estar en el interior del archivo Producto.cs que creamos nosotros y la podemos llamar ProductoMetaData, en la cual vamos a replicar todas aquellas propiedades a las cuales queremos agregar metadata adicional, en nuestro caso vamos a replicar la propiedad Nombre, para especificar que es requerida y que solo debe permitir una longitud máxima de 4 caracteres:

using System.ComponentModel.DataAnnotations;
 
namespace DataBaseFirstDemo.Dominio
{
    [MetadataType(typeof(ProductoMetaData))]
    public partial class Producto
    {
        public string InformacionProducto
        {
            get
            {
                return string.Format("{0} - {1}"this.Codigo, this.Nombre);
            }
        }
    }
 
    public class ProductoMetaData
    {
        [RequiredMaxLength(4)]
        public string Nombre { getset; }
    }
}

Y para finalizar debemos indicar que esta clase de metadata va a ser usada por nuestras clase Producto, y lo hacemos decorando nuestras clase con el atributo MetadataType e indicando la clase de metadata que acabamos de crear, tal y como se ve en el fragmento de código anterior.

Bueno amigos eso es todo de esta muestra de cómo agregar propiedades y metadata adicional a nuestras entidades autogenradas en DataBase First, espero les sea de utilidad y de interés. Y no duden en compartirlo.

Saludos, y buena suerte!