How do I exclude Enum properties with EF4/CTP5 fluent mappings in a generic fashion?
I am just diving into the use of code-first with CTP5 for Entity Framework. To ease the pain of configuring the database mappings I've created myself a little helper class to serve as the base class for any custom mappings, but I'm unable to figure out how to make the last statement in the method work.
I am locating all public properties of type Enum and then calling the .Ignore method for every property found on the given type. The code uses Fasterflect for reflection and a little ForEach helper, but it should be fairly obvious what the code does.
internal abstract class AbstractMappingProvider<T> : IMappingProvider where T : class
{
public EntityTypeConfiguration<T> Map { get; private set; }
public virtual void DefineModel( ModelBuilder modelBuilder )
{
Map = modelBuilder.Entity<T>();
Map.ToTable( typeof(T).Name );
typeof(T).Properties( Flags.Public | Flags.Instance )
.Where( p => p.PropertyType.IsEnum )
开发者_如何学编程 .ForEach( p => Map.Ignore( e => p ) );
}
}
Executing the above gives me the following error:
System.InvalidOperationException:
The expression 'e => value(Domain.AbstractMappingProvider`1+<>c__DisplayClass3[Domain.User]).p' is not a valid property expression.
It must be of the form 'e => e.Property'.
How can I use the "p" (which is in fact a property on "e") to make this not just compile but also work? :o)
I'm hoping someone has a clever trick to solve this. Alternatives solutions are welcome, but remember that the whole idea is to avoid having to call Map.Ignore for every enumeration property.
This uses the standard .NET reflection libraries but I'm sure you can convert it to fasterflect if you like. I've added an Ignore method to the AbstractMappingProvider so that we can use MakeGenericMethod() to resolve the generic type for the property at runtime in order to cast the Lambda we have at that point to the correct Expression type. There could well be a better/faster/easier way to do this.
As a side note, I'm fairly sure that in that the code first Release Candidate (part of the Entity Framework 4.1 release candidate), enums are already ignored by default when mapping by convention.
internal abstract class AbstractMappingProvider<T> : IMappingProvider where T : class
{
public EntityTypeConfiguration<T> Map { get; private set; }
public virtual void DefineModel(DbModelBuilder modelBuilder)
{
Map = modelBuilder.Entity<T>();
Map.ToTable(typeof(T).Name);
var properties = typeof(T).GetProperties(BindingFlags.Public | BindingFlags.Instance).Where(p => p.PropertyType.IsEnum);
var parameterExpression = Expression.Parameter(typeof(T), "e");
foreach (var propertyInfo in properties)
{
// Build up the expression
var propertyExpression = Expression.Property(parameterExpression, propertyInfo);
var funcType = typeof (Func<,>).MakeGenericType(typeof (T), propertyInfo.PropertyType);
var ignoreExpression = Expression.Lambda(funcType, propertyExpression, new[] {parameterExpression});
// Call the generic Ignore method on this class, passing in the expression
var ignoreMethod = this.GetType().GetMethod("Ignore");
var genericIgnoreMethod = ignoreMethod.MakeGenericMethod(propertyInfo.PropertyType);
genericIgnoreMethod.Invoke(this, new object[]{ignoreExpression});
}
}
public void Ignore<TPropertyType>(LambdaExpression lambdaExpression)
{
var expression = (Expression<Func<T, TPropertyType>>) lambdaExpression;
Map.Ignore(expression);
}
}
The problems is that P is a PropertyInfo object. That class has metadata about the property but knows nothing about values assigned to the property on various objects. You will have to manually create an Expression object to pass in to the Ignore method based by leveraging the property name that you can get from P.
精彩评论