Can you help me Many to One Relation for it? - c#

modelBuilder.Entity<Food>(food =>
{
food.ToTable("foods");
food.Property(e => e.FoodCategoryId).HasColumnName("food_category_id").IsRequired();
food.HasOne(e => e.FoodCategory).WithMany().HasForeignKey(e =>
e.FoodCategoryId).HasConstraintName("fk_foods_food_categories_id");
});
I wrote one to many relation.But i need many to one

One to many and many to one are the same definition but this article Configuring One To Many Relationships in Entity Framework Core can help you to do this.
The following model represents companies and employees with an inverse navigation property defined in the dependent entity (Employee) but no matching foreign key property in the dependent
note: you can access the Foreign key from navigation property
public class Company
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Employee> Employees { get; set; }
}
public class Employee
{
public int Id { get; set; }
public string Name { get; set; }
public Company Company { get; set; }
}
A company has many employees, each with one company. That relationship is represented as follows:
// This method belongs to your context class which is inherited from DbContext
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasMany(c => c.Employees)
.WithOne(e => e.Company);
}
It can also be configured by starting with the other end of the relationship:
// This method belongs to your context class which is inherited from DbContext
protected override void OnModelCreating(Modelbuilder modelBuilder)
{
modelBuilder.Entity<Employee>()
.HasOne(e => e.Company)
.WithMany(c => c.Employees);
}

Related

EF Core, Should I add foreign key Id in class?

I read some tutorials from different site, some add foreign key Id and some don't and other add virtual.
For example:
class Class
{
public int Id { get; set; }
public string ClassName { get; set; }
public List<Student> Students { get; set; }
}
class Student
{
public int Id { get; set; }
public string StudentName { get; set; }
public Class class { get; set; }
}
Here in the Student class, some using virtual on Class and some using ClassId inside the Student class.
What is the best practice for this? I am using EF Core 3 with .NET Core 3.1 (latest one)
The pattern is called "Shadow-property". Let's say we want to get all Students by a ClassId.
With Shadow Property, 2 options to access data:
context.Classes.Include(x => x.Students).FirstOrDefault(x => x.Id == classId)?.Students;
//we have to know the name of foreign key from db.
context.Students.Where(x => EF.Property<int>(x, "ClassId") == classId).ToList();
With explicit Foreign Key, the code is:
context.Students.Where(x => x.ClassId == classId).ToList();
The 2nd & 3rd code avoid a join, looks better performance. but which one is best of 2 styles? it really depends on project and coding-style preference.
although the 2nd style is easy to setup:
class MyContext : DbContext
{
public DbSet<Student> Students { get; set; }
public DbSet<Class> Classes { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Add the shadow property to the model
modelBuilder.Entity<Student>()
.Property<int>("ClassId");
// Use the shadow property as a foreign key
modelBuilder.Entity<Student>()
.HasOne(p => p.Class)
.WithMany(b => b.Students)
.HasForeignKey("ClassId");
}
}
Here are 2 references:
https://learn.microsoft.com/en-us/ef/core/modeling/shadow-properties
https://learn.microsoft.com/en-us/ef/core/modeling/relationships?tabs=fluent-api%2Cfluent-api-simple-key%2Csimple-key
In the context of EF, marking a property as virtual allows EF to use lazy loading to load it. For lazy loading to work EF has to create a proxy object that overrides your virtual properties with an implementation that loads the referenced entity when it is first accessed. If you don't mark the property as virtual then lazy loading won't work with it.
For web applications, maybe it's better not to use lazy-loading because lazy-loading can be pulling a huge amount of data.
Assuming you want to generate your database model/relations from your objects/classes (code-first migrations with EF core), the most important part is your entities' mapping configurations (either with annotations on your model properties, or using Fluent API).
If you don't have any specific requirements regarding your "Id" property format, just set it to be generated by the database.
In your case, I would configure it the following way:
class Class
{
public int ClassId { get; set; }
public string ClassName { get; set; }
public ICollection<Student> Students { get; set; }
}
class Student
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int ClassId { get; set; }
public Class class { get; set; }
}
And, one way to configure your mappings with Fluent API could be something like:
public class StudentConfiguration : IEntityTypeConfiguration<Student>
{
public void Configure(EntityTypeBuilder<Student> builder)
{
builder
.HasKey(s => s.StudentId);
builder
.Property(s => s.StudentId)
.UseIdentityColumn();
builder
.Property(c => c.StudentName)
.HasMaxLength(<studentName_maxLength>);
builder
.HasOne(s => s.Class)
.WithMany(c => c.Students)
.HasForeignKey(s => s.ClassId);
builder
.ToTable("Student");
}
}
public class ClassConfiguration : IEntityTypeConfiguration<Class>
{
public void Configure(EntityTypeBuilder<Class> builder)
{
builder
.HasKey(c => c.ClassId);
builder
.Property(c => c.ClassId)
.UseIdentityColumn();
builder
.Property(c => c.ClassName)
.HasMaxLength(<className_maxLength>);
builder
.HasMany(c => c.Students)
.WithOne(s => s.Class);
builder
.ToTable("Class");
}
}
More details regarding configuring Fluent API one-to-many mappings can be found here

