En este post voy a hacer un ejemplo de aplicación de Entity Framework usando una técnica no muy conocida pero que a mí personalmente me gusta.

Todos los que hayáis trabajado con EF sabes que existen varios estilos de aplicarlo: Database First, Model First y Code First. El cuándo es mejor aplicar una u otra o si una es mejor que otra, es una discusión en la que no voy a entrar en este post, porque requeriría un post solo para ello.

En esta ocasión voy a hablar de uno que realmente no tiene nombre pero que yo le llamo Database First – Code Second (Nota: navegando en Internet cuando pensé en el nombre vi que otra persona ya se había referido con ese nombre, así que no soy el creador jaja)

Prerrequisitos

Visual Studio >= 2012

SQL Server >= 2012

EF Power tools http://visualstudiogallery.msdn.microsoft.com/72a60b14-1581-4b9b-89f2-846072eff19d

Código Fuente del proyecto en: https://github.com/jrodrigv/EFDbFirstCodeSecond/tree/EFDbFirstCodeSecond

Diseño de la base de datos

Usando Visual Studio 2013 voy a crear una solución vacía:

A continuación voy a añadir un proyecto de SQL Server.

La base de datos va a ser sencilla pero ilustrativa, quiero una base de datos que soporte algo parecido a https://www.goodreads.com/. Usuarios que tienen listas de libros con distintos estados (Leyendo, Quiero leer, etc.) y esos mismos usuarios tienen contactos de los que pueden ver el estado etc.

Creación de la tabla Usuario.

Sobre el proyecto de SQL Server hacemos botón derecho, y añadimos una tabla con nombre Usuario y el bonito diseñador de tablas se nos abrirá. He introducido los siguientes campos que son más que suficientes para el ejemplo.

Creación de la tabla Libro.

He añadido un estado y un integer para el progreso de la lectura, la única cosa interesante la FK al Usuario para saber que usuario ha añadido el libro y modelar la relación de pertenencia 1-N entre un usuario y sus libros (o lecturas).

Creación de la tabla intermedia Usuario_Usuario.

Esta tabla almacenera la relación N-M entre los usuarios y sus contactos.

Publicación de la base de datos:

Sobre el proyecto de base de datos pulsamos botón derecho y propiedades. Debemos seleccionar la plataforma de destino en la que queremos desplegar la base de datos en este caso selecciona SQL Server 2012. Tenéis muchísimas más opciones en las que podéis indagar pero que voy a dejar fuera del alcance de este ejemplo.

Si pulsamos botón derecho sobre el proyecto y publicar, tenemos acceso a la información de publicación, por ahora me interesa ir la parte de Advanced para seleccionar unas opciones.

He añadido la opción de siempre recrear la base de datos y crear un back-up antes de publicar, me gusta la opción de recrear la base de datos porque es posible que algunos cambios que hagáis al diseño sean imposibles de realizarse si no la recreáis (fallando la publicación). Sin embargo si ya tenéis datos sensibles almacenados puede ser que queráis intenta desplegar los cambios sin recrear la base de datos.

A continuación voy a publicar la base de datos, una manera de hacerlo es desde el SQL Server Object Explorer. Bajo el nodo de SQL Server se nos mostraran todas las instancias de SQL Server que tengamos y además (y esto es interesante) todos los proyectos de SQL Server que tengamos en Visual Studio. Como veis tengo algo parecido a una base de datos con el nombre del proyectp colgando de (localdb)Projects. Hacemos botón derecho y Publish Data-tier Application.

Tenemos que seleccionar fichero con extensión dacpac que se genera tras compilar el proyecto de base de datos, seleccionar nuestra instancia de SQL Server, el nombre que queramos para la base de datos y pulsamos en Publicar.

Si todo ha ido bien como es mi caso, veremos un tick verde.

Y ahora si nos conectamos a nuestra instancia de SQL Server podremos ver que tenemos la base de datos creada con todas las tablas que hemos definido.

Code Second, generar las clases PoCO y dbContext a partir de la base de datos.

Creamos un nuevo proyecto de biblioteca de clases.

Pulsamos botón derecho sobre el proyecto -> Entity Framework -> Reverse Engineer Code First.

Nos conectamos a la instancia de la base de datos que hemos creado.

Y podemos ver el código que se ha generado.

Los mapeos entre el modelo de base de datos y las nuevas clases están dentro de la carpeta Mapping, es posible quenecesitemos customizar algún mapeo como veremos más adelante. También tenemos el dbContext con el nombre EFDbFirstCodeSecondContext.cs y las clases de nuestro modelo Libro y Usuario.

Recomiendo crear otro proyecto solo para el modelo y llevar las clases propias del modelo, esto ayudara a que la fase de testing sea más sencilla y poder aplicar enfoques como DDD o TDD. También si queremos hacer aplicaciones universales, pudiendo crear una portable library solo con el modelo. Pero esto queda fuera del alcance de este post J.

Si abrimos la clase Usuario veremos que el mapeo de la tabla intermedia Usuario_Usuario es un poco “extraño”.

