0

I tried to apply TPH inheritance strategy on my Domain with Code First and FluentApi.

I have the following domain model:

public abstract class Entity
{
    public Guid Id { get; set; }
    public byte [] RowVersion { get; set; }
}

public class Employee : Entity
{
    #region POCO Fields 
    public string EmployeeId { get; set; }
    //Complex Types
    public PrimaryPersonalData PrimaryPersonalData { get; set; } 
    public ContactPersonalData ContactPersonalData { get; set; }

    #endregion

    #region Navigations
    public ICollection<EmployeePosition> EmployeeCurrentPositions { get; set; }
    public ICollection<EmployeeDegree> EmployeeCurrentDegrees { get; set; }
    public ICollection<EmployeeRole> EmployeeCurrentRoles { get; set; }
    public ICollection<Department> AdministrationDepartments { get; set; }

    public Guid? FacultyId { get; set; }
    public Faculty Faculty { get; set; }
    public Guid? SectorId { get; set; }
    public Sector Sector { get; set; }
    public Guid? AcademicDepartmentId { get; set; }
    public Department AcademicDepartment { get; set; }


    #endregion

    #region Constructors
    public Employee()
    {
        EmployeeCurrentPositions = new List<EmployeePosition>();
        EmployeeCurrentDegrees = new List<EmployeeDegree>();
        EmployeeCurrentRoles = new List<EmployeeRole>();
        AdministrationDepartments = new List<Department>();
        PrimaryPersonalData = new PrimaryPersonalData();
        ContactPersonalData = new ContactPersonalData();

    }

    #endregion

}

public class Teacher : Employee
{
    public ICollection<Subject> ReadeableSubjects { get; set; }
    public Teacher()
    {
        ReadeableSubjects = new List<Subject>();
    }
}

And configurations goes separately :

public abstract class EntityConfiguration<T> : EntityTypeConfiguration<T>
    where T : Entity
{
    protected EntityConfiguration()
    {
        HasKey(p => p.Id);
        Property(p => p.Id).HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
        Property(p => p.RowVersion).IsRowVersion();
    }
}

public class EmployeeConfiguration : EntityConfiguration<Employee>
{
    public EmployeeConfiguration()
    {
        #region POCO Configuration
        Property(p=>p.EmployeeId)
            .IsRequired()
            .IsConcurrencyToken(true);

        #endregion

        #region Mapping Configuration
        HasMany(p => p.EmployeeCurrentPositions)
           .WithMany(p => p.TakingEmployees)
           .Map(p =>
           {
               p.MapLeftKey("EmployeeId");
               p.MapRightKey("PositionId");
               p.ToTable("EmployeePosition");
           });

        HasMany(p => p.EmployeeCurrentDegrees)
            .WithMany(p => p.TakingEmployees)
            .Map(p =>
            {
                p.MapLeftKey("EmployeeId");
                p.MapRightKey("DegreeId");
                p.ToTable("EmployeeDegree");
            });

        HasMany(p => p.EmployeeCurrentRoles)
            .WithMany(p => p.TakingEmployees)
            .Map(p =>
            {
                p.MapLeftKey("EmployeeId");
                p.MapRightKey("RoleId");
                p.ToTable("EmployeeRole");
            });

        #endregion

    }

public class TeacherConfiguration : EntityConfiguration<Teacher>
{
    // I suppose problem is hidden here. TeacherConfiguration inherits from 
    // EntityConfiguration and may miss configuration of Employee. Am I right?
    public TeacherConfiguration()
    {
        HasMany(p => p.ReadeableSubjects).WithMany(p => p.ReadeableTeachers).Map(m =>
        {
            m.MapLeftKey("TeacherId");
            m.MapRightKey("SubjectId");
            m.ToTable("TeacherSubject");
        });
    }
}

And the last piece - Faculty class that consists of both Employees and Teachers.

public class Faculty : Entity
{
    #region POCO Fields
    public string Acronym { get; set; }
    public string FacultyNameEn { get; set; }
    public string FacultyNameRu { get; set; }
    public string FacultyDescriptionEn { get; set; }
    public string FacultyDescriptionRu { get; set; }

