locked
Entity Framework Code First - Table Per Type Inheritance RRS feed

  • Question

  • User1512368048 posted

    Hi all,

    I'm having an issue inserting an instance of a subclass that inherits from a base class.

    Consider the following code snippets from these POCOs:

    public abstract class EntityBase<T>  
    {
       private T _id; 
       
       [Key] 
       public T ID 
       { 
          // get and set details ommitted.
       }
    }
    
    public abstract class PersonBase<T> : EntityBase<T>
    {
       // Details ommited.  
    }
    
    public class Applicant : PersonBase<int>
    {
       // Details ommitted for brevity.  
    }
    
    public class Employee : Applicant 
    {
    }  

     

    Pretty standard inheritance right now.  In our system, when an applicant finally becomes an employee, we collect extra data.  If not hired, they remain an applicant with a limited set of information.

    Now consider the fluent API that sets up the table-per-type inheritance model:

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
           base.OnModelCreating(modelBuilder);
             
           // Set up table per type object mapping for the Visitor Hierarchy.
           modelBuilder.Entity<Employee>().ToTable("Employees");
    }

    So far, so good...

    If I look at the database this creates, I have a table named Applicants with an Id column of type int, auto-incrementing ID and I have an Employees table with an ID field as the primary key (non auto incrementing).

    Basically, the ID field in the Employees table is a foreign key to the Applicants table.

    This is what I want.  I don't want a record into the Employees table corresponding to the Applicants table until they actually become an Employee. 

    The problem comes when I try to insert an Employee which comes down to this code:

    public void PersistCreationOf(T entity)
    {
         DataContextFactory.GetDataContext().Set(typeof(T)).Add(entity);
    }

    The problem: It inserts a brand new applicant and Employee.  I want to just insert the Employee record with the ID it already has (the foreign key from the Visitors table).

    Is this possible?

     

     

    Wednesday, June 15, 2011 8:45 AM

Answers

  • User3866881 posted

    Hello again:)

    As far as I know, One-To-One is a special kind of One-To-Many, I'm just considering more in order to let your work can be flexible and extendable in the future according to my experience...

    If you insist doing One-To-One. You can just change something like this:

    public class Applicant
    {
    [Key]
    [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    [Required]
    public string ApplicantName { get; set; }

    public virtual Employee Employee { get; set; }
    }

    public class Employee
    {
    [Key]
    [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    [Required]
    public string EmployeeName { get; set; }

    public int AId { get; set; }

    [ForeignKey("AId")]
    [InverseProperty("Employee")]
    public virtual Applicant Applicant { get; set; }
    }

    See more samples at:

    http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, June 17, 2011 9:50 PM

All replies

  • User3866881 posted

    Hello:)

    In my mind, I think the problem is related because you've used inherting classes. In fact I think we can use something like this(I've tested and everything is OK with me)

    namespace TestConsoleApplication1
    {
        [Table("Employees")]
        public class Employee
        {
            [Key]
            [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
            public int Id { get; set; }
            [Required]
            public string EmployeeName { get; set; }

            public int AId { get; set; }

            [ForeignKey("AId")]
            [InverseProperty("Employees")]
            public virtual Applicant Applicant { get; set; }
        }

        public class Applicant
        {
            [Key]
            [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
            public int Id { get; set; }
            [Required]
            public string ApplicantName { get; set; }

            public virtual IList<Employee> Employees { get; set; }
        }


        public class EmployeeManager : DbContext
        {
            public DbSet<Employee> Employees { get; set; }
            public DbSet<Applicant> Applicants { get; set; }

         
        }
        class Program
        {
            static void Main(string[] args)
            {
                Database.SetInitializer<EmployeeManager>(new DropCreateDatabaseIfModelChanges<EmployeeManager>());

                using (EmployeeManager em = new EmployeeManager())
                {
                    Applicant a = new Applicant { ApplicantName = "App1" };
                    em.Applicants.Add(a);
                    em.Employees.Add(new Employee { EmployeeName = "Emp1", Applicant = a });
                    em.SaveChanges();
                    Console.WriteLine("OK");
                }
               
              
            }
        }
    }

     

    Thursday, June 16, 2011 10:27 PM
  • User1512368048 posted

    Right,

    I've looked at this a lot since the post and like you're suggesting, it's obvious I should have favored composition over inheritance in this scenario.

    I was curious, though, in your example, why do you store Employees as a collection in Applicant?  

    In the way I presented the question, the Applicant can only contain one set of Employee information so it'd contain one Employee object.  Although with a collection that one applicat can have multiple sets of employee information - for example at different job sites on different charge codes, different companies, etc., which is common in our line of business (construction), so that's more flexible for sure.

     

    Friday, June 17, 2011 8:33 AM
  • User3866881 posted

    Hello again:)

    As far as I know, One-To-One is a special kind of One-To-Many, I'm just considering more in order to let your work can be flexible and extendable in the future according to my experience...

    If you insist doing One-To-One. You can just change something like this:

    public class Applicant
    {
    [Key]
    [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    [Required]
    public string ApplicantName { get; set; }

    public virtual Employee Employee { get; set; }
    }

    public class Employee
    {
    [Key]
    [DatabaseGenerated(System.ComponentModel.DataAnnotations.DatabaseGeneratedOption.Identity)]
    public int Id { get; set; }
    [Required]
    public string EmployeeName { get; set; }

    public int AId { get; set; }

    [ForeignKey("AId")]
    [InverseProperty("Employee")]
    public virtual Applicant Applicant { get; set; }
    }

    See more samples at:

    http://weblogs.asp.net/manavi/archive/2011/05/01/associations-in-ef-4-1-code-first-part-5-one-to-one-foreign-key-associations.aspx

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Friday, June 17, 2011 9:50 PM