DbMigration y Code First

Introducción

Una de las novedades que se introdujo con la versión 4.3 de Entity Framework son las llamadas Database Migration (DbMigration), que vienen a satisfacer las necesidades de los desarrolladores a la hora de realizar cambios en la estructura de la base de datos, como por ejemplo:

– Creación o borrado de Tablas.

– Cambiar o quitar columnas.

– entre otras cosas 🙂

La principal problemática venía a la hora de tener nuestra aplicación instalada en varios clientes y que tengan distintas versiones de Base de Datos.

Empezando

Para poder empezar a utilizar DbMigration es necesario que al “menos” tengamos instalada la versión 4.3.1(*) de Entity Framework, si no podremos instalarla desde nuget o desde la consola de comandos:

   1: Install-Package EntityFramework 

(*) Se encuentra en fase beta la version  5.0 de Entity Framework.

 

A continuación desde la consola de comandos, tendremos que especificar la habilitación de migraciones para nuestro proyecto, para ello ejecutaremos el comando “Enable-Migrations”.

Esta acción va a realizar los siguientes procesos:

– Va a buscar una clase dentro de nuestro proyecto que implemente la clase DBContext, para utilizar dicho contexto a la hora de definir las migraciones.

– Va a crear una nueva carpeta “Migrations” en nuestro proyecto. Esta carpeta va a contener los siguientes ficheros:

  1.  
    1. Clase Configuration.cs: Esta clase va a ser una implementación de la clase base genérica DbMigrationsConfiguration con nuestro objeto contexto.
      Dentro de ella, vamos a poder definir la configuración de la migración (en el constructor) y si hay que hacer algun proceso de llenado de información (método Seed).
    2. Una Migración Inicial: va a definir una clase que implemente la clase Base DbMigration, donde tendremos el método Up con la creación de nuestras entidades, y el método Down con el borrado de las mismas. La implementación de las migraciones se realiza en dos partes, la primera es la que acabamos de ver, y la segunda es relativa a información que se va a almacenar en la Base de Datos.

– Va a crear una tabla dentro de nuestra Base de Datos  llamada __MigrationHistory que va a almacenar información relativa a las migraciones.

 

¿Cómo crear una Migración?

Bien para crear una nueva migración desde la consola de comandos ejecutaremos la siguiente instrucción:

“Add-Migration Name”

Al ejecutarlo, se va a generar una nueva clase dentro de la carpeta Migrations, con una estructura similar a la mencionada anteriormente con la migración inicial.

Dentro de dicha clase vamos a tener que implementar los métodos Up, para subir de versión, y Down para bajar de versión.

¿Qué Métodos podemos utilizar en los métodos Up y Down?

AddColumn AddForeignKey AddPrimaryKey
AlterColumn CreateIndex CreateTable<T>
DropColumn DropForeignKey DropPrimaryKey
DropIndex DropTable MoveTable
RenameColumn RenameTable SQL

Ejemplo:

   1: public partial class ProductMigration : DbMigration

   2:     {

   3:         public override void Up()

   4:         {

   5:             CreateTable("Products",

   6:                 columns => new

   7:                 {

   8:                     ProductId = columns.Int(identity: true),

   9:                     Name = columns.String(maxLength: 100),

  10:                     Description = columns.String(),

  11:                     Price = columns.Decimal(precision: 10, scale: 2)

  12:                 });

  13:         }

  14:         

  15:         public override void Down()

  16:         {

  17:             DropTable("Products");

  18:         }

  19:     }

¿Cómo actualizar la Base de Datos?

Para poder ejecutar una migración tenemos dos posibles formas:

1) Desde la consola de Comandos:

A través del comando “Update-Database”. Los parámetros del comando son:

– SourceMigration <String>
– TargetMigration <String>
– Script [<SwitchParameter>]
– Force [<SwitchParameter>]
– ProjectName <String>
– StartUpProjectName <String>
– ConfigurationTypeName <String>
– ConnectionStringName <String>
– ConnectionString <String>
– ConnectionProviderName <String>
  

2) Desde código:

