-
Notifications
You must be signed in to change notification settings - Fork 3.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Multiple TPH entities with same dependant #20719
Comments
@untaught I have not been able to reproduce this--see my code below. Please attach a small, runnable project or post a small, runnable code listing like below that reproduces what you are seeing so that we can investigate. public abstract class Person
{
[Key]
public int Id { get; set; }
public int TypeId { get; set; }
}
public class Student : Person
{
public ICollection<Address> Addresses { get; set; }
}
public class Teacher : Person
{
public ICollection<Address> Addresses { get; set; }
public ICollection<Room> Rooms { get; set; }
}
public class Address
{
[Key]
public int Id { get; set; }
public int PersonId { get; set; }
public string Street { get; set; }
public Student Student { get; set; }
public Teacher Teacher { get; set; }
}
public class Room
{
public int Id { get; set; }
}
public static class Program
{
public static void Main()
{
using (var context = new SomeDbContext())
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
context.AddRange(
new Student
{
Addresses = new List<Address>
{
new Address(),
new Address()
}
},
new Teacher
{
Addresses = new List<Address>
{
new Address(),
new Address()
}
});
context.SaveChanges();
}
using (var context = new SomeDbContext())
{
var teachers = context.Set<Teacher>().Include(e => e.Addresses).ToList();
var students = context.Set<Student>().Include(e => e.Addresses).ToList();
}
}
}
public class SomeDbContext : DbContext
{
private static readonly ILoggerFactory
Logger = LoggerFactory.Create(x => x.AddConsole()); //.SetMinimumLevel(LogLevel.Debug));
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasDiscriminator(e => e.TypeId)
.HasValue<Person>(1)
.HasValue<Teacher>(2)
.HasValue<Student>(3);
modelBuilder.Entity<Student>().HasMany(e => e.Addresses).WithOne(e => e.Student);
modelBuilder.Entity<Teacher>().HasMany(e => e.Addresses).WithOne(e => e.Teacher);
modelBuilder.Entity<Teacher>().HasMany(e => e.Rooms).WithOne();
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.UseLoggerFactory(Logger)
.EnableSensitiveDataLogging()
.UseSqlServer(Your.SqlServerConnectionString);
} |
Here is an example of the exception. There we have TPH, so PersonId in the Address table should be the FK to the Person table. In your example EF creates two FKs in the Address table - one for Student and one for Teacher. This must not be the case because TPH is referencing same table for multiple types. It should be possible to define only one FK to that table. This way it reflects the structure in the DB. using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using Microsoft.EntityFrameworkCore;
public abstract class Person
{
[Key]
public int Id { get; set; }
public int TypeId { get; set; }
}
public class Student : Person
{
public ICollection<Address> Addresses { get; set; }
}
public class Teacher : Person
{
public ICollection<Address> Addresses { get; set; }
}
public class Address
{
[Key]
public int Id { get; set; }
public int PersonId { get; set; }
public string Street { get; set; }
[ForeignKey(nameof(PersonId))]
public Student Student { get; set; }
[ForeignKey(nameof(PersonId))]
public Teacher Teacher { get; set; }
}
public static class Program
{
public static void Main()
{
using (var context = new SomeDbContext())
{
context.AddRange(
new Student
{
Addresses = new List<Address>
{
new Address(),
new Address()
}
},
new Teacher
{
Addresses = new List<Address>
{
new Address(),
new Address()
}
});
context.SaveChanges();
}
using (var context = new SomeDbContext())
{
var teachers = context.Set<Teacher>().Include(e => e.Addresses).ToList();
var students = context.Set<Student>().Include(e => e.Addresses).ToList();
}
}
}
public class SomeDbContext : DbContext
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasDiscriminator(e => e.TypeId)
.HasValue<Teacher>(1)
.HasValue<Student>(2);
modelBuilder.Entity<Student>().HasMany(e => e.Addresses).WithOne(e => e.Student).IsRequired(false);
modelBuilder.Entity<Teacher>().HasMany(e => e.Addresses).WithOne(e => e.Teacher).IsRequired(false);
}
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
=> optionsBuilder
.EnableSensitiveDataLogging()
.UseSqlServer(ConnectionString);
} |
@untaught Thanks--I am able to reproduce the exception now. Team/@AndriySvyryd: I was able to get the model built correctly this: modelBuilder
.Entity<Student>()
.HasMany(e => e.Addresses)
.WithOne(e => e.Student)
.HasForeignKey(e => e.PersonId)
.HasConstraintName("[FK_Address_Person_PersonId]");
modelBuilder
.Entity<Teacher>()
.HasMany(e => e.Addresses)
.WithOne(e => e.Teacher)
.HasForeignKey(e => e.PersonId)
.HasConstraintName("[FK_Address_Person_PersonId]"); (Using HasConstraintName due to #12963) Which gives me:
SaveChanges then executes this successfully:
and throws the exception reported above. |
In the following example EF throws InvalidCastException:
Both Student and Teacher types have discriminator TypeId and both are mapped to Person table.
Address is a table with relation that one person can have multiple addresses. The address entity can have only Student or Teacher property set depending on its type in Persons table.
I have tried to create the relationship with fluent api and attributes but EF throws the following exception on save in Teacher or Student DbSet:
I have person types which does not have addresses and the Teacher and some other types have Room collection so it's not a good idea to set collection of adresses or rooms to Person entity.
EF core version is 3.1.3
The text was updated successfully, but these errors were encountered: