[Entity Framework] Encapsular las Fk de las entidades

La ultima vez que use Entity Framework quedé con un disgusto por como terminé modelando los objetos, en parte por ignorancia y en parte por imposiciones y convenciones propias del ORM.

En esta ocasión, me encontré con un caso de propiedades de navegación y las propiedades “{*}Id” que usa Entity Framework para construir las relaciones. Esto me sirvió para recordar como funcionan estas convenciones y ver si puedo crear un modelo menos anémico y más ajustado a la realidad.

Dado el siguiente modelo:

    public class Entity
    {
        protected Entity()
        {
            Id = Guid.NewGuid();
        }

        public Entity(Guid id)
        {
            Id = id;
        }

        public Guid Id { get; private set; }
    }

    public class User : Entity
    {
        //EF
        protected User() { }

        public User(string name, string lastName)
        {
            LastName = lastName;
            Name = name;
        }

        public void FinishRegister(Country country)
        {
            Country = country;
        }

        public string Name { get; private set; }

        public string LastName { get; private set; }

        public Country Country { get; private set; }
    }

    public class Country : Entity
    {
        //EF
        protected Country() { }

        public Country(string name, string iso)
        {
            Iso = iso;
            Name = name;
        }

        public string Name { get; private set; }

        public string Iso { get; private set; }
    }

Vemos en el modelo ya una convención de EF, el uso de la propiedad virtual para poder generar los proxies que serán usados en el lazy loading y change tracking… pero continuemos.

La propiedad virtual es una convención en EF que creará una columna FK, uniqueidentifier, allow nulls con el nombre de la propiedad y el sufijo “_Id”. Lo que se ajusta perfecto a nuestro modelo.

Supongamos ahora que el usuario se registra con su nombre y apellido, y luego finalizará su proceso de registro. En este proceso solicitamos toda la información restante. Por practicidad del ejemplo solo será el país.

Este modelo se ajusta bien a los requerimientos pero si lo llevamos a la practica no será muy “optimo“, es decir, suponiendo un entorno web: en una petición POST, para finalizar el registro, en los datos de la petición solo enviaremos el Id del país y será necesario entonces consultar la base de datos para recuperar el el usuario y el país (por su Id) y luego si finalizar el registro. Desde mi punto de vista la consulta que sobraría es la del país.

            using (var context = new ApplicationDbContext())
            {
                var user = context.Users.Find(viewModel.UserId);
                var country = context.Countries.Find(viewModel.SelectedCountry);
                user.FinishRegister(country);
                context.SaveChanges();
            }

Entonces la solución pasaría por contaminar el modelo usando una propiedad Id que represente la identidad del país y aprovechar las bondades del orm, que para eso lo estamos usando! El modelo queda de la forma:

    public class User : Entity
    {
        //EF
        protected User() { }

        public User(string name, string lastName)
        {
            LastName = lastName;
            Name = name;
        }

        public void FinishRegister(Guid countryId)
        {
            CountryId = countryId;
        }

        public string Name { get; private set; }

        public string LastName { get; private set; }

        public Guid? CountryId { get; private set; }

        public Country Country { get; private set; }
    }

En este caso usamos la convención de llaves foraneas de EF y ya podremos usar solo el Id del país para finalizar el proceso de registro.

Lo que no me gusta de esta solución es tener que exponer al publico la propiedad CountryId  pues esto no representa nada en el exterior, para eso está la propiedad del tipo País que ya definimos. Entonces lo mejor sería ocultarla del exterior quitándole su modificador de acceso publico. Pero al quitarle el acceso publico EF no entenderá la convención de llaves foráneas y  el código que finaliza el registro no servirá, porque no entiende que ese GUID es la clave de país.

Ahora la pregunta es: ¿Cómo mapear propiedades no publicas en Entity Framework?… y Stack Overflow sale al rescate.

En síntesis agregamos otro contaminante al modelo, el atributo Column, y generaremos una convención que se encargue de hacer el mapping de estas propiedades (no publicas y marcadas con dicho atributo) vía reflection. El código del contexto está en este gist

De esta forma ya podemos Finalizar el registro haciendo uso del Id pero sin exponer propiedades extra al exterior.

Espero les sea de utilidad.

Hasta el próximo post.

[Entity Framework] Encapsular las Fk de las entidades

[Windows Azure] Tables without a clustered index are not supported in this version of SQL Server – Entity Framework Migration

