Kristen Hammack
Kristen Hammack

Reputation: 399

How can I map a base entity field to different columns for each subclass

I have a base entity class, which I want to be able to inherit from so that I only have to write 1 GetById method for lots of different entity-specific repositories. However, the "Id" column in some of my database tables has to be called "Code" for legacy reasons.

I tried to do (simplified version):

public class EntityBase{
    int Id;
}

public class IdEntity : EntityBase{
}

public class CodeEntity : EntityBase{
}

And then in the DbContext:

modelBuilder.Entity<IdEntity>(entity =>
{
    entity.HasKey(e=>e.Id).HasName("PK_IdEntity");
    entity.Property(e=>e.Id).HasColumnName("ID");
}

modelBuilder.Entity<CodeEntity>(entity =>
{
    entity.HasKey(e=>e.Id).HasName("PK_CodeEntity");
    entity.Property(e=>e.Id).HasColumnName("Code"); //this seems to be where it breaks
}

This builds fine, but when I try to run it, I get an InvalidOperationException: Cannot call Property for the property 'Id' on entity type 'CodeEntity' because it is configured as a navigation property. Property can only be used to configure scalar properties.

Is what I'm trying to do impossible? That seems unlikely to me, but then what would be the correct way to do it?

EDIT:

What I was actually trying to do turns out to be much more complex than the "simplified" code I posted here. I wanted to make it so I could have a string or int ID, and use the same GetById method. So I created a class that had implicit conversions to both string and int, which I thought would be ok for Entity Framework.

It's not.

The runtime error was caused by trying to tell EF that a property with the EntityID type should be my key, which you're not allowed to do.

I couldn't find any documentation from Microsoft about requirements for key values in Entity Framework, but I found another SO question that said only scalars and byte[] are allowed for keys: Type safe keys with Entity Framework model

Upvotes: 1

Views: 1401

Answers (1)

Smit
Smit

Reputation: 2459

I could not get above code to compile or give the exception as you were getting enough after trying to fix it. Though I will describe how to write the scenario you trying to achieve.

If you want to use EntityBase.Id as PK property in EF, you need to make it public property. In your code, it is a private field (since no get/set methods). So your EntityBase class should look like this

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

With above changes and OnModelCreating code to map entity types as posted in question, the model building works correctly creating tables with desired table structure and names.

Following is a small method implementing a generic GetById on dbset. It can be made part of generic repository or even extension method.

public static T GetById<T>(DbSet<T> dbset, int id)
    where T : EntityBase
{
    return dbset.FirstOrDefault(e => e.Id == id);
}

// example
var entity = GetById(db.Set<IdEntity>(), 2);

Upvotes: 1

Related Questions