Как исключить свойства Enum с помощью беглых сопоставлений EF4/CTP5 общим способом?

Я просто погружаюсь в использование code-first с CTP5 для Entity Framework. Чтобы облегчить настройку сопоставлений базы данных, я создал себе небольшой вспомогательный класс, который будет служить базовым классом для любых пользовательских сопоставлений, но я не могу понять, как заставить работать последний оператор в методе.

Я нахожу все общедоступные свойства типа Enum, а затем вызываю метод .Ignore для каждого свойства, найденного в данном типе. Код использует Fasterflect для отражения и небольшой помощник ForEach, но должно быть довольно очевидно, что делает код.

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 ) );
    }
}

Выполнение вышеуказанного дает мне следующую ошибку:

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'.

Как я могу использовать «p» (который на самом деле является свойством «e»), чтобы это не только компилировалось, но и работало? :о)

Я надеюсь, что у кого-то есть хитрый трюк, чтобы решить эту проблему. Приветствуются альтернативные решения, но помните, что вся идея состоит в том, чтобы избежать вызова Map.Ignore для каждого свойства перечисления.


person Morten Mertner    schedule 13.03.2011    source источник
comment
Я все еще ищу рабочее решение этой проблемы, поскольку ответ, приведенный ниже, просто указывает направление, в котором нужно двигаться, но не вникает в мельчайшие детали построения правильного выражения.   -  person Morten Mertner    schedule 14.03.2011


Ответы (2)


При этом используются стандартные библиотеки отражения .NET, но я уверен, что вы можете преобразовать их в более быстрые, если хотите. Я добавил метод Ignore в AbstractMappingProvider, чтобы мы могли использовать MakeGenericMethod() для разрешения универсального типа свойства во время выполнения, чтобы преобразовать Lambda, который у нас есть в этот момент, к правильному типу Expression. Вполне может быть лучший/быстрый/простой способ сделать это.

В качестве побочного примечания я совершенно уверен, что в этом первом кандидате на выпуск кода (часть кандидата на выпуск Entity Framework 4.1) перечисления уже игнорируются по умолчанию при сопоставлении по соглашению.

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);
    }
}
person Steve Willcock    schedule 17.03.2011
comment
вау, это сделало именно то, что мне нужно. На самом деле, вашего примечания могло быть достаточно (не могу поверить, что я просто предположил, что оно сломается, увидев свойство enum), но я очень доволен тем, что увидел то, что должен был написать :) - person Morten Mertner; 18.03.2011

Проблема в том, что P является объектом PropertyInfo. Этот класс имеет метаданные о свойстве, но ничего не знает о значениях, присвоенных свойству различных объектов. Вам придется вручную создать объект Expression для передачи в метод Ignore, используя имя свойства, которое вы можете получить от P.

person Robert Levy    schedule 13.03.2011
comment
Не могли бы вы расширить это объяснение с помощью примера кода? - person Morten Mertner; 13.03.2011
comment
@Morten, извини, я мало знаю о работе с выражениями ... достаточно, чтобы знать, что это то, что тебе здесь нужно :) - person Robert Levy; 13.03.2011
comment
Жуки, так как это тоже не совсем моя сильная сторона. Я вижу, что ему требуется Expression‹Func‹T,TProperty››, но как создать его из моих ингредиентов... :) Надеюсь, кто-то еще сможет пролить на это больше света. - person Morten Mertner; 13.03.2011