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:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.Property(p => p.FirstName)
.HasMaxLength(200);
}
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.
/// <summary>
/// Extension method for map private properties
/// <example>
/// modelBuilder.Entity{Customer}()
/// .Property{Customer,int}("Age")
/// .IsOptional()
/// </example>
/// </summary>
/// <typeparam name="TEntityType">The type of entity to map</typeparam>
/// <typeparam name="KProperty">The type of private property to map</typeparam>
/// <param name="entityConfiguration">Asociated EntityTypeConfiguration</param>
/// <param name="propertyName">The name of private property</param>
/// <returns>A PrimitivePropertyConfiguration for this map</returns>
public static PrimitivePropertyConfiguration Property<TEntityType, KProperty>(this EntityTypeConfiguration<TEntityType> entityConfiguration, string propertyName)
where TEntityType : class
where KProperty:struct
{
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<Func<TEntityType, KProperty>> expression = (Expression<Func<TEntityType, KProperty>>)Expression.Lambda(memberExpression, arg);
return entityConfiguration.Property(expression);
}
else
throw new InvalidOperationException("The property not exist");
}
Ahora, gracias a este método extensor podríamos escribir mapeos con el siguiente:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Customer>()
.Property<Customer,int>("PrivateMember");
}
Y ahora que sigue? pues os pido que a los que os guste jugar que hagáis esto mismo para las propiedades de navegación….
¿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.
Saludos
Unai
The klingon note: Debugging? Klingons do not debug. Bugs are good for building character in the user!