Definiremos un objeto del tipo Configuration, otro del tipo DbMigrator, y ejecutaremos el método Update:

   1: var config = new Migrations.Configuration();

   2:             var migrator = new DbMigrator(config);

   3:             migrator.Update();

Esta acción va a ejecutar la actualización a la ultima versión de la Base de Datos. En el caso de desear ejecutar una migración concreta o volver a una versión determinada, ejecutaríamos de la siguiente manera:

   1: var config = new Migrations.Configuration();

   2: var migrator = new DbMigrator(config);

   3: migrator.Update("201203140720163_InitialCreate");

 

 

Espero que os resulte de utilidad.

 

Saludos!

WebCast Entity Framework

Buenos Tardes,

El viernes pasado iba a realizar un evento online acerca de Entity Framework, por motivos ajenos a GuseNET no pudimos realizar dicho evento de forma online.

Lo primero a todos los que estabais interesados en verlo, Lo siento!!

Voy a realizarlo como tenia previsto este Viernes 09 de Marzo a las 16:30 hora de Madrid.

El enlace de registro:https://msevents.microsoft.com/CUI/EventDetail.aspx?EventID=1032508198&Culture=es-ES

Un saludo

[GuseNET] WebCast Entity Framework

Buenos días,

 

Hace un tiempo publique un Evento presencial en el que voy a hablar acerca de Entity Framework para el grupo de usuarios GUSNET.

 

En vista de que varías personas han preguntado si lo ibamos a grabar o si lo ibamos a emitir de alguna manera online, y gracias a la gente de MS, tenemos la posibilidad de emitirlo vía Live Meeting.

 

La URL de registro del evento: aquí.

 

Un saludo

[UOC] Materiales WebCast Async y Await

Buenos días,

 

Desde este post lo primero que quiero hacer es daros las gracias por asistir a la charla sobre Async y Await que tuve el placer de impartir ayer.

 

Os dejo los materiales para que los descarguéis y podáis hacer vuestras pruebas.

 

Presentación

Demos

 

En breve también podréis acceder a la grabación de la misma para aquellos que no pudisteis asistir.

Si tenéis cualquier cosa, no dudéis en enviarme un correo o Tweet.

Un saludo.

PD: Las demos están realizadas con VS11, pero para aquellos que queráis probarlo en VS2010 solo tendréis que descargar Async CTP V3 para VS y podréis probarlo.

[GuseNET] Evento Sobre Entity Framework + Mesa Redonda

Hoy os quiero anunciar una charla que voy a impartir para el Grupo de Usuarios GUSENET.

La Agenda del Evento es:

16:30 – 19:00:  USO Y VENTAJAS DE ENTITY FRAMEWORK 4: ASÍ COMO TRUCOS PARA ENFRENTARSE A CUESTIONES HABITUALES DEL USO DIARIO
19:00 – 20:00:  Mesa redonda para debatir reuniones futuras del grupo.

¿Dónde?

Universidad de Murcia, Facultad de Informática

Aula 1.01

 

El enlace de registro: Aquí

 

Saludos

#ORAPOCO: V2.1

Continuando con mi particular cruzada de crear un Mini ORM para trabajar con ORACLE, publico una nueva versión de ORAPOCO.

Historia:

OraPoco V2.0

OraPoco Update 2

OraPoco Primer Update

OraPoco 1.0

¿Qué incluye esta nueva versión?

– Corrección de un pequeño bug.

– Mejora en el rendimiento de la obtención de resultados de la base de Datos.

– BulkInsert: Permite la inserción de datos a través de OracleBulkInsert. Es bastante más rápido que ejecutar las inserciones por separados, pero no tan rápido que utilizar BulkInsert directamente puesto que es necesario hacer una serie de conversiones.

Ejemplo:

   1: Enumerable.Range(1, 300000).AsParallel().ForAll(a =>

   2: {

   3:     datos.Add(new BL_BLOQUEO_PANTALLA

   4:     {

   5:         BLPA_FECHA = DateTime.Now.ToShortDateString(),

   6:         BLPA_HORA = DateTime.Now.ToShortTimeString(),

   7:         BLPA_PANTALLA = "",

   8:         BLPA_USUARIO = "1",

   9:         BLPA_ID = a

  10:     });

  11: });

  12: db.BulkInsert<BL_BLOQUEO_PANTALLA>(datos);

