Desmitificando CodeFirst(2/2) V 4.3

En el anterior post Desmitificando CodeFirst(1/2), me he centrado en la versión 4.2 de Entity Framework y el objetivo de este no es otro que analizar la versión 4.3 y ver realmente que hay en la tabla “dbo.__MigrationHistory”.

Siguiente con el modelo anterior lo primero que tenemos que hacer es activar Migration, pero como no es el objetivo de este post os paso un link para quien no lo conozca y quiera profundizar.

EF 4.3 Automatic Migrations Walkthrough.

El objetivo claro es demostrar dos cosas.

1. Lo que se guarda en la tabla no es un Hash sino un Xml, eso si con aderezo:).

2. Ver lo parco que es EF en la gestión de Exceptions teniendo a mano toda la información para mostrarnos algo más que lo que nos dice.

Vamos con lo primero. Para ello no necesitamos nada más que leer con Ado.Net la tabla “dbo.__MigrationHistory” y descomprimir el contenido de la columna Model.

   1: using (SqlConnection cn = new SqlConnection(@"Tu Conexion"))

   2:  {

   3:      cn.Open();

   4:  

   5:      SqlCommand cmd = new SqlCommand("select top 1 Model from dbo.__MigrationHistory order by CreatedOn desc", cn);

   6:      SqlDataReader rd = cmd.ExecuteReader();

   7:      rd.Read();

   8:      byte[] b = rd[0] as byte[];

   9:  

  10:      using (MemoryStream ms = new MemoryStream(b))

  11:      {

  12:          using (GZipStream stream2 = new GZipStream(ms, CompressionMode.Decompress))

  13:          {

  14:              var Edmx = XDocument.Load(stream2);

  15:              Console.WriteLine(Edmx.ToString());

  16:          }

  17:  

  18:      }    

  19:      

  20:  

  21:  

  22:  }

  23:  

  24:  Console.ReadLine();

Con lo cual si el XDocument lo guardasmos en disco con la extensión edmx y agregamos este al proyecto vemos que es lo que hay en la tabla Migrations.

Dibujo

Ya si nos queda claro que estamos utilizando Xml:)

Vamos a seguir con nuestro querido “InvalidOperationException”. Y concretamente de lo que me quejo es de EF no nos informe de algo más en esta exception, puesto que podría decirnos exactamente que es lo que cambia entre un modelo y otro.

Para ello seguimos con la operación del anterior post y cambiamos  la longitud de la propiedad Nombre de 50 a 100 y obtenemos la misma exceptión pero con un mensaje diferente.

Dibujo

Claro lo fácil sería ejecutar los siguiente comandos desde Pakage Manager Console

1. Enable-Migrations.

2. Add-Migration "Salvameporfavor"

3. Update-Database –Verbose.

Y todos felices, pero se puede dar el caso que no me interese modificar la base de datos sino simplemente saber el porque y en consecuencia actuar. Para ello vamos de nuevo con Reflectión y ver que hace internamente EF para mostrarnos esta exceptión. En definitiva lo que hace es comparar el modelo guardado con el modelo generado.

El código para obtener las diferencias es el siguiente y después de mostrarlo vendrás las criticas, eso si constructivas:)

   1: XDocument documentDataBase = null;

   2:  

   3: using (SqlConnection cn = new SqlConnection(@"Tu Cadena de Conexion"))

   4: {

   5:     cn.Open();

   6:  

   7:     SqlCommand cmd = new SqlCommand("select top 1 Model from dbo.__MigrationHistory order by CreatedOn desc", cn);

   8:     SqlDataReader rd = cmd.ExecuteReader();

   9:     rd.Read();

  10:     byte[] b = rd[0] as byte[];

  11:     using (MemoryStream ms = new MemoryStream(b))

  12:     {

  13:         using (GZipStream stream2 = new GZipStream(ms, CompressionMode.Decompress))

  14:         {

  15:             documentDataBase = XDocument.Load(stream2);

  16:         }

  17:  

  18:     }       

  19:  

  20:  

  21: }

  22:  

  23: XDocument documentModel = null;

  24: using (ContextCliente ct = new ContextCliente())

  25: {

  26:     using (MemoryStream stream = new MemoryStream())

  27:     {

  28:         XmlWriterSettings settings = new XmlWriterSettings();

  29:         settings.Indent = true;

  30:         using (XmlWriter writer = XmlWriter.Create(stream, settings))

  31:         {

  32:             EdmxWriter.WriteEdmx(ct, writer);

  33:  

  34:         }

  35:         stream.Position = 0L;

  36:         documentModel = XDocument.Load(stream);

  37:     }

  38:  

  39:  

  40: }

  41:  

  42:  

  43: Assembly a = typeof(DbContext).Assembly;

  44:  

  45: var tipo = a.GetTypes().Where(c => c.FullName == "System.Data.Entity.Migrations.Infrastructure.EdmModelDiffer").FirstOrDefault();

  46:  

  47: var ModelDiffer = Activator.CreateInstance(tipo);

  48:  

  49: var Metodo = ModelDiffer.GetType().GetMethod("Diff");

  50:  

  51: var resultado = Metodo.Invoke(ModelDiffer, new object[] { documentDataBase, documentModel, null });

Si miramos el código estamos llamando al metodo “Diff” de la clase “System.Data.Entity.Migrations.Infrastructure.EdmModelDiffer”. Como no, definida con el modificador de acceso “Internal” 🙁 y este nos devuelve un IEnumerable<MigrationOperation> y en nuestro caso una clase derivada de MigrationOperation con nombre “AlterColumnOperation”, me suena a la migration que queda pendiente de ejecutar en la bb.dd.

Y ahora yo digo en vez de preguntar y devolver bool en el método “ModelMatches” de la clase “InternalContext” por que no se contruye una exception en condiciones diciendo en que difieren ambos modelos, veis como la critica es constructiva:)

ModelMatches de la clase InternalContext".

   1: public virtual bool ModelMatches(XDocument model)

   2: {

   3:     string connectionString = null;

   4:     return !new EdmModelDiffer().Diff(model, this.Owner.GetModel(), connectionString).Any<MigrationOperation>();

   5: }

Pues nada esperemos que alguien piense en ciertas mejoras y que estás nos ayuden a dormir mas felices:)

2 comentarios en “Desmitificando CodeFirst(2/2) V 4.3”

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *