EF 4.1 Code First, ¿map private members?

 

 

Bueno, lo prometido es deuda, y como a muchos “amiguetes” les prometí no abandonar la serie sobre mapping de EF 4.1 puest aquí sigo al ataque. Se que alguno me recordará que también tengo pendiente escribir el apéndice sobre EF 4.1 que permita completar el libro de EF que acabo de publicar, con respecto a esto, me gustaría, aprovechando la coyuntura hacer dos anotaciones. La primera es que más que un apéndice-capítulo se está convirtiendo casi en un libro aparte, el caso es que son tantas cosas para contar que uno va degenerando en páginas y páginas, espero que provechosas. Le segunda anotación que me gustaría hacer es que trataré por todos los medios que aquellos que habéis comprado el libro podáis acceder a este apendice/nuevo libro de una forma “nada” costosa.

 

Venga al tajo, la idea con este y algún nuevo post, es ir ampliando la información sobre mapping o bien creando código que nos ayude con este trabajo. La idea de hacerlo un poco más cañero de lo normal viene del hecho que últimamente he leído algún post como este de José Ramaniello que me ha parecido bastante un poco sesgado  y, por lo tanto, aquí estamos.

 

En ocasiones podemos encontrarnos escenarios dónde necesitemos que nuestras entidades puedan mapear una propiedad no pública, si, has escuchado bien. El ejemplo más prototípico de esto es por ejemplo en un agregado raiz ocultar el acceso directo a una navegación, de tal forma que, las operaciones de agregar entidades al agregado se puedan hacer por medio de un método del mismo y no directamente por una colección. Cierto es, que por defecto EF 4.1 no nos permite el mapeo de elementos privados, por lo menos en la superficie del API, sin embargo, rebuscando un poco y no conformándose es medianamente sencillo ver que tenemos solución a este problema. Realmente, cuando mapeamos una propiedad  escribimos algo similar a lo siguiente:

Pues bien, el método Property, que admite una expresión lambda, realmente no tiene porque circunscribirse a propiedades públicas, es decir, podríamos crear, gracias a las capacidades de los árboles de expressiones, una firma idéntica a la solicitada por este método sin necesidad de que el acceso fuera a un elemento público, ¿como? Pues bien, en el siguiente método extensor podéis ver como generar un nuevo método Property con esta capacidad.

 

Ahora, gracias a este método extensor podríamos escribir mapeos con el siguiente:

 

¿Os ha gustado? Pues esto, y muchas cosas más ya están la cocina del nuevo apéndice-capitulo, perdón por la promoción :-/, algunas, para abrir bocar y quizás, para cerrar alguna, las iremos poniendo en sucesivas entradas.

