Mapping private properties with EF 41 RC and Fluent mapping

EF 4.1 is now in RC phase and as a NHibernate user I’m curious to check the fluent API to map entities to database. One of the feature that I and Andrea miss most is the possibility to map private properties with fluent interface. It seems strange to map private properties at once, but it can be useful in DDD. Suppose you have these simple classes.

image

Figure 1: A really simple domain

We have two things to notice, the first is that the Category class has a private property called PrivateDetails, and the other is that the Products collection is protected, and you can add products from the outside thanks to the AddProduct() method.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Category
{
public Int32 CategoryId { get; set; }
public string Name { get; set; }
private String PrivateDetails { get; set; }
 
private ICollection<Product> _products;
protected virtual ICollection<Product> Products
{
get { return _products ?? (_products = new HashSet<Product>()); }
set { _products = value; }
}
 
public void AddProduct(Product p)
{
Products.Add(p);
}
 
public void SetDetails(String details)
{
PrivateDetails = details;
}
}

The idea behind this is that you should access only the AGGREGATE roots, not manipulating directly the collection of Products, this forces the user of the class to use specific methods. Now a problem arise, how we can map this class with EF 4.1 fluent interface? The problem is generated from the Fluent interface, that permits only to specify properties with Lambda.

image

Figure 2: The HasMany() method accepts an Expression

As you can see if I specify that CategoryId is mapped to an Identity database column with the instruction

Property(c => c.CategoryId)

this technique is known as static reflection and is really useful in such scenarios, but … now I could not use the HasMany() methods to map a protected property.

image

Figure 3: How can I map a protected property if I could not use in a lambda?

This problem derives only from the Fluent Interface, because EF is internally capable to map private members of objects, then we can use a little trick. I want to be able to write code like this

1
this.HasMany<Category, Product>("Products");

This would solve all our problems, because with this statement I’m asking EF to map a collectino called Products. Fortunately writing such an extension method is quite simple, it is just a bunch of Expressions

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
public static ManyNavigationPropertyConfiguration<T, U> HasMany<T, U>(
this EntityTypeConfiguration<T> mapper,
String propertyName)
where T : class
where U : class
{
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
 
PropertyInfo pi = type.GetProperty(propertyName,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
 
LambdaExpression lambda = Expression.Lambda(expr, arg);
 
Expression<Func<T, ICollection<U>>> expression =
(Expression<Func<T, ICollection<U>>>)lambda;
return mapper.HasMany(expression);
 
}

This code seems complex but it is rather simple. It creates a parameter expression of the same type of the object, then grab a reference to the property from its name with reflection and with the PropertyInfo creates an Expression.Property. This expression (created in line 13) is the equivalent of c => c.Products lambda and it can be passed to the Expression.Lambda to create an Expression<Func<T, ICollection<U>>> object, expected from the HasMany() method.

With the same technique I can write an extension method that maps a private string property.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public static StringPropertyConfiguration PropertyStr<T>(
this EntityTypeConfiguration<T> mapper,
String propertyName) where T : class
{
Type type = typeof(T);
ParameterExpression arg = Expression.Parameter(type, "x");
Expression expr = arg;
 
PropertyInfo pi = type.GetProperty(propertyName,
BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
expr = Expression.Property(expr, pi);
LambdaExpression lambda = Expression.Lambda(expr, arg);
 
Expression<Func<T, String>> expression = (Expression<Func<T, string>>) lambda;
return mapper.Property(expression);
}

Thanks to those two methods now I’m able to write this mapping for the category class.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class CategoryMapping : EntityTypeConfiguration<Category>
{
public CategoryMapping()
{
Property(c => c.CategoryId).HasDatabaseGeneratedOption(
DatabaseGeneratedOption.Identity);
 
this.PropertyStr("PrivateDetails").HasColumnName("Details");
 
this.HasMany<Category, Product>("Products");
ToTable("Category");
}
}

As you can see I’m able to map the PrivateDetails property and I can choose column name, and the Products property with no problem. Now I can use my model

1
2
3
4
5
6
var food = new Category { Name = "Foods" };
food.SetDetails("Details");
db.Categories.Add(food);
Product p = new Product() {Name = "Beer"};
food.AddProduct(p);
int recordsAffected = db.SaveChanges();

As you can ses I’m able to add product without the need to directly access the collection, and I can set a private property through a method (not so useful technique, but just to show that mapping private properties works). Running the sample I got

image

Figure 4: The fact that three entities were saved confirmed me that the mapping of the protected collection works.

I can verify that everything is ok thanks to EFProfiler.

image

Figure 5: Thanks to EFProf I can verify that products are correctly saved and linked to the category object

EF 4.1 Code first finally gives to EF the direction towards a real ORM, usable in DDD scenario.

alk.

Code is here.