Nos ha realizado dos mapeos de listas y nosotros solo deseamos uno. Este mapeo indica una doble navegación por una parte un usuario tiene su lista de usuarios pero, también tenemos otro mapeo con el sentido contrario de la navegación en el que podemos ver que usuario nos tiene a nosotros en su lista! Puede ser que para ciertos escenarios sea interesante tenerlo no?

public partial class Usuario
    {
        public Usuario()
        {
            this.Libroes = new List<Libro>();
            this.Usuario1 = new List<Usuario>();
            this.Usuarios = new List<Usuario>();
        }

        public int Id { get; set; }
        public string email { get; set; }
        public string password { get; set; }
        public string nombre { get; set; }
        public virtual ICollection<Libro> Libroes { get; set; }
        public virtual ICollection<Usuario> Usuario1 { get; set; }
        public virtual ICollection<Usuario> Usuarios { get; set; }
    }

Os voy a contar como podríamos eliminar este sentido de la navegación, iríamos a la clase UsuarioMap y borraríamos el parámetro dentro de WithMany() quedando asi.

// Relationships
            this.HasMany(t => t.Usuario1)
                .WithMany()
                .Map(m =>
                    {
                        m.ToTable("Usuario_Usuario");
                        m.MapLeftKey("UsuarioId_contacto");
                        m.MapRightKey("UsuarioId_principal");
                    });

Ejemplo:

Vamos a crear un proyecto de consola

NOTA: Vais a necesitar copiar la cadena de conexión de la base de los datos del App.config del proyecto DataAccess al fichero App.config de este proyecto.

El código que no necesita comentarios J

static void Main(string[] args)
        {
            try
            {
                var usuario1 = new Usuario() { nombre = "Jesus", email = "jesus.rodriguez.valencia@gmail.com",password = "admin"};
                var usuario2 = new Usuario() { nombre = "Javier", email = "javito@outlook.com" ,password = "admin"};
                var usuario3 = new Usuario() { nombre = "Jose", email = "jose@hotmail.com" ,password = "admin"};

                var libro1 = new Libro() { titulo = "El Hobbit", Usuario = usuario1, progreso = 50, estado = "leyendo" };
                var libro2 = new Libro() { titulo = "El Silmarillion", Usuario = usuario2, progreso = 10, estado = "leyendo" };
                var libro3 = new Libro() { titulo = "El Senor de los anillos", Usuario = usuario3, progreso = 0, estado = "no_empezado" };

                using (var dbContext = new EFDbFirstCodeSecondContext())
                {
                    dbContext.Usuarios.Add(usuario1);
                    dbContext.Usuarios.Add(usuario2);
                    dbContext.Usuarios.Add(usuario3);
                    usuario1.Usuarios.Add(usuario2);
                    usuario1.Usuarios.Add(usuario3);
                    usuario2.Usuarios.Add(usuario1);
                    usuario3.Usuarios.Add(usuario1);
                    dbContext.Libroes.Add(libro1);
                    dbContext.Libroes.Add(libro2);
                    dbContext.Libroes.Add(libro3);
                    dbContext.SaveChanges();
                }
                Console.WriteLine("Perfecto!");
            }
            catch (Exception ex)
            {
              Console.WriteLine(ex.Message);
                Console.Read();
            }
        }

 

Finalmente vamos a comprobar que la base de datos tiene la información deseada.

 

 

 

Anuncios

 

Hoy me peto el Visual Studio, como puede ser! Bueno la verdad es que ya iba siendo hora, tengo instalados el 2010, 2012 y 2013 con varias extensiones instaladas y alguna vez debería ocurrir (tengo que decir que en 10 años que llevo utilizando esta es la primera vez directamente no me arrancaba).Básicamente a los segundos de ejecutarlo se me quedaba completamente congelando.

Como esto es algo que no suele ocurrir, voy a comentar que he hecho para arreglarlo.

Primer paso, arrancar Visual Studio en modo seguro. Con un acceso directo añadimos el parámetro  devenv.exe /SafeMode
y ejecutamos.

Si esta vez funciona, es que podemos asegurar que es culpa de una de las extensiones que tenemos instaladas.

Segundo paso, crear un fichero de log del arranque de Visual Studio con devenv /log c:\vs.log

Si no especificáis ruta lo creara en C:\Users\ user_folder \AppData\Roaming\Microsoft\VisualStudio\XX.X\
ActivityLog.xml

Si especificáis una ruta con directorios, aseguraos que existen porque el comando no los creara por vosotros.

Dentro del fichero veréis paso a paso las operaciones que está haciendo Visual Studio.

Tercer paso, si después de 2 todavía no sabéis que es lo que falla, entonces toca ir al visor de eventos y comprobar la hora a la que se congelo Visual Studio y ver que eventos han ocurrido en ese momento.

En mi caso ha sido un problema con las TFS Power Tools 2012. He desinstalado las 2012 e instalado las 2013 Update 2 y conseguido que VS2012 y VS2013 funcionen sin problema.