23 comentarios en “EF 4.1 Code First, ¿map private members?”

  1. Estoy intentando hacer lo mismo pero para que acepte una propiedad de tipo string, pero no doy ocn la tecla…
    Por qué exige esta línea: return entityConfiguration.Property(expression);

    ¿Que en la expression el parámetro de tipo KProperty sea un struct?

  2. Este metodo extensor es una sobrecarga para retornar PrimitivePropertyConfiguration, si lo quieres con un string deberías hacer lo mismo para un StringPropertyConfiguration y eliminar la restriccion del segundo parámetro genérico….. Si lo necesitas podría enviarte el código..

    saludos
    Unai

  3. Me respondo yo mismo…

    Para mapear propiedades de tipo string privadas, hay que crear una expresión distinta…

    public static StringPropertyConfiguration PrivateStringProperty(this EntityTypeConfiguration entityConfiguration, string propertyName)
    where TEntityType : class
    {

    var propertyInfo = typeof(TEntityType).GetProperty(propertyName, BindingFlags.NonPublic
    |
    BindingFlags.Instance
    |
    BindingFlags.Public);
    if (propertyInfo != null) // if private property exists
    {
    ParameterExpression arg = Expression.Parameter(typeof(TEntityType), “parameterName”);
    MemberExpression memberExpression = Expression.Property((Expression)arg, propertyInfo);

    //Create the expression to map
    Expression> expression = (Expression>)Expression.Lambda(memberExpression, arg);

    return entityConfiguration.Property(expression);
    }
    else
    throw new InvalidOperationException(“The property not exist”);
    }

  4. Has sido más rápido que yo escribiendo!!…Gracias Unai, ya veo por donde van los tiros, la verdad es que estaba buscando este apaño por que lo necesitábamos. Veo que deberemos crear unos cuantos métodos de extensión.

  5. Sergio, si tienes algun mapeo interesante, dificil o alguna casuística que quisieras cubrir y no parece posible coméntamelo. Me vendría bien para el apéndice de libro :-), y de paso, igual sería hasta interesante para un post!…

    Muchas gracias,
    Unai

  6. Hola de nuevo. Gracias por el ofrecimiento, lo voy a aprovechar… 🙂

    Estamos migrando el mapeo de nuestras entidades de EF con STE’s a EF 4.1 con CodeFirst. De momento el problema que nos hemos encontrado es precisamente el que has resuelto con este post, el mapeo de propiedades privadas, en nuestro caso son propiedades que albergan un XML en base de datos, que es el estado de un objeto serializado, y lo que queremos que se publique en la superficie de la clase es el objeto rehidratado no el XML de la base de datos. por eso tenemos una propiedad string privada que se mapea a la base de datos y una propiedad del tipo del objeto serializado. Hasta aquí problema resuelto.

    Lo que nos vendría bien y vamos a trabajar en ello de inmediato, es en conseguir esto con un atributo de mapeo en lugar de con la fluent api. ¿Crees que se puede hacer?

    Todavía no hemos terminado de migrar todas las entidades, es posible que aparezca algún otro problema, en cuyo caso lo compartiré aquí si te parece bien

  7. Por cierto, esperamos con avidez el apéndice de tu libro, ya que hemos comprado un ejemplar (ya compramos uno de la versión anterior por cierto) y estamos un poco huérfanos de CodeFirst…

    Te felicito por los dos libros porque nos han resultado de gran interés para nuestros desarrollos.

  8. Bueno Sergio, gracias por los comentarios, así da gusto hacer las cosas :-). Con respecto a hacer el mapeo de miembros privados con atributos tengo dos inconvenientes. El primero es que desgraciadamente la superficie de convenciones personalizadas se ha quitado de EF RC y por lo tanto no podremos hacer eso por convenciones, configuracion de atributos. La segunda, es personal, a mi no me gustan los mapeos sobre atributos, prefiero separar esto de las entidades en mis EntityTypeConfiguration…

    Saludos
    Unai

  9. Es la segunda vez que dices que mi artículo esta sesgado sin dar ninguna explicación. Voy a tolerar eso cordialmente por que realmente no me interesa.
    No obstante te pido que si conoces alguna forma de solucionar los problemas que yo he mostrado en mi blog me contactes, cualquiera sea el medio.
    Quiero que sepas que yo estoy abierto a tus comentarios y a aprender cosas nuevas, como así también a modificar mi artículo de ser necesario.

  10. Por otro lado el caso de uso que decís existe, aunque poco frecuente. Lo que se hace no es mapear una propiedad privada si no un field.

    Es posible con EF mapear un field en lugar de una propiedad?

    Saludos,

  11. José, digo que esta “sesgado” sin ánimo de confrontación, no entiendo que tiene de malo, básicamente porque cuando se compara, se compara poniendo en la balanza ventajas de uno y de otro. ¿Realmente no encuentras nada de EF que te guste más que NH? No se, por ejemplo la posibilidad de hacer path de inclusion, el tooling, el soporte de sp, hombre, no se, te considero un tipo muy bueno en esto y esto me extraña de alguien como tu, por eso digo que esta “sesgado”… sobre todo cuando se hace un X versus Y.

    Con respecto a tu otra pregunta, tendría que cambiar el arbol de expresion para comprobarlo, pero si me pides hacerlo de cabeza te diría que no porque internamente se buscan getters y setter y la reflexion la hacen con property info y no con member info, de todas formas, me vale como workaorund.

    Para terminar, hombre, la posibilidad en un agregado de no exponer una coleccion de mimbros de este y obligar a pasar por un método para hacer un (addorderline por ejemplo) no es tan poco frecuente ¿no?, y esto es una pregunta NO retorica :-), para que no existan malos rollos 🙂

    Unai

  12. Aunque no lo dije en mi artículo considero que no existe ninguna razón técnica para utilizar hoy en día EntityFramework y al contrario existen muchas para no hacerlo.
    El tooling no me parece importante, por eso yo hice review de CodeFirst no del DSL grafico (NHibernate dispone de varios DSLs graficos) u otro tipo de tool alrededor.
    El soporte de SP es mejor en nhibernate pero como estoy en contra de eso no escribí nada al respecto.. sería un artículo aparte.
    Sobre el agregado y la colección es cierto, pero es un caso diferente al que expones aca. En el caso de la colección, la propiedad tiene un getter publico y no tiene setter. NHibernate en este caso funciona diferente, no solo que no necesitas un extension method, si no que infiere el field (o backing field si es un auto property con setter privado) solo.

    Muchas gracias por tu respuesta.

  13. No tienes que dar gracias por la respuesta, no entiendo porque deberías pero bueno, gracias a ti tb entonces. Entiendo tu postura, pero yo no estoy de acuerdo, o no la comparto, independientemente de que crea o no que NH es más maduro (esto es algo que nunca he negado) si creo que hay escenarios, como los mencionados que resuelve igual o mejor, pero cierto es, esto para otro post. Con respecto a tu primera linea, bueno, es lo que tiene que ninguno tengamos una patente de corso, al final cada uno elije y paga sus consecuencias (durante el desarrollo lógicamente), yo lo utilizo, tengo muchos proyectos en producción con el, y mas que tendré, porque lo considero conveniente hacerlo, y conozco, creo que lo suficiente, nh.. Para mi, y yo tengo derecho a decidir,esto es una carrera de fondo y no un sprint…

    Unai

  14. Por cierto, una acolación porque veo que te ha hecho gracia el tema, mencionar SP no implica que yo esté o no de acuerdo, tendrías facil ver el que NO ( más bien todo lo contrario) leyendo mis post u otros articulos, o mirando la comunidad, es un detalle que salio simplemente escribiendo, como podría haber mencionado otros, pero bueno, si solamente te quieres quedar con eso y por ahí reir un poco, a mi si que me es indiferente… No sé, me parece un poco triste que te quedaras, quisieras quedarte solamente con esto, pero …en fin.

    Unai

  15. Unai, mis mas sinceras disculpas si sobrevalue lo de los StoreProcedure.

    Me interesa la parte que comentas sobre escenarios en donde si vale la pena utilizar EntityFramework, podrías ampliar esto en un comentario o en un futuro artículo? Realmente me interesa mucho este tema.

    No hace falta aclarar que cada uno es libre de decidir y pagar las consecuencias.

    Yo dije que en este momento no encuentro ninguna razón técnica para utilizarlo. Y en mi post también he hablado de razones no técnicas como falta de documentación oficial. Entiendo perfectamente que pueden existir razones no-técnicas para utilizarlo como el hecho de algunas empresas que no quieren utilizar opensource, o proyectos donde solo se permite utilizar cosas de microsoft, o cuestiones de gustos, etc.

    Voy a explayarme un poco más sobre esto. En un momento la misma gente que utiliza y desarrolla NHibernate dijo que la razón técnica para utilizar EntityFramework era que su proveedor de LINQ era mejor (OJO no estoy hablando de usuarios, estoy hablando de gente que arduamente dedica tiempo al proyecto NHibernate -gratis-).

    Hoy en día el proveedor de Linq de NHibernate esta bastante maduro y soporta muchos casos, si bien es prácticamente imposible soportar todos. Pero es realmente esto importante en EntityFramework si las cosas más básicas como customizar el mapeo de un tipo no estan permitidas? De que me sirve poder hacer consultas con LINQ si ni si quiera puedo mapear mis objetos. Por lo tanto, incluso esa razón que era muy fuerte, ya dejo de ser importante.

  16. A ver José, yo acepto tus disculpas, no tengo ningún problema, soy viejo como para que me ofendan este tipo de cosas, aunque cierto es que no me gustó, quizás más porque en esto yo soy el primer “taliban”… pero puesto que ya llevamos unos cuantos comentarios y respuestas, y hay confianza, hubiera estado mejor que me preguntaras directamente que opinaba o leyeras algun otro post, no obstante, ningun problema.

    Con respecto a la petición, pues si, ningun problema, déjame salir de unas guerras, trabajo, proyecto OSS ( si, igual me habéis calificado pronto !) y lo vemos.

    Tema aparte, repito lo que dije en mi primera respuesta, para mi esto es un placer, no una guerra, a mi nadie me paga tampoco, y por lo poco que he leido de ti, me pareces una persona con la que me sentaría con una cerveza en la mano a discutir. Si quieres guardarte mi correo para otra vez o por lo que necesites, unai@plainconcepts.com

    Unai

  17. Ha sido muy instructiva la conversación (puliendo las asperezas).

    Me gustaría que cualquiera de los dos (preferiblemente ambos) desarrollara más el tema de los SP, cuyo rechazo comparto, pero como en toda regla tenemos excepciones, y a mí, como dicen las abuelas: “La primera en la frente”, pues al comenzar un proyecto con Code First me encuentro con la necesidad de eliminar X registros, lo cual tengo que tirar con un SP forzosamente por cuestiones de rendimiento.

    Por cierto, al hilo de esto, otra diferencia importante para mí es en cuanto a las operaciones por lotes como DELETEs o UPDATEs, que en EF esperamos impacientemente (no soy usuario de NH por lo que no entro a valorar lo que este ofrece).

  18. Hola Unai,
    Como pones en tu articulo, he intentado hacer lo mismo con propiedades de navegación, pero no he sido capaz.
    He intentado con RequiredNavigationPropertyConfiguration, con ForeignKeyNavigationPropertyConfiguration, etc.
    Pero no lo consigo.

    saludos

  19. Ruben, la firma debería ser:

    public static ManyNavigationPropertyConfiguration HasMany(this EntityTypeConfiguration entityConfiguration, string navigationPropertyName)
    where TEntityType:class
    where TTargetEntityType:class
    {}

    Ahora, todo igual exceptuando que no llamas a entityConfiguration.Property sino a entityConfiguration.HasMany internamente

    Espero que te ayude
    Unai

  20. Una vez las espadas en alto: en el artículo de José hay unas cuantas características de NH no presentes en EF. ¿Existe release próxima prevista con ellas, o extensión actual, o workaround para solucionarlas con EF?
    Y me refiero a:
    – Generadores de identidades
    – Personalización de mapeo de tipos (aunque el tema de los enumerados es fácil hacerlo en EF)
    – Colecciones de elementos como propiedad (ayudaría a simplificar grafos de agregados)
    – Mapeo de diccionarios

Deja un comentario

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