泛型约束和表达式类型求值

本文关键字:类型 表达式 约束 泛型 | 更新日期: 2023-09-27 18:29:19

这是基于我之前的一个问题:EF Code First实现的接口属性

我有这样的东西。

interface IKeywordedEntity
{
    ICollection<Keyword> Keywords { get; }
}
class Foo : EntityBase, IKeywordedEntity
{
     public virtual ICollection<Keyword> Keywords { get { ... } }
}

EntityBase的细节并不重要。

最初,我写了这个扩展方法来保持干燥:

public static void WithKeywords<TEntityType>(this EntityTypeConfiguration<TEntityType> 
    entityTypeConfiguration) where TEntityType : EntityBase, IKeywordedEntity
{
    entityTypeConfiguration.HasMany(e => e.Keywords).WithMany();
}

我会这样调用它:

modelBuilder.Entity<Foo>.WithKeywords();

但是,实体框架或C#编译器将lambda中的e视为IKeywordedEntity,而不是TEntityType。这吓坏了实体框架。

知道了这一点,我尝试手动将lambda写为HasManyExpression。我想到了以下内容:

public static void WithKeywords<TEntityType>(this EntityTypeConfiguration<TEntityType> 
    entityTypeConfiguration) where TEntityType : EntityBase, IKeywordedEntity
{
    var rootExpression = Expression.Parameter(typeof (TEntityType));
    var expression = Expression.Property(rootExpression, "Keywords");
    entityTypeConfiguration.HasMany(Expression.Lambda<Func<TEntityType, ICollection<Keyword>>>(expression, rootExpression)).WithMany();
}

现在IntelliSense向我正确显示了EntityBaseIKeywordedEntity的组合,并告诉我e就是TEntityType

我的问题是:为什么lambda中传递/编译的表达式树将e视为IKeywordedEntity,而不是TEntityType

泛型约束和表达式类型求值

我不是表达式和Lambdas方面的专家,但我认为这就是正在发生的事情。

当我们只指定一个lambda将其转换为表达式树时,编译器将完成将内容转换为树的工作。在方法体中,编译器可以看到的是IKeyworkedEntity.Keywords而不是TEntityType.Keywords,并且它使用显式属性名称,即使用以下

var rootExpression = Expression.Parameter(typeof (IKeyworkedEntity));
var expression = Expression.Property(rootExpression, "Keywords");

这就是我们所看到的问题。现在,如果我们自己构建表达式树,我们知道TEntityType上有一个属性,并且我们在树中这样说。这导致迁移完成,因为它在类上而不是在接口上看到属性。