General constraints and type evaluation of an expression
This is based on a previous question I had: EF Code. First implemented the interface property
I have something like this.
interface IKeywordedEntity
{
ICollection<Keyword> Keywords { get; }
}
class Foo : EntityBase, IKeywordedEntity
{
public virtual ICollection<Keyword> Keywords { get { ... } }
}
The details are EntityBase
not important.
I originally wrote this extension method for storing DRY stuff:
public static void WithKeywords<TEntityType>(this EntityTypeConfiguration<TEntityType>
entityTypeConfiguration) where TEntityType : EntityBase, IKeywordedEntity
{
entityTypeConfiguration.HasMany(e => e.Keywords).WithMany();
}
I would call it like this:
modelBuilder.Entity<Foo>.WithKeywords();
However, the Entity Framework or the C # compiler treats e
the lambda as IKeywordedEntity
not TEntityType
. This spawns the Entity Framework.
Knowing this, I experimented with manually writing lambda Expression
for both HasMany
. I came up with the following:
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 now correctly shows me the combination of EntityBase
and IKeywordedEntity
and and tells me that e
- TEntityType
.
My question is, why does a passed / compiled expression tree from a lambda treat e
like IKeywordedEntity
and not TEntityType
?
source to share
I'm not an expert on expressions and Lambdas, but this is what I think is going on.
When we just specify a lambda to convert it to an expression tree, the compiler does the job of converting things to a tree. In the body of the method, all the compiler can see is IKeyworkedEntity.Keywords
not TEntityType.Keywords
and it uses the explicit property name ie uses the following
var rootExpression = Expression.Parameter(typeof (IKeyworkedEntity));
var expression = Expression.Property(rootExpression, "Keywords");
This is causing the problem we see. Now, if we build the expression tree ourselves, we know that TEntityType
there is a property in, and we talk about it in the tree. This leads to migration as it sees the property on the class and not on the interface.
source to share