Asignar relaciones con Entity Framework

En este artículo vamos a ver como podemos establecer relaciones entre entidades con Entity Framework. A menudo las cosas más básicas son las que mas nos confunden, y en el caso de Entity Framework el cambio que supone el dejar de trabajar con “filas” de la base de datos para trabajar con “entidades” es un claro ejemplo.

Cuando trabajamos con tablas de una base de datos, establecemos la relaciones a través de foreign keys – cuando queremos relacionar un registro con otro simplemente asignamos el valor del campo con el valor de la tabla “maestra”. Un ejemplillo chorra:

UPDATE PROVINCIA
SET COD_PAIS = 'ESP'
WHERE COD_PROVINCIA = 1 -- MADRID

Cuando trabajamos con Entity Framework dejamos de trabajar con “tablas” y trabajamos con entidades - instancias objetos. Las relaciones ya no se establecen con campos, sino a través de propiedades.

Estas propiedades que definen las relaciones en nuestro modelo de objetos (modelo conceptual) se denominan propiedades de navegación: son relaciones establecidas entre entidades y dependiendo de la cardinalidad de las relaciones serán colecciones o instancias de la entidad relacionada.

Vamos a hacer un pequeño ejemplo, usamos VS2008 y .NET Framework 3.5 SP1– … aún no he instalado el .NET Framework 4.0 ni Visual Studio 2010. :-(

Lo primero vamos a crear una pequeña base de datos de ejemplo.

create database EFSample
go
use EFSample
go
/*==============================================================*/
/* Table: lista_valor                                           */
/*==============================================================*/
create table lista_valor (
   cod_lista_valor      int                  identity,
   cod_tipo_lista_valor varchar(5)           null,
   valor                varchar(100)         not null,
   valor_enlazado       varchar(100)         null,
   constraint PK_LISTA_VALOR primary key (cod_lista_valor)
)
go

/*==============================================================*/
/* Table: tipo_lista_valor                                      */
/*==============================================================*/
create table tipo_lista_valor (
   cod_tipo_lista_valor varchar(5)           not null,
   descripcion          varchar(100)         not null,
   valor_por_defecto    varchar(100)         null,
   constraint PK_TIPO_LISTA_VALOR primary key (cod_tipo_lista_valor)
)
go

alter table lista_valor
   add constraint FK_LISTA_VA_REFERENCE_TIPO_LIS foreign key (cod_tipo_lista_valor)
      references tipo_lista_valor (cod_tipo_lista_valor)
go

El modelo de base de datos sólo tiene dos tablas:

  • tipo_lista_valor, tabla maestra que contiene los tipos de lista de valor – muchas veces llamadas LOV
  • lista_valor, los valores admitidos para esta lista de valor.

Un ejemplo seria el tipo_lista_valor “TIPO DE TELEFONO” y los posibles valores “MOVIL, FIJO …”

Una vez que hemos creado la base de datos, añadimos un modelo de EF a nuestro proyecto de VS2008 - Model1.edmx. Dejamos el modelo de entidades tal y como lo genera el asistente.

ef1

Ahora vamos a hacer una serie de ejemplos que nos van a mostrar como podemos asignar esas relaciones y crear nuevos datos. Resumiendo mucho tenemos dos formas de asignar estas propiedades – asignando directamente la entidad (con nuevas entidades o cargándola previamente de la base de datos) o estableciendo la EntityKey de la entidad. Ahora veremos como se hace

Establecer la relación con nuevas entidades.

Lo primero que vamos a ver es como podemos crear y relacionar dos entidades que no existen en el modelo de datos.

/*
 Ninguna de las dos entidades existen en la base de datos
 */
static void AddValor()
{
    using (EFSampleEntities ctx = new EFSampleEntities())
    {
        /*El tipo de lista de valor no existe, creamos uno nuevo */
        tipo_lista_valor tipo = new tipo_lista_valor()
                                {
                                    cod_tipo_lista_valor = "TIP01",
                                    descripcion = "Tipo de lista de valor 1",
                                    valor_por_defecto = ""
                                };

        lista_valor valor = new lista_valor();
        valor.tipo_lista_valor = tipo;
        valor.valor = "Valor del tipo 1";
        valor.valor_enlazado = String.Empty;
        ctx.AddTolista_valor(valor);

        /*
         El StateManager realiza dos operaciones:
         * 1. El alta de tipo_lista_valor
         * 2. El alta de lista_valor
         */

        ctx.SaveChanges();
    }
}

Explicamos un poco. Abrimos un contexto de entidades EFSampleEntities(ctx) y creamos una instancia de tipo_lista_valor(tipo)  y de lista_valor(valor). El tipo lista_valor la variable valor - tiene una propiedad de navegación tipo_lista_valor, que permite asignar-obtener la referencia de tipo_lista_valor. en este primer ejemplo asignamos sus propiedades escalares (código, descripción …), establecemos la relación entre ambas entidades: valor.tipo_lista_valor = tipo y le indicamos al contexto que queremos añadir la entidad a lista_valor.

En este caso, tanto el tipo de lista de valor como el valor son nuevos objetos (¡los hemos creado con new!) por lo que al guardar los cambios se crearán en la base de datos un nuevo registro en la tabla tipo_lista_valor y un nuevo registro en lista_valor.

********************************
*    Tabla tipo_lista_valor    *
********************************
cod_tipo_lista_valor descripcion                 valor_por_defecto
-------------------- --------------------------- ------------------
TIP01                Tipo de lista de valor 1   

***************************
*    Tabla lista_valor    *
***************************
cod_lista_valor cod_tipo_lista_valor valor                valor_enlazado
--------------- -------------------- -------------------- ----------------
1               TIP01                Valor del tipo 1    

Establecer la relación consultando la entidad.

Pero este no es el escenario “normal” – lo habitual es que la referencia “padre” – tipo_lista_valor - ya exista en nuestro modelo de datos y que el usuario haga una selección – con un desplegable por ejemplo – y nuestro programa recibiera el dato “TIP01” no la entidad.

En este caso una primera aproximación pasaría por consultar la base de datos – obtener la entidad y establecer la relación.

/*
 El tipo de lista de valor existe.
 */
static void AddValor2() {
    using (EFSampleEntities ctx = new EFSampleEntities())
    {
        /*El tipo de lista de valor no existe, creamos uno nuevo */
        string tipokey = "TIP01";
        tipo_lista_valor tipo = (from tlv in ctx.tipo_lista_valor
                                 where tlv.cod_tipo_lista_valor == tipokey
                                 select tlv).First();

        lista_valor valor = new lista_valor();
        valor.tipo_lista_valor = tipo;
        valor.valor = "Valor (2) del tipo 1";
        valor.valor_enlazado = String.Empty;
        ctx.AddTolista_valor(valor);

        /*
         El StateManager realiza dos operaciones:
         * 1. El alta del registro en lista_valor                
         */

        ctx.SaveChanges();
    }
}

En este caso únicamente disponemos del dato "TIP01" – la clave primaria –, y para obtener la entidad utilizamos una consulta Linq to Entities para obtener una instancia de tipo_lista_valor . Una vez que tenemos ya las dos instancias – tipo y valor – grabamos los datos. Únicamente se realiza una inserción, la del valor.

Esta segunda aproximación es la forma que prefiero usar en la mayoría de ocasiones dado que me permite trabajar de forma completamente tipada, pero tiene un claro inconveniente – realiza una consulta innecesaria a la base de datos. Es un aspecto que tenemos que tener en consideración en entornos donde requiramos de un alto nivel de rendimiento.

Establecer la relación estableciendo la referencia a la entidad.

Por último, y para solucionar el problema anterior – podemos asignar la relación estableciendo la referencia - propiedad EntityReference<T> - como se muestra a continuación.

Primero tenemos que establecer las referencias a los namespaces:

using System.Data;
using System.Data.Objects.DataClasses;

El código quedaría de la  siguiente manera.

/*
 El tipo de lista de valor existe.
 */
static void AddValor3()
{
    using (EFSampleEntities ctx = new EFSampleEntities())
    {
        /*El tipo de lista de valor no existe, creamos uno nuevo */
        string tipokey = "TIP01";

        var referenciaAlTipo = new EntityReference<tipo_lista_valor>()
                                {
                                    EntityKey = new EntityKey("EFSampleEntities.tipo_lista_valor",
                                                                "cod_tipo_lista_valor",
                                                                tipokey)
                                };



        lista_valor valor = new lista_valor();
        valor.tipo_lista_valorReference = referenciaAlTipo;
        valor.valor = "Valor (3) del tipo 1";
        valor.valor_enlazado = String.Empty;
        ctx.AddTolista_valor(valor);

        /*
         El StateManager realiza dos operaciones:
         * 1. El alta del registro en lista_valor                
         */

        ctx.SaveChanges();
    }
}

En lugar de utilizar una consulta Linq y asignar el resultado a la propiedad de navegación, lo que hacemos es crear un objeto de tipo EntityReference<tipo_lista_valor> y asignarla a la propiedad tipo_lista_valorReference.

Este método es más eficaz, pero nos obliga a tener el nombre del tipo como un string en el código, y por lo tanto es susceptible de provocar errores en tiempo de ejecución si cambiamos nuestro modelo de entidades.

Saludos y hasta la proxima,

DJK

Pedro  Herrarte  Sánchez
Asignar relaciones con Entity Framework
Pedro Herrarte Sánchez

Pedro Herrarte, es consultor independiente, ofreciendo servicios de consultoría, análisis, desarrollo y formación. Posee mas de diez años de experiencia trabajando para las principales empresas de España. Es especialista en tecnologías .NET, entornos Web (ASP.NET, ASP.NET MVC,jQuery, HTML5), bases de datos (SQL Server y ORACLE) e integración de sistemas. Es experto en desarrollo (C#, VB.Net, T-SQL, PL/SQL, , ASP, CGI , C, Pro*C, Java, Essbase, Vignette, PowerBuilder y Visual Basic ...) y bases de datos (SQL Server y ORACLE). Pedro es MCP y MAP 2012, es fundador, diseñador y programador de www.devjoker.com..
Fecha de alta:16/09/2010
Última actualizacion:16/09/2010
Visitas totales:10846
Valorar el contenido:
Últimas consultas realizadas en los foros
Últimas preguntas sin contestar en los foros de devjoker.com