简体   繁体   中英

Entity Framework, table with 2 foreign keys to 2 different tables

I have 3 models with fields in it like the following:

public class RootObject
{
    [Key]
    public int RootObjectId { get; set; }
    [ForeignKey("RootObjectId")]
    public virtual AObject AObject { get; set; }
    [ForeignKey("RootObjectId")]
    public virtual BObject BObject { get; set; }

    public string Name { get; set; }
}

public class AObject
{
    [Key]
    public int AObjectId { get; set; }

    //Other fields
}

public class BObject
{
    [Key]
    public int BObjectId { get; set; }

    //Other fields
}

I want it so that if I were to visually inspect the RootObject table I would see see a list of RootObjectId 's and Name 's. For ease, lets assume even numbered RootObjectId 's are mapped to AObjectId 's and odds are mapped to BObjectId 's. If I were to visually inspect AObject , I would expect to see the ID's 2, 4, 6, ... that are FK's for RootObject . If were to visually inspect BObject , I would expect to see the ID's 1, 3, 5, ... that are FK's for RootObject .

Currently, when I try this approach I get the following error:

"An error occurred while updating the entries...Referential integrity constraint violations. A Dependent Role has multiple principals with different values."

I tried to remove the FK attributes in RootObject but that created 2 additional columns in RootObject that were populated with ID numbers. I don't want this since every RootObject has either one AObject or one BObject . It can't have both.

To me, you are looking for something for which the TPT (Table per Type) approach in Entity Framework could be a solution. Applied to your case (there are many approaches, but this I tested and it works):

public class RootObject
{
    [Key]
    public int RootObjectId { get; set; }
    public string Name { get; set; }
}

[Table("AObjects")]
public class AObject : RootObject
{
    //Other fields
    public string AField { get; set; }
}

[Table("BObjects")]
public class BObject : RootObject
{
    //Other fields
    public string BField { get; set; }
}

For the DbContext class:

public DbSet<RootObject> RootObjects { get; set; }
public DbSet<AObject> AObjects { get; set; }
public DbSet<BObject> BObjects { get; set; }

Seed example:

AObject a1 = new AObject() { Name = "ImA", AField = "adata" };
BObject b1 = new BObject() { Name = "ImB", BField = "bdata" };
context.AObjects.Add(a1);
context.BObjects.Add(b1);
context.SaveChanges();

I don't think it is possible to use ONE column to be a foreign key to two different tables. You should rather think of two (optional) FK like:

public class RootObject
{
    [Key]
    public int RootObjectId { get; set; }

    public int? EvensAObjectId { get; set; }
    public int? OddsBObjectId { get; set; }

    [ForeignKey("EvensAObjectId")]
    public virtual AObject AObject { get; set; }

    [ForeignKey("OddsBObjectId")]
    public virtual BObject BObject { get; set; }

    public string Name { get; set; }
}

To set up 1-0..1 relationships you need to define the relationships explicitly during model configuration.

public class Model1 : DbContext
{
    public Model1()
        : base("name=Model1")
    {
    }

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
        modelBuilder.Entity<AObject>()
            .HasRequired(e => e.RootObject).WithOptional(r => r.AObject);

        modelBuilder.Entity<BObject>()
            .HasRequired(e => e.RootObject).WithOptional(r => r.BObject);

        base.OnModelCreating(modelBuilder);
    }

    public virtual DbSet<RootObject> RootObjects { get; set; }
    public virtual DbSet<AObject> AObjects { get; set; }
    public virtual DbSet<BObject> BObjects { get; set; }
}


public class RootObject
{
    [Key]
    public int RootObjectId { get; set; }
    public virtual AObject AObject { get; set; }
    public virtual BObject BObject { get; set; }

    public string Name { get; set; }
}

public class AObject
{
    [Key]
    public int AObjectId { get; set; }

    public virtual RootObject RootObject { get; set; }
}

public class BObject
{
    [Key]
    public int BObjectId { get; set; }

    public virtual RootObject RootObject { get; set; }
}

You'll want to be really careful when setting RootObject.AObject and RootObject.BObject , as if there is already a related row you will get an error when you save. Also, I don't think there's any way to get EF enforce the constraint that each RootObject must have either an AObject or a BObject, but not both - you'd need to enforce that in your code.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM