11

I am trying to use a common EntityTypeConfiguration class to configure the primary key for all of my entities, so that each derived configuration class does not repeat itself. All of my entities implement a common interface IEntity (which says that each entity must have an Id property of type int).

My configuration base class looks like this:

public class EntityConfiguration<TEntity> : EntityTypeConfiguration<TEntity>

    where TEntity : class , IEntity {

    public EntityConfiguration() {

        HasKey( e => e.Id );

        Property( e => e.Id ).HasDatabaseGeneratedOption( DatabaseGeneratedOption.Identity );

    }

}

Each entity then has it's own specific configuration class extending this one like this:

public class CustomerConfiguration : EntityConfiguration<Customer> {

    public CustomerConfiguration() : base() {

        // Entity specific configuration here

    }

}

It compiles fine, but the problem I am having is that at runtime I get the following Exception being raised when EF 4.1 RC tries to create the model:

System.InvalidOperationException was unhandled Message=The key component 'Id' is not a declared property on type 'Customer'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property. Source=EntityFramework

If I change the CustomerConfiguration class to extend from EntityTypeConfiguration<Customer> and repeat the primary key configuration then it works fine, but I lose the ability to share common configuration (DRY principal is the motivation).

  • Am I doing something wrong here?
  • Is there another way to share common configuration between entities?

For reference here are the other classes involved:

public interface IEntity {

    int Id { get; set; }

}

public class Customer : IEntity {

    public virtual int Id { get; set; }

    public virtual string name { get; set; }

}

Thanks!

1
  • I’m voting to close this question because it's about a release candidate, and an obsolete version. Commented Aug 19, 2021 at 11:33

4 Answers 4

12

It looks like these configurations has some problem with interface. It works if you change IEntity to EntityBase:

public class EntityBase
{
    public virtual int Id { get; set; }
}

public class Customer : EntityBase
{
    public virtual string Name { get; set; }
}

public class EntityConfiguration<TEntity> : EntityTypeConfiguration<TEntity>
    where TEntity : EntityBase
{
    public EntityConfiguration()
    {
        HasKey(e => e.Id);
        Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

public class CustomerConfiguration : EntityConfiguration<Customer>
{
    public CustomerConfiguration()
        : base()
    {
        ...
    }
}
Sign up to request clarification or add additional context in comments.

4 Comments

Well, I tried converting the interfaces into abstract classes, and it still doesn't work. I then made the base classes non-abstract, and it still doesn't work! I have now started to repeat the configuration until (one day?) the Entity Framework team support software using interfaces with code-first (which I would think was a core requirement myself).
It worked for me with a base class so there must be another problem.
While this does work for me, I'd much rather use an interface. Hopefully EF will support this in the future.
@LadislavMrnka: +1 I had a similar issue with the same error message on VS2010 EF4.4 when trying to apply a RowVersion configuration through a common interface. As soon as I changed IEntity to EntityBase it worked. It worked both way ways then; using a normal class as base class where the RowVersion is simply inherited or an abstract class using an override on the RowVersion. Pity the interface approach doesn't work.
2

I do not think that you need to go through all of this. EF 4.1 Code First uses a lot of convention over configuration and via this, the Id property of an entity is configured as the primary key. So by implementing the IEntity interface on your entities you are setting them up with the Id as the primary key.

Here is a link to the ADO.NET Team Blog that explains how the primary key convention works - Conventions for Code First

2 Comments

Yes, that is true, but personally I don't like the relying on the conventions and where possible I want to have configuration explicit, self-documenting and under version control. Sooner or later something may not fit within the conventions so these problems should be solvable up-front. There is also some shared configuration not shown in this simplified example that I am not sure the convention will handle automatically.
"I don't like the relying on the conventions" - then write a test and it will fail when the convention changes, Documenting the fact (in code or otherwise) is a less than ideal approach
1

You could just create a static method on a class and pass the entity into it. For example:

public class CustomerConfiguration : EntityConfiguration<Customer>
{
    public CustomerConfiguration()
        : base()
    {
        ...
        EntityConfiguration.Configure(this);
    }
}

public static class EntityConfiguration
{
    public static void Configure<TEntity>(EntityTypeConfiguration<TEntity> entity) where TEntity : EntityBase
    {
        entity.HasKey(e => e.Id);
        entity.Property(e => e.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
    }
}

Comments

0

I have similar issue with EF5.0 when i have generic abstract class with Id property and implementation for abstract members and self defined properties. look like entity framework code first is looking only for mapped class properties. i have tried to use reflector - seems i am right, but don't sure about this for 100%.

And, fortunately, have found solution for this:

 protected override void OnModelCreating(DbModelBuilder modelBuilder)
        {                
            modelBuilder.Conventions.Remove<IncludeMetadataConvention>();
            modelBuilder.Entity<MyEntity>()
               .Map(m =>
               {
                   **m.MapInheritedProperties();**                   
               });
        }

so in my case: to map also properties from base class i have to add one line of code m.MapInheritedProperties()...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.