Sunday, May 30, 2010

Renaming Columns when using Code Only in EF 4.0

The Code-Only feature in Entity Framework 4.0 is currently in CTP 3. Although I’ve been using it a fair bit, one particular pain point for me at  the moment relates to the limited support provided to fine-tune the generated database table columns mapping.

When using Code-Only, one uses a fluent interface to define how your entities (typically POCO classes) should map to a database schema.  For example, you could use the following extract to specify that the Description property in your entity should be mapped in the database as a column with name Description and of type VARCHAR(100) NOT NULL

  1. Property(u => u.Description)
  2.     .IsRequired()
  3.     .IsVariableLength()
  4.     .IsNotUnicode()
  5.     .HasMaxLength(100);

This is cool – I think the above syntax is practical and easy to understand. Even better is that one could generate a complete database schema purely from mapping your domain objects (entity classes). Unfortunately, however, the current CTP 3 release is of very limited (or no) help when it comes to doing the following:

  1. Changing the conventions used by EF when generating the corresponding column names for properties. For example, if you have a Customer entity class with primary key property named Id, then EF will by default always map/generate the property to a column named Id – there’s no way to override this default behaviour to, for example, map to CustomerId instead. Foreign key columns will typically be named xxx_id – though what if you wanted xxxId instead? It would be nice if one could specify a template of column naming conventions – something that would promote the DRY principle.
  2. You can override the column name for your properties, however you must do so for ALL your properties – not merely a subset of them.
  3. There doesn’t seem to be a way to change the ordering of generated columns – worse even, when generating a schema from your domain model, one will often find the primary key column not being the first column in the generated tables. For now, I suspect your best option would be to run a SQL script after schema creation to re-order columns to your liking.

Now obviously I hope that a future Code-Only release would simply provide enhancement to their current fluent interface to address the abovementioned shortcomings; in the mean time, I thought I’d explore some alternatives.

Items 1 and 2 above could be overcome by using the following code block to explicitly specify the column name of each property (here I’m obviously assuming Type Per Hierarchy mapping but that’s not really relevant to the source problem):

  1. MapSingleType(t => new
  2.     {
  3.         CompanyDivisionID = t.Id,
  4.         CompanyDivisionName = t.Name,
  5.         t.Description,
  6.         t.IsActive
  7.     });

Note, by using the anonymous class,

  1. The property Id will be mapped to a column named CompanyDivisionID
  2. If no alternate name is specified, then EF will simply map the name as per default; e.g. the Description and IsActive properties will be mapped to the columns with names Description and IsActive, respectively.

Obviously, having to explicitly mention all your columns (for all your entities) in the mapping so that you can override the default generated column naming convention is tedious at best. I suspect code generation is probably the best interim solution here. Now before you get all excited – I haven’t nearly come up with a complete solution (personally, I think you should rather wait for the next Code-Only release); but at least I can help you get there…or make you learn something in the process…I know I have.

Using LINQ Expressions to Generate Code

For the code generation – you could probably (quite successfully) use T4; however I thought I’d try something that I haven’t had much chance playing around with yet…generating code via LINQ Expressions.

Basically, LINQ introduces a new approach to generate code that is a bit more high level that the IL-based Emit technique. LINQ introduces a runtime representation for expressions which you could think of this as a language-neutral abstract syntax tree that can easily compiled into IL.

Before showing how one could address this problem using Expressions, first realise that the EF provides a different syntax than specified above that is a bit more suited to dynamic code generation:

  1. MapSingleType(t => EntityMap.Row(
  2.     EntityMap.Column(t.Id, "CompanyDivisionID"),
  3.     EntityMap.Column(t.Name, "CompanyDivisionName"),
  4.     EntityMap.Column(t.Description),
  5.     EntityMap.Column(t.IsActive)));

To generate this code dynamically at run time using LINQ Expressions, we need to dynamically build up an expression tree that represents the Lambda function above. The following code snippet shows a simple implementation of how this could be done:

  1. PropertyInfo[] properties = null; //todo: use reflection to get entity's properties...
  2. ParameterExpression tParam = Expression.Parameter(typeof(TEntity), "t");
  3.             
  4. var allEntityMemberExpressions = new List<Expression>();
  5. foreach (var p in properties)
  6. {
  7.     ConstantExpression pName = Expression.Constant(p.Name, typeof(string));
  8.     var member = Expression.Convert(Expression.MakeMemberAccess(tParam, p), typeof (object));
  9.     var entityMemberExpression = Expression.Call(typeof (EntityMap), "Column", null, member, pName);
  10.     allEntityMemberExpressions.Add(entityMemberExpression);
  11. }
  12.  
  13. // convert allEntityMemberExpressions into an array of expressions
  14. // and call EntityMap.Row(EntityMapColumn[])
  15. var entityMapRows = Expression.Call(
  16.     typeof(EntityMap), "Row", null,
  17.     Expression.NewArrayInit(
  18.         typeof(EntityMapColumn),
  19.         allEntityMemberExpressions));
  20.  
  21. MapSingleType(
  22.     // create the lambda: t => EntityMapRow(...)
  23.     Expression.Lambda<Func<TEntity, object>>(
  24.         entityMapRows,
  25.         new ParameterExpression[] { tParam }));
  26.         }
  27.     }

For the most part, hopefully this is self-explanatory – nonetheless, a few explanations:

  1. Line 7: We create a constant expression to represent the column name we want to give our property
  2. Line 8: Expression.MakeMemberAccess with create the expression that represents t.Property. However, some of our properties are likely primitive types (e.g. int) hence we explicitly cast it to type object to avoid a runtime exception in line 9 stating that the static EntityMap.Column(object, string) method couldn’t be resolved.
  3. Line 17: Although we have a collection of expressions – each representing a property/column mapping, we need to convert this to an array of expressions due to the signature of the static EntityMapColumn.Row(Expression[]) method. This is achieved via the Expession.NewArrayInit method.

Anyway, unless you’re a computer, this code is certainly more readable than IL Emit code. This might not necessarily be the best way to address my original problem – but I’ve learned something new today that I’m sure I’m going to find useful in the future…

No comments: