简体   繁体   中英

Entity Framework Core One To Many with No Foreign Key on Master Table

We've decided to consolidate all enums into a RefValue table which I at least have no problem conceiving of, but I can't seem to get Entity to play along.

I think it should make sense once you look at the schema, but the gist of it is that a Person object would have 2 fields when it came to enums (eg gender, relationshipStatus, nameSuffix, etc). One field would store the value (eg "female") and the other would store the id of that RefValue (eg some guid which would be a foreign key to the RefValue table where the value was stored/defined). That way we wouldn't have to do multiple joins on the RefValue table to find those properties values.

Here's the schema:

[Table("Person")]
public class Person : Base
{
    public Guid Id {get; set; }

    public string Name { get; set; }

    public string Gender { get; set; }
    public RefValue GenderRef { get; set; }

    public string RelationshipStatus { get; set; }
    public RefValue RelationshipStatusRef { get; set; }

    public string NameSuffix { get; set; }
    public RefValue NameSuffixRef { get; set; }
}

[Table("RefValue")]
public class RefValue : Base
{
    public Guid Id {get; set; }

    public string Type { get; set; }

    public string Value { get; set; }
}

In the Person object, I'd really just like the RefValue properties to just be Guids as foreign keys to the RefValue table. I started steering towards just making them RefValue properties because it seemed like EFC would make things easier if I did it their way with the concept of navigation properties. The problem though is that it's pretty insistent on having a column on the RefValue table to be the other side of those one-to-many relationships.

I'm fine with adding extra columns on the RefValue table so that is looks more like this:

[Table("RefValue")]
public class RefValue : Base
{
    public string Type { get; set; }

    public string Value { get; set; }

    public ICollection<Person> Genders { get; set; }

    public ICollection<Person> RelationshipStatus { get; set; }

    public ICollection<Person> NameSuffix { get; set; }
}

But no matter how I spin my context class's OnModelCreating() method, I can't seem to get them to play together.

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<RefValue>()
            .HasMany(p => p.Genders)
            .WithOne(p => p.GenderRef)
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<RefValue>()
            .HasMany(p => p.RelationshipStatus)
            .WithOne(p => p.RelationshipStatusRef)
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<RefValue>()
            .HasMany(p => p.NameSuffix)
            .WithOne(p => p.NameSuffixRef)
            .OnDelete(DeleteBehavior.SetNull);
    }

I've tried going the inverse way as well, but I usually get the Introducing FOREIGN KEY constraint ... on table ... may cause cycles or multiple cascade paths

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Person>()
            .HasOne(p => p.PersonTypeRef)
            .WithMany()
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<Person>()
            .HasOne(p => p.RelationshipStatusRef)
            .WithMany()
            .OnDelete(DeleteBehavior.SetNull);
        modelBuilder.Entity<Person>()
            .HasOne(p => p.NameSuffixRef)
            .WithMany()
            .OnDelete(DeleteBehavior.SetNull);
    }

Is there any way to create this kind of one-to-many (or really many-to-one) kind of relationship without the cascading problem or (and especially) entity creating shadow properties on the non-dependent table?

Let's take your original entity model:

[Table("Person")]
public class Person : Base
{
    public Guid Id {get; set; }

    public string Name { get; set; }

    public string Gender { get; set; }
    public RefValue GenderRef { get; set; }

    public string RelationshipStatus { get; set; }
    public RefValue RelationshipStatusRef { get; set; }

    public string NameSuffix { get; set; }
    public RefValue NameSuffixRef { get; set; }
}

[Table("RefValue")]
public class RefValue : Base
{
    public Guid Id {get; set; }

    public string Type { get; set; }

    public string Value { get; set; }
}

There is absolutely no problem to configure unidirectional (ie with navigation property only at one of the sides) FK relationships using the fluent API. The only thing you need to consider is that because of the multiple cascade paths introduced by such model, you can't use delete cascade options, ie you should specify DeleteBehavior.Restrict .

With that being said, here is the fluent configuration for the above model:

modelBuilder.Entity<Person>()
    .HasOne(p => p.GenderRef)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<Person>()
    .HasOne(p => p.RelationshipStatusRef)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

modelBuilder.Entity<Person>()
    .HasOne(p => p.NameSuffixRef)
    .WithMany()
    .OnDelete(DeleteBehavior.Restrict);

And in case you add collection navigation properties, don't forget to specify them in the corresponding WithMany calls, otherwise EF Core will create additional relationships.

We've decided to consolidate all enums into a RefValue table

That makes me sad. How are you going to keep people from having a gender of Single, or a relationship status of Female? How are you going to populate a drop-down with all the valid values for a particular foreign key? How are you going to help report writers from joining the tables correctly?

I can't seem to get them to play together.

That's because what you are attempting is a fundamental violation of relational design. And this is just the first of many difficulties that should make you rethink this design.

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