¿Qué es lo que vendrá?

Proyecto de Testing, Mejoras y más equivalencia de tipos de Oracle, Procedimientos Almacenados…

 

Como siempre os pido un poquito de feedback si lo probáis.

Enlaces de Descarga:

GitHub.

Nuget

 

Saludos!!!

[UOC] WebCast sobre Async y Await

Alex Casquete me preguntó hace un tiempo si me apetecía dar una charlita sobre el nuevo paradigma de programación asíncrona, que se va a introducir con C# 5.0 y que ya podemos probar con VS 11 y la Async CTP 11, ASYNC y AWAIT.

Mi respuesta fue que si 😀

Así que se queréis ver de que va todo esto el Jueves 23 de Febrero haré este WebCast en colaboración con UoC Dot Net Club.

Enlace al registro del evento. Registro.

Saludos

ORAPOCO: V2.0

Introducción

Bueno, como habréis podido notar ando creando un ORM “ligero” para trabajar contra Oracle (Serie ORAPOCO).

Bien, en la segunda actualización, tanto @Eiximenis como @Juanma, me indicaban algo que las clases POCO no debía implementar ninguna clase Base, algo que a mi tampoco me convencía haberlo agregado.

Además de ello Eduard comentaba la posibilidad de hacer configuración de los Mapeos, para evitar hacer uso de Atributos en las clases POCO y que están sean completamente POCO. El tema de los mapeos es parecido a lo que se ve en FluentNhibernate.

Por otro lado @_PedroHurtado, me comentaba que era posible hacer Sql Injection, y me propuso que modificara los parámetros String por Expression.

Bueno por estos motivos nace está nueva versión de ORAPOCO.

Cambios

Modificación de la Plantilla T4

He modificado la plantilla T4, de tal forma que va a generar objetos POCO, sin ningún tipo de atributo:

Objeto POCO:

1: public class BL_BLOQUEO_PANTALLA


2: {


3: public System.Int32 BLPA_ID {get;set;}


4: public System.String BLPA_PANTALLA {get;set;}


5: public System.String BLPA_FECHA {get;set;}


6: public System.String BLPA_HORA {get;set;}


7: public System.String BLPA_USUARIO {get;set;}


8: }


Del mismo modo se van a generar unas nuevas clases, que serán los Mapeadores por defecto:

1: public class BL_BLOQUEO_PANTALLAMapper : GenericMapper<BL_BLOQUEO_PANTALLA>


2: {


3: public Type GetMappedType()


4: {


5: return typeof(BL_BLOQUEO_PANTALLA);


6: }


7: public BL_BLOQUEO_PANTALLAMapper() : base()


8: {


9: AddColumnMapping(x=> x.BLPA_ID);


10: SetPrimaryKeyField(x => x.BLPA_ID);


11: AddColumnMapping(x=> x.BLPA_PANTALLA);


12: SetNullable(x=> x.BLPA_PANTALLA);


13: AddColumnMapping(x=> x.BLPA_FECHA);


14: SetNullable(x=> x.BLPA_FECHA);


15: AddColumnMapping(x=> x.BLPA_HORA);


16: SetNullable(x=> x.BLPA_HORA);


17: AddColumnMapping(x=> x.BLPA_USUARIO);


18: SetNullable(x=> x.BLPA_USUARIO);


19: }


20: }


Creación de los Objetos OracleColumnInfo,Mapper, IGenericMapper<T> y GenericMapper<T>

Para facilitar el mapeo de datos, he creado las siguientes clases e interfaz:

Clase OraceColumnInfo para representar la información relativa a cada columna de los objetos POCO.

1: public class OracleColumnInfo


2: {


3: public OracleColumnInfo()


4: {


5: IsPk = false;


6: Nullable = false;


7: }


8: public bool IsPk { get; set; }


9: public string ColumnName { get; set; }


10: public bool Nullable { get; set; }


11: public string DefaultValue { get; set; }


12: public string SequenceName { get; set; }


13: }


La Interfaz IGenericMapper<T>:

1: public interface IGenericMapper<T>


