简体   繁体   中英

Code First two objects having other object

using entity framework code first I have this :

public class Person {
    public int PersonId {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}
    public virtual List<Note> Notes {get;set;}
}

public class Product {
    public int ProductId {get;set;}
    public string Name {get;set;}
    public virtual List<Note> Notes {get;set;}
}

public class Note {
    public int NoteId {get;set;}
    public string Body {get;set;}
    public virtual User Author {get;set;}
    // what should be here ?
}

How can I know if a note is from a Person or a Product ? Should I need different classes (PersonNote, ProductNote) for that ? Can I use a Interface approach ? like INoteable ?

I'm not sure what's the best strategy for the DB to be modeled or the classes. Any suggestion is appreciated.

Thanks,

EDIT (based on answers)

the suggested answer means that for every entity that has notes, the table will have a new column which i don't like much. Is there any way to use a discriminator ? I don't like the idea of modifying the table everytime a new entity can have notes and potentially end up with like 10 FK (9 always null) if I have 10 entities that supports notes.

I ideally want to have something like an Interface (or whatever) so I can have in code, Product : INoteable and by that it means that a note can be created using this id. Maybe with a discriminator column. Is that even possible ?

Sorry for not being clear the first time.

In real life scenario I have like : Products, Persons, Purchase Orders, Sales, Payments, Payment Orders, and a few more entities that I need to implement notes.

EDIT 2 :

What about this Database structure :

TABLE: Note
int PK NoteId
int FK NoteDataId
string Body

TABLE: Person
int PK PersonId 
int FK NoteDataId

TABLE: Product
int PK ProductId
int FK NoteDataId

TABLE: NoteData
int PK NoteDataId

with this data structure, all entities that want to implement Notes I just add a navigation property NoteDataId and when creating notes, I just give the NoteDataId value. I think EF will take care of the creation of NoteDataId row if it doesn't exists.

EDIT 3:

Example data:

Person:
PersonId 1
Name Bart
NoteDataId 1

PersonId 2
Name Alex
NoteDataId 2

Product:
ProductId 1
Name "Dulce de Leche"
NoteDataId 3

NoteData:
NoteDataId 1
NoteDataId 2
NoteDataId 3

Note:
NoteId 1
NoteDataId 1
Body "first note"

NoteId 2
NoteDataId 1
Body "second note of Bart"

NoteId 3
NoteDataId 2
Body "first note of Alex"

NoteId 4
NoteDataId 3
Body "Note about Dulce de Leche"

how to get the notes of some person ?

SELECT * FROM Note
JOIN NoteData USING (NoteDataId)
JOIN Person USING (NoteDataId)
WHERE PersonId = 1

backwards ?

SELECT * FROM Note
JOIN NoteData USING (NoteDataId)
LEFT JOIN Person USING (NoteDataId) 'can be null, only one type exists'
LEFT JOIN Product using (NoteDataId) 'can be null, only one type exists'
WHERE NoteId = 2

Create nullable foreign keys:

public class Note {
    public int NoteId {get;set;}
    public string Body {get;set;}
    public virtual User Author {get;set;}

    public int? PersonId { get; set; }
    public virtual Person Person { get; set; }

    public int? ProductId { get; set; }
    public virtual Product Product { get; set; }
}

You have to map the properties as optional.

Configuration for Note :

Property(n => n.PersonId).IsOptional();
Property(n => n.ProductId).IsOptional();

Configuration for Person and Product :

Property(p => p.Note).IsOptional();


Update :

You could also use the table-per-hierarchy solution.

Leave Note the same (might as well make it abstract) and create derived types. For example PersonNote :

 public clas PersonNote : Note { public int PersonId { get; set; } public virtual Person Person { get; set; } } 

Where Person gets a navigation property:

 public virtual PersonNote Note { get; set; } 

And ProductNote :

 public clas ProductNote : Note { public int ProductId { get; set; } public virtual Product Product { get; set; } } 

Where Product gets a navigation property:

 public virtual ProductNote Note { get; set; } 

Entity Framework will create one Note table containing all the properties of the derived types and a discriminator column. But this will result in a lot of classes in your code, but that's not a bad thing in my opinion.


Update 2

You could also let go of the navigation property from Note to a Product or Person if you don't need it. This will keep your code a lot simpler. You can leave Note as it is, and add navigation and foreign key properties to your other entities:

 public class Person { // properties.. public int NoteId { get; set; } public virtual Note Note { get; set; } } 

Edited: Thinking about it allover. This doesn't solve the first issue, we only added another table. Why don't you add a note type column that map to an enum.


According to your last edit, this is how I would do the code

public class Person {
    public int PersonId {get;set;}
    public string FirstName {get;set;}
    public string LastName {get;set;}

    public int NoteData NoteDataId{get;set;}

    [ForeignKey("NoteDataId")]
    public virtual NoteData NoteData{get;set;}
}

public class Product {
    public int ProductId {get;set;}
    public string Name {get;set;}

    public int NoteData NoteDataId{get;set;}

    [ForeignKey("NoteDataId")]
    public virtual NoteData NoteData{get;set;}
}

public class NoteData {
    public int NoteDataID {get;set;}

    public virtual List<Note> Notes {get;set;}
}    

public class Note {
    public int NoteId {get;set;}
    public string Body {get;set;}

    public int NoteData NoteDataId{get;set;}

    [ForeignKey("NoteDataId")]
    public virtual NoteData NoteData{get;set;}
}

I'm not sure how it would work for EF, but in our model we use a generic "Parent" property that returns an object. This only works if you only have one object that owns the note and not many objects referencing the note.

public virtual object Parent { get; set; }

However, I don't think EF will automatically populate that for you...

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