Daniel A. White
Daniel A. White

Reputation: 190945

EF Code First implemented interface property

I have the following model.

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

I want to write an extension method that takes care of the keywords automatically for each of these in OnModelCreating.

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

So I invoke it like this in OnModelCreating.

modelBuilder.Entity<Foo>.WithKeywords();
modelBuilder.Entity<Bar>.WithKeywords();

However, I get the following exception:

The navigation property 'Keywords' is not a declared property on type 'Foo'. Verify that it has not been explicitly excluded from the model and that it is a valid navigation property.

What can I do to allow this extension method to work?

Upvotes: 3

Views: 2098

Answers (3)

Daniel A. White
Daniel A. White

Reputation: 190945

After reading Ladislav's answer, I decided to manually write the expression.

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

Upvotes: 2

Ladislav Mrnka
Ladislav Mrnka

Reputation: 364269

After playing with this myself I think you will not. That is a limitation or bug in EF's fluent API. In your extension method you are not mapping Foo but IKeywordEntity and the mapping gets corrupted. There are two problems - EF doesn't like interfaces but even if you change your design and use abstract class instead of interface it will work for simple properties but it will still not work for navigation properties. At least that is what I get from my own experimenting.

Upvotes: 2

NSGaga
NSGaga

Reputation: 14302

Even though I'm not sure what exactly is your code line of thought here (I get what you're after) - e.g. what's the EntityBase doing and do you have Keywords 'implemented' within it
You'd need to make sure you have the property mapped somehow (and actually implemented)
I think you're going towards a TPC model - which means something like this...

modelBuilder.Entity<Foo>().Map(x =>
{
  x.MapInheritedProperties();
  x.ToTable("Foo");
})

...MapInheritedProperties would sort of 'flatten' the hierarchy for a 'concrete' type.
You'd need a base class, abstract at least to implement the properties and Code First can pick that up.
a somewhat related question...
Entity Framework 4.1 Code First: Get all Entities with a specific base class
In short, I think you're better off using abstract class for this, but you'll still have some work to do - generalizing things around code first is not that easy cause of the many caveats. Also you need to get your inheritance model straight, what you're after.

Upvotes: 0

Related Questions