[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.

Anuncios
[Entity Framework] Encapsular las Fk de las entidades

Un comentario en “[Entity Framework] Encapsular las Fk de las entidades

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s