【问题标题】:How do I exclude Enum properties with EF4/CTP5 fluent mappings in a generic fashion?如何以通用方式排除具有 EF4/CTP5 流畅映射的枚举属性?
【发布时间】:2011-03-13 05:35:05
【问题描述】:

我只是深入研究将代码优先与 CTP5 一起用于实体框架。为了减轻配置数据库映射的痛苦,我自己创建了一个小助手类作为任何自定义映射的基类,但我无法弄清楚如何使方法中的最后一条语句起作用。

我正在查找 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”上的一个属性)使它不仅可以编译而且可以工作? :o)

我希望有人有一个聪明的技巧来解决这个问题。欢迎使用替代解决方案,但请记住,整个想法是避免必须为每个枚举属性调用 Map.Ignore。

【问题讨论】:

  • 我仍在寻找解决此问题的有效解决方案,因为下面给出的答案只是一个指向方向的指针,但并未涉及构建正确的表达方式。

标签: c# entity-framework code-first entity-framework-ctp5 ef-code-first


【解决方案1】:

这使用标准的 .NET 反射库,但我相信您可以根据需要将其转换为 fastflect。我已经向 AbstractMappingProvider 添加了一个 Ignore 方法,以便我们可以使用 MakeGenericMethod() 在运行时解析属性的泛型类型,以便将我们当时拥有的 Lambda 转换为正确的 Expression 类型。很可能有更好/更快/更简单的方法来做到这一点。

作为旁注,我相当肯定,在代码优先发布候选(实体框架 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);
    }
}

【讨论】:

  • 哇,这正是我所需要的。事实上,您的附注可能已经足够了(不敢相信我只是假设它会在看到枚举属性时中断),但我也很高兴看到我应该写的内容:)
【解决方案2】:

问题在于 P 是一个 PropertyInfo 对象。该类具有有关属性的元数据,但对分配给各种对象的属性的值一无所知。您必须手动创建一个 Expression 对象,以通过利用可以从 P 获得的属性名称传递给 Ignore 方法。

【讨论】:

  • 您能否用代码示例扩展该解释?
  • @Morten,抱歉,我不太了解如何使用 Expressions...只要知道这就是您需要的 :)
  • 混蛋,因为这也不完全是我的强项。我可以看到它需要一个 Expression> 但如何从我的成分中创建它... :) 希望其他人可以对此有所了解。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-07-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多