0

The last weeks I have been working on the development of a database based on Entity Framework 6 (Code-First) on c# using Visual Studio 2015.

I'm currently working on all the options that inheritance offers to work with. At this point and following the standards of a database I should implement multiple inheritance with Primary Key. What this means? This means that I should implement a class that inherits from another class and have his own PK for identify him different from the parent class. At this point I could implement it with TPC, making the parent class abstract and not defining any PK on the parent. This is a sample of the code at this point (this works and I have tested it)

    public abstract class Person
    {

        public string Name { get; set; }
        public string LastName { get; set; }

    }

    [Table("Students")]
    public class Student : Person
    {

        [Key]public int Id_student { get; set; }
        public string code_s { get; set; }
        public virtual ICollection<Course> courses { get; set; }

    }

Then the standard I should follow to build my database requires me to implement another inheritance taking Student as the Parent and creating another class that inherits from Student. The first and stupid idea I had was to make it as simple as write

ChildOfChild : Student

But obviously it didn't work.

Then it come to my mind the possibility of make Student class Abstract, but when I declared the Student class Abstract it didn't let me instantiate it and seed the table. I'm not sure if there is any other way to do this, using abstract class or any other method that could be useful to develop this. If you didn't understand the problem I'm trying to solve this is the sample of code that I would like to have and works.

    public abstract class Person
    {

        public string Name { get; set; }
        public string LastName { get; set; }

    }

    [Table("Students")]
    public class Student : Person
    {

        [Key]public int Id_student { get; set; }
        public string code_s { get; set; }
        public virtual ICollection<Course> courses { get; set; }

    }

    [Table("ExchangeStudent")]
    public class ExchangeStudent : Student
    {

        [Key]public int Id_exchange { get; set; }
        public string HomeUniversity {get; set;}

    }
  • Do you really want to split `Students` and `ExchangeStudents` into two separate tables, I would presume you will be making it more difficult for yourself down the line when selecting all student and linking a student to a class.... _Or is this an example of the problem you are facing?_ – Scrobi Aug 22 '17 at 08:21
  • This is just an example of what i have to implement, the real case is much complex than Student-ExchangeStudent. The real model has general classes that other classes should inherit from them and then other entities that inherit from this child entities. – Marc Planas Aug 22 '17 at 08:52
  • Why not just a PersonId as PK? That's the normal and sane way. – H H Aug 22 '17 at 08:55
  • Because then Student will have just 1 Pk that will be PersonId and it will be common Pk with other classes that also inherit from Person. The result will be something like: Student {PersonId = 12,Name = "Mark"} Teacher {PersonId = 13, Name = "Jhon"} Student {PersonId = 14, Name = "Alex"} and the common properties of Person inside Student will be stored in Person class. I'm not sure if i have explained it well, tell me if u understood it right – Marc Planas Aug 22 '17 at 09:02
  • Rather than using the `Key` attribute have you looked at defining your primary key by overriding [OnModelCreating](https://msdn.microsoft.com/library/system.data.entity.dbcontext.onmodelcreating.aspx) – Scrobi Aug 22 '17 at 09:15
  • Then if I override the primary key it would be different from the parent key that we are overriding ? i mean if they will be stored in different tables and will not be related. We could have Student {Id_student = 10} and then Person{Id_person = 10} whithout any error? – Marc Planas Aug 22 '17 at 09:18
  • I don't mean to override the `Id` property I mean to using fluent api, to define the relationships and ids rather than attributes - see an example [here](https://stackoverflow.com/questions/13607512/how-to-specify-primary-key-name-in-ef-code-first) – Scrobi Aug 22 '17 at 09:22
  • @Scrobi I just tried what you are saying right now and don't works. I defines the new Key via fluent API but when I execute the program the database that is created got a table ExchangeStudent but there is just 1 Pk and its from Student (Student_Id), the PK that i defined via Fluent Api is not configured as Pk and its just a normal attribute with no Key propertie – Marc Planas Aug 22 '17 at 10:03
  • Did you remove the `Key`? Can you add what you tried please. – Scrobi Aug 22 '17 at 11:46
  • @Scrobi `public class Substituto : Teacher { public Guid Substitucio_Id { get; set; } public string Substitucio { get; set; } } modelBuilder.Entity().HasKey(p => p.Substitucio_Id); modelBuilder.Entity() .Property(i => i.Substitucio_Id) .HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity) .HasColumnName("CustomSubsID"); ` this is the class and the sample of code that i use in ModelBuilder – Marc Planas Aug 22 '17 at 12:39

4 Answers4

0

You can't have two primary keys. Student defines Id_student as primary key and then in ExchangeStudent you define Id_exchange as primary key. I would implement a property 'Id' in Person and get rid of the Id_student and Id_exchange properties.

Hintham
  • 1,078
  • 10
  • 29
  • Thats not the model I wanted to have. The model I wanted is classes that has his own PK and then another class that inherits from this class and also have a PK that its just for this child class and isn't related to the parent class, and then other classes that inherit from this child classes and also have their own PK that aren't related with the parent class. I know this is quite complex but i hope that some "expert" has some solution for my problem – Marc Planas Aug 22 '17 at 08:56
  • I think you misunderstand the concept of inheritance. All inheriting classes will have their own primary key. If you want to define a primary key in each inheriting class you need to override the primary key property from the parent. by adding the new keyword. – Hintham Aug 22 '17 at 08:59
  • But then all the primary keys of the diferent classes that inherit from the same parent class will not be related between them? what I mean is if a Student could have the Id_student = 10 and then there will be a Teacher (This class also inherits from Person) with Id_teacher =10, Id_teacher and Id_student are the Pk from the parent class that (as u said) we override in the child and these PK are stored in the table Student and Teacher separetly. Is this what you mean? – Marc Planas Aug 22 '17 at 09:13
  • i forget to mention u @Hintham – Marc Planas Aug 22 '17 at 10:47
0
    public abstract class Student : Person
    {
        [Key]public int Id_student { get; set; }        
    }

    public class LocalStudent : Student{

    }

    public class ExchangeStudent : Student
    {        
        public string HomeUniversity {get; set;}
    }

but i would think about something like

public class Student : Person
    {

        [Key]public int Id_student { get; set; }
        public string code_s { get; set; }
        public virtual ICollection<Course> courses { get; set; }
        public ExchangeInfo ExchangeInfo{get;set;}
    }


    public class ExchangeInfo : Student
    {

        [Key]public int Id_exchange { get; set; }
        public Student Student{get;set;}
        public string HomeUniversity {get; set;}    
    }

Can HomeUniversity sounds like an attribute of student? it will be more confortable to work with single table of student instead of making 2 selects from 2 tables, to find out who is really listening cources also how you will create link for tables like course to two tables Student and ExchangeStudent?

Anatoli Klamer
  • 2,279
  • 2
  • 17
  • 22
  • This is just a simple example of what i'm trying to do on a complex model. The real entities aren't Person-Student-ExchangeStudent, I just wanted to explain the concept by this example of what i'm really trying to implement. What i'm trying to implement is; classes that has his own PK and then another classes that inherits from this class and also have a PK that its just for this child class and isn't related to the parent class or other child entities of this parent, and then other classes that inherit from this child and also have their own PK that aren't related with the parent class. – Marc Planas Aug 22 '17 at 09:08
  • so,it's unusual to inherit item with PK and trying to redefine it, so in my first example i'm trying to split 2 separate entities with common properties. Can you provide real example? maybe without all properties, but at least example in real world on in business – Anatoli Klamer Aug 23 '17 at 15:45
0

Will there be any Person that doesn't have an Id? If not, give a Person an Id property. Define in your fluent API where you model the TPC that this Id is the primary key of your Students as well as your exchange students.

public class Person
{
    public int Id {get; set;
    public string Name { get; set; }
    public string LastName { get; set; }
}

public class Student : Person
{
    // the primary key is in the base class
    public string code_s { get; set; }
    public virtual ICollection<Course> courses { get; set; }
}

public class ExchangeStudent : Student
{
    // the primary key is in one of the base classes
    public string HomeUniversity {get; set;}
}

Your DbContext configures TPC as in this link

class MyDbContext : DbContext
{
    public DbSet<Student> Students {get; set;}
    public DbSet<ExchangeStudent> ExchangeStudents {get; set;}

    protected override OnModelCreating(...)
    {   // configure Student and ExcahngeStudent as TPC,
        // if needed define primary key
        modelBuilder.Entity<Student>()
        .HasKey(student => student.Id)
        .Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("Students");
        });

        modelBuilder.Entity<ExchangeStudent>()
        .HasKey(student => student.Id)
        .Map(m =>
        {
            m.MapInheritedProperties();
            m.ToTable("ExchangeStudents");
        });
    }

I'm not sure if HasKey is needed. Because the primary key follows the naming conventions, it might be that it is not needed.

The advantage of using fluent API above the use of attributes is that it gives you freedom to use the same classes and queries in a different database model, just by specifying a different DbContext.

Harald Coppoolse
  • 28,834
  • 7
  • 67
  • 116
  • Hi Harald, this isn't what I was looking for, what I wanted is that the base class haven't got any PK, then the first child should have his own PK and then the second child should have another PK that will be for identify each row within the table of this second child. Its possible to implement something like this? – Marc Planas Aug 22 '17 at 12:54
  • Why would you want two primary keys? What would the value of the primary key of the first child mean? Why is it a problem that the primary key is in a parent class? This way you will be certain that every class has exactly one primary key, no more, no less – Harald Coppoolse Aug 22 '17 at 19:29
  • The perfect way would be:: have just 1 primary key in each class, each primary key is independent of the parent PK, what I mean with this is; if parent got Id = 10, there could be a child with Id = 10 and there will no problem, also if its possible I wanted a child of the child that also could have Id = 10. Did I explain it well? – Marc Planas Aug 23 '17 at 07:20
  • Do you really need inheritance? – Anatoli Klamer Aug 23 '17 at 15:50
0

From my understanding you want two tables where both with their own primary keys and a foreign key to define a one to one relationship. In your C# code you want these tables to have an inheritance relationship.

The only way I can see you doing this with Entity Framework is to split your requirements. Firstly create the Database models that represent the table structure you want.

namespace DAO
{
    public abstract class Person
    {
        public string Name { get; set; }
    }

    public class Student
    {
        public int StudentId { get; set; }
        public string Nickname { get; set; }
    }

    //Use navigation property rather than inheritance to define the relationship
    public class ExchangeStudent 
    {
        public int ExchangeStudentId { get; set; }
        public string HomeUniversity { get; set; }
        public virtual Student Student { get; set; }
    }
}

This gave me the following code:

 public override void Up()
        {
            CreateTable(
                "dbo.ExchangeStudent",
                c => new
                    {
                        ExchangeStudentId = c.Int(nullable: false, identity: true),
                        HomeUniversity = c.String(),
                        Student_StudentId = c.Int(),
                    })
                .PrimaryKey(t => t.ExchangeStudentId)
                .ForeignKey("dbo.Student", t => t.Student_StudentId)
                .Index(t => t.Student_StudentId);

            CreateTable(
                "dbo.Student",
                c => new
                    {
                        StudentId = c.Int(nullable: false, identity: true),
                        Nickname = c.String(),
                        Name = c.String(),
                    })
                .PrimaryKey(t => t.StudentId);

        }

Now if you need to have a inheritance relationship between Student and ExchangeStudent you can create new objects to define in a different namespace

namespace BusinessObject
{
    public abstract class Person
    {
        public string Name { get; set; }
    }

    public class Student
    {
        public int StudentId { get; set; }
        public string Nickname { get; set; }
    }

    //ExchangeStudent derives from Student
    public class ExchangeStudent : Student
    {
        public int ExchangeStudentId { get; set; }
        public string HomeUniversity { get; set; }
    }
}

Then you just need to create a class that maps the two objects. There are 3rd party libraries that can do this for you as well.

Scrobi
  • 1,215
  • 10
  • 13
  • 1
    Hi Scrobi, first of all thanks for all the help and dedication. Also, are you considering that Student also inherits from Person ? I'm not familiarized with the use of different namespaces but i will investigate this aspect for understand better your solution. Your solution looks quite good and its the first coherent solution. Thank you again for the help :) – Marc Planas Aug 23 '17 at 09:17