Como muchos desarrolladores, no soy experto en ningún motor de base de datos. Conozco muchas de las cosas que como desarrollador debería conocer con motores con los que he trabajado, y pese a que se qué éste es un tema controversial, soy de los que piensa que el desarrollador no debería “casarse” con un tipo o producto/fabricante de almacenamiento de datos en especifico, esto prefiero dejárselo a los expertos DBAs y acudir o incluir uno en el equipo cuando sea necesario (casi siempre), y por el contrario saber moverse muy bien entre distintos tipos y motores de bases de datos  sin llegar a ser un experto. Esto lo digo porqué creo que pasarían muchos años para conseguirlo y en la época que la que vivimos hablar de años ya es mucho tiempo para la tecnología.

En este caso me pasó algo curioso con el SQL Azure y una migración del Entity Framework, que, como cosa rara, en mi maquina funcionaba :(. El caso es sencillo: Cambiar el tipo de dato de una columna que es clave primaria. No preguntes porqué ha surgido esta necesidad, pero así fue.

En mi SQL Server 2012 bastó con un código de la forma:

            DropPrimaryKey("dbo.MyTable");
            AlterColumn("dbo.MyTale", "Id", c => c.Long(nullable: false));
            AddPrimaryKey("dbo.MyTable", "Id");

Y con esto se habrá generado bien un Index Clustered para MyTable.

Cuando pasamos al Azure y corrimos una YSOD nos avisó de este error:

ErrorSQL

Y aquí fue donde me enteré de algo nuevo sobre SQL Azure! que sin ser un experto debía saber, pues en la documentación inicial del producto lo dice: “Windows Azure SQL Database does not support tables without clustered indexes” entonces la instrucción DropPrimaryKey se vuelve invalida y mi migración ya no sirve aquí :(.

La solución es un poco más engorrosa ahora, hay que crear una nueva tabla con la estructura deseada, copiar los datos a esta, borrar la tabla inicial y renombrar la nueva tabla con el nombre correcto, esto en un script de SQL y ejecutarlo con el método SQL(string). Algo de la forma:

            const string sql = @"
EXECUTE sp_rename N'[PK_dbo.MyTable]', N'[PK_dbo.MyTale_old]',  'OBJECT'
CREATE TABLE [dbo].[Temp_MyTable](
 [Id] [bigint] NOT NULL, 
 [Foo] [nvarchar](max) NULL,
 CONSTRAINT [PK_dbo.MyTable] PRIMARY KEY CLUSTERED 
(
 [Id] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
)
INSERT INTO dbo.[Temp_MyTable] (Id, [Foo])
SELECT [Id]
      ,[Foo]
  FROM [dbo].[MyTable]

drop table dbo.[MyTable]

EXECUTE sp_rename N'Temp_MyTable', N'MyTable', 'OBJECT'";

            Sql(sql);

Esto por supuesto que no salió de mi conocimiento, aquí  y aquí algo documentación de la comunidad 🙂

Espero sea de utilidad.

Hasta el próximo post.

[Windows Azure] Tables without a clustered index are not supported in this version of SQL Server – Entity Framework Migration

[Entity Framework] Sobre Data Annotations

Hola, ya llevaba tiempo sin escribir en esta serie en la que intento cubrir aspectos sobre todas la versiones de este potente ORM. En el último post  que alcance escribir aun tocaba el tema de las convenciones que debemos respetar para que el Entity Framework nos entienda lo que queremos hacer, pero si dado el caso no queremos extender uno de estos comportamientos ¿como lo logramos? Pues bien, para eso llegaron los Data Annotations. Estos son atributos que podemos usar para definir información adicional a nuestras clases de entidad, con estas podemos sobrescribir el comportamiento por defecto de las convenciones de entity framework.

Para poder usar Data Annotations referenciamos la librería System.ComponentModel.DataAnnotations

Ejemplos.

–  Llave primaria.

Como vimos en el primer post sobre codefirst vimos que Entity Framework tomara como llave primaria aquella propiedad que lleve como nombre o que en su nombre tenga el sufijo “Id” o “ID”, y es muy conveniente, pues no se me ocurriría una llave primaria sin esta convención, pero dado el caso yo quisiera que mi tabla Pais, su llave primaria llevara otro nombre, como por ejemplo PaisLlavePrimaria solo debemos usar el Atributo [Key], así:

    public class Pais
    {
        [Key]
        public int PaisLlavePrimaria { getset; }
        public string Nombre { getset; }
    }

–  Configurando tipo Identity de los tipos numéricos

Por defecto cuando Entity Framework nos genera una llave primaria la deja como Identity incremento uno. Para sobrescribir este comportamiento basta con usar el atributo DatabaseGenerated y seleccionar una de las opciones  del enumerador DatabaseGeneratedOption, así:

    public class Pais
    {
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { getset; }
        public string Nombre { getset; }
    }

–  Campo obligatorio

Basta con decorarlo con Required, así:

    public class Pais
    {
        public int Id { getset; }
        [Required]
        public string Nombre { getset; }
    }

– Longitud del campo.

Para definir la longitud de un campo basta con decorarlo con el atributo MaxLength, así:

    public class Pais
    {
        public int Id { getset; }
        [MaxLength(30)]
        public string Nombre { getset; }
    }

– Ignorar una propiedad

Si tenemos una propiedad que no deseamos que se envie como columna la base de datos, basta con decorarla con el atributo NotMapped, así:

    public class Pais
    {
        public int Id { getset; }
        public string Nombre { getset; }
        [NotMapped]
        public int Poblacion { getset; }
    }

– Mapear una propiedad a una columna con distinto nombre

Si en nuestro modelo conceptual trabajamos nombres que no nos gustaría enviar de esa forma a la base de datos, basta con emplear el atributo Column  y especificar el nombre, este nombre será con el que quede nuestra columna en la base de datos, así:

    public class Pais
    {
        public int Id { getset; }
        [Column("NombrePais")]
        public string Nombre { getset; }
    }

Bien, estos son algunos de los atributos de lo que podemos echar mano cuando deseemos sobrescribir alguna de las convenciones que maneja por defecto Entity Framework

Hasta el próximo post.

[Entity Framework] Sobre Data Annotations

[How To][Code First] Definir la base de datos del proyecto.

Hola, por defecto cuando trabajamos con CodeFirst este usa .\ SQLEXPRESS para hospedar la base de datos, y la creara con el nombre definido en nuestra clase de contexto. Pero si deseamos cambiar este comportamiento ¿que debemos hacer? bien, en este post enseñare como usar aun base de datos Existente, como crear una nueva y como usar dos Bases de datos en “simultaneo”.

Dadas las entidades:

    public class Pais
    {
        public int Id { get; set; }
        public string Nombre { get; set; }
    }

    public class Ciudad
    {
        public int Id { get; set; }
        public int PaisId { get; set; }
        public virtual ICollection Pais { get; set; }
        public string Nombre { get; set; }
    }

Y dado el contexto:

    public class DemosCodeFirstEntities : DbContext
    {
        public DbSet Pais { get; set; }

        public DbSet Ciudad { get; set; }
    }

– Como usar una base de datos existente.

Para emplear una base de datos existente, nos bastara con entender la convención de connectionstring, que nos dice que se empleara la cadena de conexión que use el mismo nombre del contexto. Entonces podemos emplear una connectionstring de la forma:

  
    connectionString="Data Source=.;Initial Catalog=EFDemo;Persist Security Info=True;User ID=sa;Password=*****"
         providerName="System.Data.SqlClient" />

Si ejecutamos el siguente codigo:

            using (var context = new Contexto.DemosCodeFirstEntities())
            {
                context.Pais.Add(new Pais { Nombre = "Argelia" });
                context.SaveChanges();
            }

Veremos que CodeFirst ha empleado la base de datos que le hemos indicado en el ConnectionString, solo por haber usado la convención de cadena de conexión.

– Como crear una nueva base de datos.

Para crear una nueva base de datos, basta con reusar la cadena de conexión del ejemplo anterior, pero en lugar de definir un catalogo existente, vamos a emplear un catalogo nuevo, así:

  <connectionStrings>
    <add name="DemosCodeFirstEntities"
         connectionString="Data Source=.;Initial Catalog=CreadoNuevo;Persist Security Info=True;User ID=sa;Password=****"
         providerName="System.Data.SqlClient" />
  </connectionStrings>

Al ejecutar y usar el contexto si revisamos, veremos que se ha creado un nuevo catalogo con las tablas que hemos definido y una adicional, la EdmMetadata este es empleado por EF para saber que el modelo fue mapeado correctamente y es compatible con la base de datos. Si modificáramos entonces el modelo agregando una tabla, obtendríamos una excepción y deberíamos eliminar y crear nuevamente la base de datos. Sobre como evitar este comportamiento, lo veremos en un post siguiente.

– Como usar dos bases de datos.

Para poder emplear dos bases de datos, una para test y otra para producción por ejemplo, debemos usar dos connectionstring con distinto nombre y agregar un constructor al contexto que nos permita usarlo,  así:

App.Config:

    DemosCodeFirstEntitiesTest"
           connectionString="Data Source=.;Initial Catalog=BaseTest;Persist Security Info=True;User ID=sa;Password=****"
           providerName="System.Data.SqlClient" />

El Conetxto:

    public class DemosCodeFirstEntities : DbContext
    {
        public DemosCodeFirstEntities(string connectionString)
            : base(connectionString)
        {

        }

        public DbSet Pais { get; set; }

        public DbSet Ciudad { get; set; }
    }

Si compilamos obtendremos un error, esto se debe a que este comportamiento no pertenece al ensamblado EntityFramework, debemos agregar una referencia a System.Data.Entity. Y del lado de quien lo consume, pasamos el nombre de la cadena de conexión que deseamos usar:

            using (var context = new Contexto.DemosCodeFirstEntities("DemosCodeFirstEntitiesTest"))
            {
                context.Pais.Add(new Pais { Nombre = "Italia" });
                context.SaveChanges();
            }

Como ven entendiendo y usando las convenciones que maneja code first podemos trabajar de manera muy flexible.

Espero les sea de utilidad.

Hasta el próximo post.

[How To][Code First] Definir la base de datos del proyecto.

[Entity framework 4.1] Sobre Code First – DBContext y BDSet

Hola, en la anterior entrada ya vimos lo que trae [traía] de “nuevo”  esta versión del Entity Framework, allí vimos que este release permite el patrón de desarrollo de Code First, pero ¿qué es realmente? ¿Cambia mucho?… bueno pues en esta entrada tratare de describir a manera de introducción sus principales componentes.

La carpeta entidades contendrá nuestro modelo del dominio,  ¿pero como p##@$ puede ser?… pues resulta que en esta versión y en esta técnica se sigue un concepto denominado, configuración por convención, es decir, ya no tendremos que manejar tantos archivos de configuración, porque si respetamos las convenciones (un contrato entre el framework y el desarrollador :)) el framework nos entenderá perfectamente.

– Convenciones para crear el modelo:

  1. Llave primaria: Cualquier propiedad de que contenga ID o Id en su nombre será reconocida como una llave primaria en la base datos.
  2.  Relaciones – Propiedades de navegación: Una llave foránea se establece denotando una propiedad del mismo tipo que la llave primaria con la siguiente estructura <nombreLlavePrimaria><NombreForanea>. Una propiedad de navegación se establece usando una propiedad virtual ICollection Nombre.
  3. Tipos complejos: Cuando Code First no puede inferir la llave primaria, o esta no es registrada, se crea automáticamente un tipo complejo.

Teniendo presente esto, podemos tener definidas nuestras clases de la forma:

    public class Pais
    {
        public int Id { getset; }
        public string Nombre { getset; }
        public virtual  ICollection<Ciudad> Ciudades { getset; }
    }
    public class Ciudad
    {
        public int Id { getset; }
        public int PaisId { getset; }
        public string Nombre { getset; }
    }

Una vez definidas las entidades vamos a crear un contexto para trabajar, en Code First el contexto lo definimos con una clase que implemente de DbContext, esta es una clase que abstrae muchas de las funcionalidades que veíamos en ObjectContext, si llegáramos a necesitar cualquier cosa de ObjectContext podríamos hacer uso de esta cuando quisiéramos :). DbContext viene a trabajar de la mano con DbSet que viene siendo lo que veíamos (o mejor ni veíamos) como ObjectSet, teniendo esto claro tenemos entonces un contexto definido así:

public class MiContexto : DbContext
{
        public DbSet Paises { get; set; }
        public DbSet Ciudades { get; set; }
}

Con esto ya tenemos definido un contexto con el que podemos empezar a trabajar. Todas las consultas que hagamos y que hagan uso del contexto serán almacenadas en localhost\SQLEXPRESS. Para emplear otro almacén físico de datos bastara con crear un connection string normal [como el que empleábamos con Ado.Net] para reflejar los cambios allí.

En los próximos post veremos casos más complejos.

Hasta el próximo post.

[Entity framework 4.1] Sobre Code First – DBContext y BDSet

[Entity Framework] Entity Framework 4.1

Hola, a la fecha de este post debo aclarar que ya estamos en la versión 4.3 de este ORM de Microsoft, pero en esta seria quería ser exhaustivo con las bases del mismo así que hasta ahora empezare a hablar de EF 4.1 y lo que introdujo de “nuevo” :P.

– Que hay de nuevo:

Para empezar hay que aclarar que hasta la versión 4.0 Entity Framework hacia parte directa del .NET Framework, a partir de esta los siguientes reléase se descargan de forma independiente a través del NugEt Package.

Esta nueva versión trae con sigo un término que abstrae un poco lo que ya conocíamos como ObjectContext, pues viene a implementar el DbContext con el que podremos trabajar DataBaseFirst, ModelFirst y CodeFirst :S

Code First es un nuevo patrón de desarrollo para Entity Framework y centra todo su poder en definir nuestro modelo usando clases que pueden ser mapeadas a una base de datos existente o a una nueva base de datos, definiendo propiedades y configuraciones adicionales haciendo uso de los Data Annotations.

– Limitaciones:

Entre las limitaciones de esta versión se encuentram:

  1. No hay soporte para Querys compilados.
  2. No hay soporte para Mest (Multiple EntitySets per Type). Una caracteristica que poco uso, pero que en ocasiones es útil.
  3. No hay soporte para entidades de seguimiento propio.

– Obteniendo el Entity Framework 4.1:

Como ya se dijo antes este release se encuentra como un nuget, así que su instalación se hace desde aquí. [Si no sabes cómo usar la consola mira este enlace]

Ya veremos en próximas entradas las novedades de esta versión en acción.

Hasta el próximo post.

[Entity Framework] Entity Framework 4.1

[Entity Framework] Model First Parte III – Asociaciones y propiedades de navegación

Hola, en el último post de esta miniserie  de Model First que nos ha servido para entender aun más el Model Designer de VisualStudio 2010 y cada una de las propiedades que nuestras entidades pueden manejar. En esta última hablaremos de dos conceptos, las asociaciones y las propiedades de navegación.

– Asociaciones en un EDMX:

Una asociación define una relación entre dos tipos de entidad. En esta se especifican las entidades que estarán implicadas y su multiplicidad, es decir 1-1, 1-*. Como resultado de una asociación obtendremos una propiedad de navegación en las entidades implicadas

– Como crear una Asociación.

Para crear una asociación basta con ubicarnos en el EMX designer, clic contrario Add – Association:

Una vez hecho esto nos saldrá la ventana que nos permitirá crear la asociación:

Las propiedades  que no pide son un nombre para asociación, definir los dos extremos de la asociación, en cada uno se define la multiplicidad, se especifica si se requiere una propiedad de navegación (ya la veremos) y si deseamos agregar una llave foránea en la entidad ciudad y por ultimo en un  se nos describe como quedara la asociación.

Al pulsar ok veremos que el Entity data Model Designer nos ha agregado mágicamente una propiedad de navegación y en la entidad ciudad nos ha generado una propiedad PaisId, además de una flechita que nos representa gráficamente la asociación. Esto se ve de la siguiente forma en nuestro .CSDL:

– Propiedades de navegación:

Como ya vimos una propiedad de navegación es el resultado de una asociación, y proporcionan el medio para navegar entre asociaciones [muy conveniente 😛 ]. Con estas podemos administrar las relaciones en ambos sentidos de la misma, obteniendo un EntityReference si la relación es uno a uno o un EntityCollection si la multiplicidad es a varios.

Por ejemplo la asociación de Pais hacia Ciudad es de uno a varios, y si miramos en el código veremos algo como lo siguiente:

Un punto a tener en cuenta en el momento de emplear propiedades de navegación es el uso de Lazy Loading, se hablo al respecto en esta serie en este post

– Excepciones a su uso:

Mucha gente prefiere no usar propiedades de navegación en sus modelos conceptuales de Entity Framework, y no es para menos 🙂 da miedo que se traiga mas información de la que requerimos en una consulta al tener objetos de otra entidad [problema que se soluciona con el patrón Lazy Load], pero si es tu caso quitarlos, no pasa nada!! 😀 el proyecto compila y anda, solo que te privaras de hacer cosas como la siguiente:

static void Main(string[] args)
{
            using (ModeloContainer context = new ModeloContainer())
            {
                Ciudad ciudad = new Ciudad();
                ciudad.Nombre = "Bogota";
                Pais pais = new Pais();
                pais.Nombre = "Colombia";
                pais.Ciudad.Add(ciudad);
                context.Pais.AddObject(pais);
                context.SaveChanges();
            }
}

Espero les sea de utilidad.

Hasta el próximo post.

[Entity Framework] Model First Parte III – Asociaciones y propiedades de navegación