    #endregion

    #region Navigations
    public ICollection<Department> Departments { get; set; }
    public ICollection<Employee> Employees { get; set; }
    public ICollection<Teacher> Teachers { get; set; }
    public ICollection<Student> Students { get; set; }
    public ICollection<Subject> Subjects { get; set; }
    public ICollection<Specialty> Specialties { get; set; }

    #endregion

    #region Constructors

    public Faculty(string facultyAcronym, string facultyNameEn, string facultyNameRu,
        string facultyDescriptionEn, string facultyDescriptionRu)
        : this()
    {
        Acronym = facultyAcronym;
        FacultyNameEn = facultyNameEn;
        FacultyNameRu = facultyNameRu;
        FacultyDescriptionEn = facultyDescriptionEn;
        FacultyDescriptionRu = facultyDescriptionRu;
    }
    public Faculty()
    {
        Departments = new List<Department>();
        Employees = new List<Employee>();
        Students = new List<Student>();
        Subjects = new List<Subject>();
        Specialties = new List<Specialty>();
        Teachers = new List<Teacher>();
    }
    #endregion

}

public class FacultyConfiguration : EntityConfiguration<Faculty>
{
    public FacultyConfiguration()
    {
        #region POCO Configurations
        Property(p => p.Acronym)
            .IsRequired()
            .HasMaxLength(10);

        Property(p => p.FacultyNameEn)
            .IsRequired()
            .HasMaxLength(100);

        Property(p => p.FacultyNameRu)
            .IsRequired()
            .HasMaxLength(100);

        Property(p => p.FacultyDescriptionEn)
            .IsRequired()
            .HasMaxLength(1000);

        Property(p => p.FacultyDescriptionRu)
            .IsRequired()
            .HasMaxLength(1000);

        #endregion

        #region Mapping Configurations
        HasMany(p => p.Departments)
            .WithRequired(p => p.Faculty)
            .HasForeignKey(p => p.FacultyId);

        HasMany(p => p.Employees)
            .WithOptional(p => p.Faculty)
            .HasForeignKey(p => p.FacultyId);

        HasMany(p => p.Teachers)
            .WithOptional(p => p.Faculty)
            .HasForeignKey(p => p.FacultyId);

        HasMany(p => p.Students)
            .WithRequired(p => p.Faculty)
            .HasForeignKey(p => p.FacultyId);

        HasMany(p => p.Subjects)
            .WithRequired(p => p.Faculty)
            .HasForeignKey(p => p.FacultyId);

        HasMany(p => p.Specialties)
            .WithRequired(p => p.Faculty)
            .HasForeignKey(p => p.FacultyId);
        #endregion

    }
}

And finally I got an exception :

Additional information: The foreign key component 'FacultyId' is not a declared property on type 'Teacher'. Verify that it has not been explicitly excluded from the model and that it is a valid primitive property.

I found similar question here but there is a bit different situation. I think problem is that TeacherConfiguration inherit from EntityConfiguration but not from EmployeeConfiguration. Could you help me to find what is wrong here?

Community
  • 1
  • 1
Iskander Raimbaev
  • 1,322
  • 2
  • 17
  • 35
  • Try removing the explicit mappings about `Employee` and `Teacher` relationships with `Faculty`. EF is usually good inferring the relationships when the naming of the properties and the primary key fields are consistent, as in your case. Sometimes redundant explicit mappings interfere with the EF magic. Another thing that could work is putting an explicit `ToTable()` in the configuration classes of both `Employee` and `Teacher`, with the same table name in both. Is your migration generating correctly only one table with a Discriminator column? – Diana Oct 28 '16 at 20:39
  • Also, a side note: if you intend to use Lazy Loading you should declare all your navigation properties as `virtual`. – Diana Oct 28 '16 at 20:41
  • Another idea: try adding `.HasOptional(t => t.Faculty)` in the `Teacher` configuration class. – Diana Oct 28 '16 at 20:48

0 Answers0