I have this entity model:
I want to have a Customer entity, which can be either a Person or an Organization, It can't be both.
As of now I came up with a customer entity which points to both organization and person but with a nullable field (meaning Guid?), which means having a non-mandatory relationship with Person and Organization. Something like:
class Customer
{
public Guid ID { get; set; }
public Guid? RelatedPersonID { get; set; }
public Guid? RelatedOrganizationID { get; set; }
public int CustomerStatus { get; set; }
public bool IsVIP { get; set; }
// ... other customer related properties
public virtual Person RelatedPerson { get; set; }
public virtual Organization RelatedOrganization { get; set; }
}
I'm using entity framework 5, codefirst approach and I haven't created the database yet. I was wondering if there's a better model that meets this requirements.
Related
I am trying to build an organization hierarchy where each team might contain one or many members and/or one or many sub-teams.
To do so, my model is:
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Members { get; set; }
public virtual ICollection<Team> SubTeams { get; set; }
public Employee Manager { get; set; }
}
When adding a migration and updating database, everything seems logical in the table.
EF has added an extra nullable column "Team_Id" where the Id of the parent Team gets stored.
My question is about getting the Id of the parent Team from my model.
I tried adding:
public int? Team_Id
To my model, but EF considered it as a model change and asked for another migration.
How can I get the value of column Team_Id in my model? getting this info takes too much processing when looping through teams.
I always add foreign key in my model. When it adds to the model, EF won't add Team_Id .
public class Team
{
public int Id { get; set; }
public string Name { get; set; }
public virtual ICollection<Employee> Members { get; set; }
public virtual ICollection<Team> SubTeams { get; set; }
public Employee Manager { get; set; }
public int? ParentId { get; set; }
[ForeignKey("ParentId")]
public Team ParentTeam { get; set; }
}
I hope this example be helpful.
I'm trying to create a customer entity that has multiple contact persons, as well as one primary contact person, but I can't seem to add the migration, as I'm getting the following error:
Unable to determine the relationship represented by navigation property 'ContactPerson.Customer' of type 'Customer'. Either manually configure the relationship, or ignore this property using the '[NotMapped]' attribute or by using 'EntityTypeBuilder.Ignore' in 'OnModelCreating'.
Customer
public class Customer
{
public Guid CustomerId { get; set; }
public string Name { get; set; }
public DateTime CreateDate { get; set; }
public string City { get; set; }
public string Address { get; set; }
// Contact person data
public virtual ContactPerson PrimaryContactPerson { get; set; }
public virtual ICollection<ContactPerson> ContactPersons { get; set; }
}
ContactPerson
public class ContactPerson
{
public Guid ContactPersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public virtual Customer Customer { get; set; }
}
I tried adding the foreign keys myself, and annotating the foreign key property with my entity, on both the Customer entity as well as the ContactPerson entity, like this:
public class ContactPerson
{
public Guid ContactPersonId { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
[ForeignKey("Customer")]
public Guid CustomerId { get; set; }
public virtual Customer Customer { get; set; }
}
But it doesn't seem to make any difference, I'm still getting the same error. How can it be that EF can't determine the relationship?
I suppose it has something to do with the one-to-one relation simultaneously existing with the one-to-many relation, but I can't seem to wrap my head around this issue. Advice and suggestion are highly appreciated!
If I comment out the PrimaryContactPerson property, EF adds the migration just fine, so I'm positive that this has something to do with the two different relations.
I managed to solve my issue using the Entity Framework Fluent API, like this:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<ContactPerson>(e =>
e.HasOne(r => r.Customer).WithMany(c => c.ContactPersons)
);
}
I have two entities in database: customer and user and building a one-to-many relationship between them, so 1 customer -> many users.
This is my customer model:
public class Customer
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public ICollection<User> Users { get; set; }
}
What would be the correct User model, I mean with Customer property and CustomerId property or just CustomerId? So this:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public Customer Customer { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
}
.. or this?:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[ForeignKey("Customer")]
public int CustomerId { get; set; }
}
Actually it should be like the code example below. You should specify what is local primary key, remote key and if there is navigation property (Customer), then You should specify "How should it recognize the object" => based on the this or that key of Customer (CustomerId).
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//other properties
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
public int CustomerId { get; set; }
}
If You are not interested in having navigation property, You can simply avoid that to have only:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//other properties
public int CustomerId { get; set; }
}
More info can be found there: http://www.entityframeworktutorial.net/code-first/foreignkey-dataannotations-attribute-in-code-first.aspx
Note: You do not need to specify [ForeignKey] attribute, if You are using Navigation Property with the same name (e.g. Customer & CustomerId).
If there are multiple references to the same class (e.g. You have User that will have exactly 2 Customers (CustomerA, CustomerB), then You must specify following:
public class User
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//other properties
[ForeignKey("CustomerIdA")]
public Customer CustomerA { get; set; }
public int CustomerIdA { get; set; }
[ForeignKey("CustomerIdB")]
public Customer CustomerB { get; set; }
public int CustomerIdB { get; set; }
}
In the first case you have a correctly set navigational property between User and Customer. In the second case, you cannot navigate from User to Customer, and you have a Foreign Key attribute that point to a non-existent property.
You should, however, invert the order of the ForeingKeyAttribute to make it clearer (both ways are OK, though):
[ForeignKey("CustomerId")]
public Customer Customer { get; set; }
public int CustomerId { get; set; }
If you won't need User.Customer, you can skip setting up the navigational property altogether. The same applies to Customer.Users, you can skip setting up that navigational property if it's not needed.
I have a Project model which has a ProjectLead (one instance of the Person Foreign Key), this works fine. But now I also need to add a collection of People (Project members) referencing the same Person table and I can't get the Entity Framework to generate my database. As soon as I try to add the Fluent API code to create the link table ProjectPerson I get an error - "Schema specified is not valid. Errors: The relationship 'MyApp.WebApi.Models.Person_Projects' was not loaded because the type 'MyApp.WebApi.Models.Person' is not available." I assume this is because of the existing FK relationship already in place with ProjectLead.
Project Model:
public class Project
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
// Foreign Key - Project lead (Person)
public int ProjectLeadId { get; set; }
public virtual Person ProjectLead { get; set; }
// Create many to many relationship with People - Team members on this project
public ICollection<Person> People { get; set; }
public Project()
{
People = new HashSet<Person>();
}
}
Person Model:
public class Person
{
[Key]
public int PersonId { get; set; }
public String Firstname { get; set; }
public String Surname { get; set; }
// Create many to many relationship
public ICollection<Project> Projects { get; set; }
public Person()
{
Projects = new HashSet<Project>();
}
}
DB Context:
public class HerculesWebApiContext : DbContext
{
public DbSet<Person> People { get; set; }
public DbSet<Project> Projects { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// This works fine
modelBuilder.Entity<Project>()
.HasRequired(c => c.ProjectLead)
.WithMany(d => d.Projects)
.HasForeignKey(c => c.ProjectLeadId)
.WillCascadeOnDelete(false);
// Adding these lines to create the link table `PersonProjects` causes an error
//modelBuilder.Entity<Person>().HasMany(t => t.Projects).WithMany(t => t.People);
//modelBuilder.Entity<Project>().HasMany(t => t.People).WithMany(t => t.Projects);
}
}
I gather that perhaps I need to use the InverseProperty attribute, but I am not sure where this should go in this case?
Can you explicitly define your join table? So, define a ProjectPeople relationship and make the code something like this...
public class ProjectPerson{
[Key]
public int ProjectPersonId { get; set; }
[ForeignKey("Project")]
public int? ProjectId {get;set;}
public virtual Project {get;set;}
[ForeignKey("Person")]
public int? PersonId {get;set;}
public virtual Person {get;set;}
public string RelationshipType {get;set;}
}
Then your other 2 classes will look like this...
public class Project
{
[Key]
public int ProjectId { get; set; }
public string Name { get; set; }
// Foreign Key - Project lead (Person)
public int ProjectLeadId { get; set; }
public virtual Person ProjectLead { get; set; }
// Create many to many relationship with People - Team members on this project
public virtual ICollection<ProjectPerson> ProjectPeople { get; set; }
public Project()
{
ProjectPerson = new HashSet<ProjectPerson>();
}
}
And this..
Public class Person
{
[Key]
public int PersonId { get; set; }
public String Firstname { get; set; }
public String Surname { get; set; }
// Create many to many relationship
public virtual ICollection<ProjectPerson> ProjectPeople { get; set; }
public Person()
{
ProjectPerson = new HashSet<ProjectPerson>();
}
}
I've been lurking around for quite some time, so here's the first question ;)
I've been playing with Entity Framework 5.0 Code First and I want to do following:
I have two entities and I want every entity to have relation to Address entity in the following way:
I have one Address entity which stores address values, it doesn't have relation to entities for which it has values, instead
There's another entity AddressBook which has reference to Address entity and the coresponding entities (Person, Company, some others in the future)
Here's code:
public partial class Address : BaseEntity
{
[Key]
public int ID { get; set; }
public string Street { get; set; }
public string CityName { get; set; }
public int? PostalCode { get; set; }
public virtual ICollection<Person> Persons { get; set; }
public virtual ICollection<Company> Companies{ get; set; }
}
public partial class Person : BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public partial class Company: BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
What will this do is create database schema with tables:
Address
AddressPerson (with composite primary key)
Address_ID
Person_ID
AddressCompany
Address_ID
Company_ID
People
Companies
Here's what I want to do:
Address
AddressBook
Address_ID (PK)
Person_ID (FK)
Company_ID (FK)
People
Companies
What I want to do is have table like AddressBook:
public partial class AddressBook
{
[Key]
public int ID { get; set; }
public virtual Address Address { get; set; }
public virtual Person Person { get; set; }
public virtual Company Company { get; set; }
}
I'm not sure how to define navigational properties in Person and Company class.
They should have ICollection<Address> Addresses navigational property, because I want them to work just with collection of addresses without knowing about underlying AddressBook.
Is it possible to do this with DbModelBuilder or should I write code inside getter and setter of ICollection<Address> Addresses property and get addresses from AddressBook?
Thanks!
You cannot create a mapping in a way that Entity Framework would understand the Addresses collections in Person and Company as true navigation properties (which support eager and lazy loading and so on). For this you need indeed AddressBooks collections. You can add the Addresses as not mapped and readonly helper properties then:
public partial class Person : BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<AddressBook> AddressBookEntries { get; set; }
public IEnumerable<Address> Addresses
{
get { return AddressBookEntries.Select(ab => ab.Address); }
}
}
(The same with Company.)
An alternative and in my opinion better approach would be to create a common base class for Person and Company, move the Addresses collection into that base class and have a single many-to-many relationship and single join table between this base class and Address:
public abstract class EntityWithAddresses : BaseEntity
{
[Key]
public int ID { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
}
public partial class Person : EntityWithAddresses
{
}
public partial class Company : EntityWithAddresses
{
}
Address has a navigation collection to the new base class:
public partial class Address : BaseEntity
{
[Key]
public int ID { get; set; }
public string Street { get; set; }
public string CityName { get; set; }
public int? PostalCode { get; set; }
public virtual ICollection<EntityWithAddresses> EntityWithAddresses { get; set; }
}