2: {


3: IGenericMapper<T> SetPrimaryKeyField(Expression<Func<T, object>> property);


4: IGenericMapper<T> AddColumnMapping(Expression<Func<T, object>> property);


5: IGenericMapper<T> SetNullable(Expression<Func<T, object>> property);


6: IGenericMapper<T> SetDefaultValue(Expression<Func<T, object>> property, string DefaultValue);


7: IGenericMapper<T> SetSequence(Expression<Func<T, object>> property, string SequenceName);


8: }


La clase Mapper: Va a servir para representar el conjunto de columnas de un objeto POCO.

La clase: GenericMapper<T>, va a extender a Mapper e implementar a IGenericMapper:

1: public class GenericMapper<T> : Mapper,IGenericMapper<T>


2: {


3: public GenericMapper()


4: {


5: Columns = new List<OracleColumnInfo>();


6: }


7: 


8: public IGenericMapper<T> AddColumnMapping(Expression<Func<T, object>> property)


9: {


10: PropertyInfo propertyInfo = GetProperty(property);


11: return this;


12: }


13:


14: public IGenericMapper<T> SetPrimaryKeyField(Expression<Func<T, object>> property)


15: {


16: PropertyInfo propertyInfo = GetProperty(property);


17: AddColumn(propertyInfo);


18: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().IsPk = true;


19: return this;


20: }


21:


22: public IGenericMapper<T> SetNullable(Expression<Func<T, object>> property)


23: {


24: PropertyInfo propertyInfo = GetProperty(property);


25: AddColumn(propertyInfo);


26: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().Nullable = true;


27: return this;


28: }


29: public IGenericMapper<T> SetDefaultValue(Expression<Func<T, object>> property, string DefaultValue)


30: {


31: PropertyInfo propertyInfo = GetProperty(property);


32: AddColumn(propertyInfo);


33: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().DefaultValue = DefaultValue;


34: return this;


35: }


36: 


37: private void AddColumn(PropertyInfo propertyInfo)


38: {


39: var column = Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault();


40: if (column == null)


41: Columns.Add(new OracleColumnInfo { ColumnName = propertyInfo.Name });


42: }


43: private static PropertyInfo GetProperty(Expression<Func<T, object>> property)


44: {


45: PropertyInfo propertyInfo = null;


46: if (property.Body is MemberExpression)


47: {


48: propertyInfo = (property.Body as MemberExpression).Member as PropertyInfo;


49: }


50: else


51: {


52: propertyInfo = (((UnaryExpression)property.Body).Operand as MemberExpression).Member as PropertyInfo;


53: }


54: 


55: return propertyInfo;


56: }


57:


58: public IGenericMapper<T> SetSequence(Expression<Func<T, object>> property, string SequenceName)


59: {


60: PropertyInfo propertyInfo = GetProperty(property);


61: AddColumn(propertyInfo);


62: Columns.Where(col => col.ColumnName.Equals(propertyInfo.Name)).FirstOrDefault().SequenceName = SequenceName;


63: return this;


64: }


65:


66: }


Creación de ConfigureMapping y AutoConfigureMapping

Ambos métodos van  a servir para configurar los Mapeos, y serán invocados de forma Manual.

AutoConfigureMapping, va a registrar todas aquellas clases que Implementen AutoConfigureMapping dentro del Assembly en el que nos encontremos.

Código:

Primer Ejemplo: Como crear y registrar un nuevo Mapeo:

1: db.ConfigureMapping<SAMPLE>(new GenericMapper<SAMPLE>().


2: SetPrimaryKeyField(x => x.ID).


3: AddColumnMapping(x => x.NOMBRE).


4: SetDefaultValue(x => x.ID, «SysGuid()»));


El Mapeo permite llevar a cabo:

– Creación de columnas.

– Establecer PK Simple o Compuesta.

– Establecer Valor Por Defecto.

– Establecer Nulos.

– Establecer Secuencias.

A la hora de realizar una inserción, actualización o borrado, se va a comprobar si existe un mapeo Configurado, en caso de no existir se intentará ejecutar como si tuviese Atributos.

Nuevas formas de Ejecutar Consultas:

