Blau
Blau

Reputation: 5762

Entity framework sharing foreign key in base class

Problem

I want inherited classes from channel to share same foreign key, merging Gateway_GatewayId and Gateway_GatewayId1 columns into GatewayId column.

Can it be done?

enter image description here

I have tried this:

modelBuilder.Entity<ChannelModbus>().HasRequired(d => d.Gateway).WithMany()
            .HasForeignKey(d => d.GatewayId).WillCascadeOnDelete(false)

But get this error:

The foreign key component 'GatewayId' is not a declared property on type 'ChannelModbus'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.

And I've tried to use MapKey too:

modelBuilder.Entity<ChannelModbus>().HasRequired(d => d.Gateway).WithMany()
            .Map(d => d.MapKey("GatewayId")).WillCascadeOnDelete(false);

And get this error:

One or more validation errors were detected during model generation:

GatewayId: Name: Each property name in a type must be unique. Property name 'GatewayId' is already defined.

GatewayId: Name: Each property name in a type must be unique. Property name 'GatewayId' is already defined.

Code

public abstract class Channel
{
    public int ChannelId { get; private set; }
    public int GatewayId { get; set; }
}

public abstract class Gateway
{
    public int GatewayId { get; private set; }
}

public abstract class GatewayTcp : Gateway
{
    public string Ip { get; set; }
    public int Puerto { get; set; }
}

public class GatewayModbus : GatewayTcp { }
public class GatewayXmlRpc : GatewayTcp { }

public class ChannelModbus : Channel
{
    public virtual GatewayModbus Gateway { get; set; }
}

public class ChannelXmlRpc : Channel
{
    public virtual GatewayXmlRpc Gateway { get; set; }
}

public partial class MyDbContext : DbContext
{
    public IDbSet<Channel> Channels { get; set; }
    public IDbSet<Gateway> Gateways { get; set; }

    protected override void OnModelCreating(DbModelBuilder modelBuilder) {
        modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();

        modelBuilder.Entity<Gateway>().Property(d => d.GatewayId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Gateway>().HasKey(d => d.GatewayId);

        modelBuilder.Entity<Channel>().Property(d => d.ChannelId).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        modelBuilder.Entity<Channel>().HasKey(d => d.ChannelId);

        modelBuilder.Entity<ChannelModbus>().HasRequired(d => d.Gateway).WithMany().WillCascadeOnDelete(false);
        modelBuilder.Entity<ChannelXmlRpc>().HasRequired(d => d.Gateway).WithMany().WillCascadeOnDelete(false);

        //modelBuilder.Entity<ChannelModbus>().HasRequired(d => d.Gateway).WithMany().HasForeignKey(d => d.GatewayId).WillCascadeOnDelete(false);
        //modelBuilder.Entity<ChannelXmlRpc>().HasRequired(d => d.Gateway).WithMany().HasForeignKey(d => d.GatewayId).WillCascadeOnDelete(false);
    }
}

Upvotes: 1

Views: 1771

Answers (1)

Yuliam Chandra
Yuliam Chandra

Reputation: 14640

Yes, it's possible. Just move the Gateway property from ChannelModbus and ChannelXmlRpc to the base class, Channel.

public abstract class Channel
{
    public int ChannelId { get; private set; }
    public int GatewayId { get; set; }
    public virtual GatewayModbus Gateway { get; set; }
}

When you define the property on each derived class, since you use TPH inheritance, all columns will be mapped to a single table, one column belongs to ChannelModbus and one column belongs to ChannelXmlRpc even though they refer to the same entity. By moving it to the base class, both derived class will use the same column for foreign key.

Upvotes: 2

Related Questions