Error while generating migration for One to One relation using EFCore

I have two models as below:
public class Person{
public virtual int Id { get; set; }
public virtual int BaseId { get; set; }
public virtual string Name { get; set; }
public virtual Employee Employee { get; set; }
}
public class Employee{
public virtual int Id { get; set; }
public virtual string Code{ get; set; }
public virtual Person Person { get; set; }
}
Every Employee is a Person, but every Person is not necessarily an Employee. Relation between these two is type of One-One relation, but I need to make this relation between a non-primary key column(Person.BaseId) and the desired foreign key column(Employee.Id). In face the Id column in Employee model is the primary key and foreign key column at the same time.
I have this mapping configuration:
public override void Configure(EntityTypeBuilder<Person> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable("tblPeople", "dbo");
builder
.HasOne(p => p.Employee)
.WithOne(p => p.Person)
.HasForeignKey<Employee>(p => p.Id)
.HasPrincipalKey<Person>(p => p.BaseId);
}
public override void Configure(EntityTypeBuilder<Employee> builder)
{
builder.HasKey(x => x.Id);
builder.ToTable("tblEmployees", "dbo");
}
When I try to generate the migration I get the following error:
The child/dependent side could not be determined for the one-to-one
relationship between 'Employee.Person' and 'Person.Employee'. To
identify the child/dependent side of the relationship, configure the
foreign key property. If these navigations should not be part of the
same relationship configure them without specifying the inverse. See
http://go.microsoft.com/fwlink/?LinkId=724062 for more details.
I do not want to use the Data Annotation approach to solve this problem.
I found the solution, mistake was in my EntityTypeConfiguration classes design. I had the following design:
For the case of simplicity I have cropped the code
public abstract class BaseMap<T, U> : IEntityTypeConfiguration<T> where T: BaseEntity<U>
{
public virtual void Configure(EntityTypeBuilder<T> builder)
{
builder.HasKey(x => x.Id);
}
}
public abstract class ChildBaseMap<T, U> : BaseMap<T, U> where T: ChildBaseEntity<U>
{
public virtual void Configure(EntityTypeBuilder<T> builder)//<== virtual is wrong here as I need to override the parent Configure
{
base.Configure(builder);
builder.Property(x => x.BaseId).IsRequired();
}
}
public class PersonMap : ChildBaseMap<Person, int>
{
public override void Configure(EntityTypeBuilder<Person> builder)
{
....
Just override the Configure method, instead of define it as virtual in ChildBaseMap class solve the problem!

Many to Many Self Reference

An user can manage many company. And an user can have an active company at a time from list of many-company. How I can achieve this?
My current model is :
public class User
{
public int Id
public virtual ICollection<Company> Companies { get; set; }
public User()
{
this.Companies = new HashSet<Company>();
}
}
public class Company
{
public int Id
public virtual ICollection<User> Users { get; set; }
public User()
{
this.Users = new HashSet<User>();
}
}
If I add another anotation :
public class User
{
public int CompanyId
public virtual Company Company { get; set; }
}
This model wont referenced to many-to-many table which UserCompany table.
How I can achieve this? Or is there another approach for this case?
Now I'm thinking about make manual many-to-many relationship model and add another ActiveCompany field referenced from custom many-to-many relationship. Is this good approach?
I'm pretty sure you can do this by using the fluent api and overriding the OnModelCreating method in your DbContext, but I don't have an example to hand.
However you might just find it easier to add another property for your user
public int ActiveCompanyId { get; set; }
There are a lot of variables like if you are using lazy loading, how many companies / users there are, what your patterns of data access are, which may determine your best overall approach. If your Companies property is usually or always populated then you could create a get only property that is not mapped:
public class User
{
public int Id
public virtual ICollection<Company> Companies { get; set; }
public int ActiveCompanyId { get; set; }
[NotMapped]
public Company ActiveCompany
{
get
{
return this.Companys == null ? null :
Companies.Where(x => x.Id == this.Active.CompanyId)
.SingleOrDefault;
}
}
public User()
{
this.Companies = new HashSet<Company>();
}
}
Can you try this mapping :
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Company>()
.HasMany(c => c.Users)
.WithMany(u => u.Comapnies)
.Map(x =>
{
x.MapLeftKey("CompanyId");
x.MapRightKey("UserId");
x.ToTable("UserCompany");
});
modelBuilder.Entity<User>()
.HasRequired(b => b.Company)
.WithMany()
.WillCascadeOnDelete(false);
}

One-to-one relationship in CF Entity Framework using mapping

I'm using Entity Framework 5, Code-First.
I've two domain objects (or tables). 1st is User, and 2nd is UserProfile. One user can have only one profile, and one profile belongs to only one user. That is 1-1 relationship.
Here are the classes.... (I simplified the code to make it understandably, It is actually more complex)
User
public class User {
public virtual Int64 UserId { get; set; }
public virtual UserProfile UserProfile { get; set; }
public virtual String Username{ get; set; }
public virtual String Email { get; set; }
public virtual String Password { get; set; }
}
UserProfile
public class UserProfile {
public virtual Int64 UserId { get; set; }
public virtual User User { get; set; }
public virtual Int64 Reputation { get; set; }
public virtual String WebsiteUrl { get; set; }
}
Here are the Maps....
UserMap
public UserMap() {
this.Property(t => t.Email)
.IsRequired()
.HasMaxLength(100);
this.Property(t => t.Password)
.IsRequired()
.HasMaxLength(15);
this.Property(t => t.Username)
.IsRequired()
.HasMaxLength(15);
}
UserProfileMap
public UserProfileMap()
{
this.HasKey(t => t.UserId);
}
Here is the Context....
public class TcContext : DbContext {
static TcContext () {
Database.SetInitializer(new TcContextInitializer());
}
public DbSet<User> Users { get; set; }
public DbSet<UserProfile> UserProfiles { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder) {
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
modelBuilder.Configurations.Add(new UserMap());
modelBuilder.Configurations.Add(new UserProfileMap());
}
}
And here is my error message....
Unable to determine the principal end of an association between the types 'Tc.Domain.UserProfile' and 'Tc.Domain.User'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
I think in this way EF should determine the relationship automatically. But it gives me the above error message. I've researched this problem for a while but can't find a good illustration of the problem in my case.
Where is my mistake? Or, should I define some sort of additional relations in maps?
I had to modify the UserMap class as follows
public UserMap() {
this.Property(t => t.Email)
.IsRequired()
.HasMaxLength(100);
this.Property(t => t.Password)
.IsRequired()
.HasMaxLength(15);
this.Property(t => t.Username)
.IsRequired()
.HasMaxLength(15);
this.HasOptional(t => t.UserProfile)
.WithRequired(t => t.User);
}
It essentially says:
"User has an optional entity UserProfile which in turn has a required User entity"
The definition of the key in UserProfile is a must here.
Just looked at it again, do you need to add HasKey to UserMap so it knows which one is the key otherwise you could do the following :-
Are you able to change UserProfile to inherit from User and then add this to your OnModelCreating method
modelBuilder.Entity().ToTable("UserProfile");
this will create your one to one relationship on the database. I had to do this with my current model to get one to one otherwise if you do not specify the ToTable part it will lump them into one table with a Discriminator column
cheers Mark

Entity Framework 4.1 InverseProperty Attribute and ForeignKey

I will create two references between Employee and Team entities with foreign keys.
So I defined two entities as follow
public class Employee
{
public int EmployeeId { get; set; }
public string Name { get; set; }
[ForeignKey("FirstTeam")]
public int FirstTeamId { get; set; }
[InverseProperty("FirstEmployees")]
public virtual Team FirstTeam { get; set; }
[ForeignKey("SecondTeam")]
public int SecondTeamId { get; set; }
[InverseProperty("SecondEmployees")]
public virtual Team SecondTeam { get; set; }
}
public class Team
{
public int Id { get; set; }
public string TeamName { get; set; }
[InverseProperty("FirstTeam")]
public virtual ICollection<Employee> FirstEmployees { get; set; }
[InverseProperty("SecondTeam")]
public virtual ICollection<Employee> SecondEmployees { get; set; }
}
I thought it is correct theoretically, but it shows the Exception as follow :
{"Introducing FOREIGN KEY constraint 'Employee_SecondTeam' on table 'Employees' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.\r\nCould not create constraint. See previous errors."}
Can anybody help me?
Thanks in advance
Kwon
It is theoretically correct but SQL server (not Entity framework) doesn't like it because your model allows single employee to be a member of both First and Second team. If the Team is deleted this will cause multiple delete paths to the same Employee entity.
This cannot be used together with cascade deletes which are used by default in EF code first if you define foreign key as mandatory (not nullable).
If you want to avoid the exception you must use fluent mapping:
public Context : DbContext
{
public DbSet<Employee> Employees { get; set; }
public DbSet<Team> Teams { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Employee>()
.HasRequired(e => e.SecondTeam)
.WithMany(t => t.SecondEmployees)
.HasForeignKey(e => e.FirstTeamId)
.WillCascadeOnDelete(false);
...
}
}
This will result in scenario where you must delete members of SecondTeam manually before you delete the team.
All is correct in previous answer, but one thing is wrong
modelBuilder.Entity<Employee>()
.HasRequired(e => e.SecondTeam)
.WithMany(t => t.SecondEmployees)
.HasForeignKey(e => e.SecondTeamId) // mistake
.WillCascadeOnDelete(false);
FirstTeamId instead of SecondTeamId will cause that in SecondTeam navigation property will be always FirstTeam

Categories