Al haber sustituido string por Expression, se ejecutarán las consultas del siguiente modo:

1: var data = db.Query<EMPRESA>(columns:


2: (x) => new { x.EMPR_NOMBRE_CORTO, x.EMPR_NOMBRE_EMPRESA },


3: where:


4: (x => ( x.EMPR_NOMBRE_EMPRESA.Contains(«D») ||


5: x.EMPR_CODIGO_EMPRESA != 101 ||


6: x.EMPR_AÑO >= 2008 ) && (


7: x.EMPR_MES == 1 )));


Los valores esperados para los parámetros “Columns”, “Where” y “Order”, van a ser expresiones lambda.

 

Finally

Bien como siempre tenéis las descargas en:

GitHub

Nuget

 

Y como siempre cualquier feedback será bien recibido.

ORAPOCO: Update 2

Introducción

Hace unos días publique ORAPOCO, un ORM ligero para trabajar con Bases de Datos Oracle, y su primera actualización

Cambios y Mejoras

– He realizado un poco de refactoring en el código.

– He creado una clase abstracta “Base” que va a servir como Base de nuestras Entidades.

– He añadido una propiedad para indicar el modo de ejecución de los comandos: Normal(Valor por defecto) o UnitOfWork.

– He modificado la plantilla, para dar la posibilidad de utilizar el atributo “DefaultValue” con los valores por defecto de la Base de Datos.

Ejemplos:

Las nuevas entidades generadas por la plantilla tendrán un aspecto similar a:

 

   1: public class SAMPLE : Base

   2:    {

   3:        [IsPK()]

   4:        [DefaultValue("SYS_GUID()")]

   5:        public System.Guid ID { get; set; }

   6:  

   7:  

   8:        public System.String NOMBRE { get; set; }

   9:  

  10:    }

 

Un ejemplo de utilización de UoW:

   1: var db = new POCO.Ora.TP.OracleDB("MyDB",POCO.Ora.TP.Enums.Mode.UnitOfWork);

   2: var sample = new POCO.Ora.TP.SAMPLE { NOMBRE = "Javier" };

   3: db.Insert<POCO.Ora.TP.SAMPLE>(sample);

   4: db.Insert<POCO.Ora.TP.SAMPLE>(new POCO.Ora.TP.SAMPLE { NOMBRE = "Alvaro" });

   5: db.Save();

 

Descargas:

 

Como siempre tenéis las descargas en:

GitHub

Nuget

Y proximamente en CodePlex.

 

Saludos, y como siempre si me dais FeedBack Genial!

ORAPOCO: Primera actualización

Introducción

Hace unos días publique ORAPOCO, y hoy lanzo una nueva versión con algunas features y modificaciones.

Lo nuevo

Modificaciones:

– Modificación en el atributo IsPK: Tal como comentaba Eduard en el post anterior realmente no es necesario indicar True en dicho atributo puesto que solo lo van a tener los campos que formen la PK.

– Refactorización de los métodos de Inserción, Actualización y Borrado.

Mejoras:

– Creación del atributo Sequence:

   1: internal class SequenceAttribute : System.Attribute 

   2: {

   3:     public SequenceAttribute(string sequenceName)

   4:     {

   5:         if (string.IsNullOrWhiteSpace(sequenceName)) throw new Exception("Sequence name is necessary.");

   6:         SequenceName = sequenceName;

   7:     }

   8:  

   9:     public string SequenceName { get; set; }

  10: }

 

Este atributo, que se agregará de forma manual a nuestras entidades nos va a permitir simular el uso de auto numéricos en Oracle a través de una Secuencia.

Ejemplo de uso:

   1: public class TM_EMPRESAS

   2: {

   3:     [IsPK()]

   4:     [Sequence("S_Empresas")]

   5:     public System.Int16 EMPR_CODIGO_EMPRESA {get;set;}       

   6:     public System.String EMPR_NOMBRE_EMPRESA {get;set;}       

   7: ///...

   8: }

De esta manera a la hora de ejecutar la sentencia utilizará el siguiente valor auto numérico de la secuencia indicada.

Descargas:

GitHub

Nuget

 

Saludos!