One or Zero to One Entity Framework Code First FluentApi - c#

I need to create fluentapi one or zero to one reference and have navigation properties on both of entities.
EntityTwo should contain simple proerty to store foreign key (EntityOneId)
public class EntityOne
{
public int Id { get; set; }
public EntityTwo EntityTwo { get; set; }
}
public class EntityTwo
{
public int Id { get; set; }
public int EntityOneId { get; set; }
public EntityOne EntityOne { get; set; }
}
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//some code trimmed
modelBuilder.Entity<EntityOne>()
.HasOptional(entity => entity.EntityTwo)
.WithRequired();
modelBuilder.Entity<EntityTwo>()
.HasRequired(entity => entity.EntityOne)
.WithMany()
.HasForeignKey(entity => entity.EntityOneId)
.WillCascadeOnDelete(false);
}
}
more complex scenario:
public class EntityOne
{
public int Id { get; set; }
public EntityTwo EntityTwo { get; set; }
}
public class EntityThree
{
public int Id { get; set; }
public EntityTwo EntityTwo { get; set; }
}
public class EntityTwo
{
public int Id { get; set; }
public int EntityOneId { get; set; }
public EntityOne EntityOne { get; set; }
public int EntityThreeId { get; set; }
public EntityThree EntityThree { get; set; }
}
public class MyDbContext : DbContext
{
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
//some code trimmed
modelBuilder.Entity<EntityOne>()
.HasOptional(entity => entity.EntityTwo)
.WithRequired();
modelBuilder.Entity<EntityThree>()
.HasOptional(entity => entity.EntityTwo)
.WithRequired();
modelBuilder.Entity<EntityTwo>()
.HasRequired(entity => entity.EntityOne)
.WithMany()
.HasForeignKey(entity => entity.EntityOneId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<EntityTwo>()
.HasRequired(entity => entity.EntityThree)
.WithMany()
.HasForeignKey(entity => entity.EntityThreeId)
.WillCascadeOnDelete(false);
}
}

In one-to-one relation one end must be principal and second end must be dependent. Principal end is the one which will be inserted first and which can exist without the dependent one. Dependent end is the one which must be inserted after the principal because it has foreign key to the principal. When configuring one-to-one relationships, Entity Framework requires that the primary key of the dependent also be the foreign key. The proper way to achieve what you want could be this, but is using Data Annotations:
public class EntityOne
{
public int Id { get; set; }
public virtual EntityTwo EntityTwo { get; set; }
}
public class EntityTwo
{
[Key, ForeignKey("EntityOne")]
public int EntityOneId { get; set; }
public virtual EntityOne EntityOne { get; set; }
}
I suggest you check this link, you can find there more info about how work the one-to-one relationships in EF Code First.
Update:
I am afraid that what you want is not possible.You can't create a one-to-one relation with a FK that is not declared as a PK. If you want to have each Entities with their own Id an configure an one-to-one relationship between that two entities, then delete the FK property in the EntityTwo.
My recomendation is map that relationship using Fluent Api as I show below:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<EntityTwo>()
.HasRequired(et => et.EntityOne)
.WithOptional(eo=>eo.EntityTwo);
}
Or you can just add the Required attribute over the navigation property that is principal, for example:
public class EntityTwo
{
public int Id { get; set; }
// public int EntityOneId { get; set; }
[Required]
public EntityOne EntityOne { get; set; }
}

The only way I've come up with to handle this is, which is admittedly somewhat ugly, is creating a collection and a helper property to represent the one/zero side. Data annotations included for clarity.
public class EntityOne
{
[Key]
public int EntityOneId { get; set; }
public EntityTwo EntityTwo => EntityTwoNavigation?.FirstOrDefault();
public ICollection<EntityTwo> EntityTwoNavigation { get; set; }
}
public class EntityTwo
{
[Key]
public int EntityTwoId { get; set; }
public int EntityOneId { get; set; }
[ForeignKey("EntityOneId")]
public EntityOne EntityOne { get; set; }
}

Related

Direct and Indirec many-to-many configuration using EF Core 5 using

I have the following entities
public class Course
{
public long Id { get; set; }
public virtual ICollection<User> Users{ get; set; }
public virtual ICollection<UserCourse> CourseUsers { get; set; }
}
public class User
{
public long Id { get; set; }
public virtual ICollection<Course> Courses { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class UserCourse
{
public long UserId { get; set; }
public User User { get; set; }
public long CourseId { get; set; }
public Course Course { get; set; }
public bool IsRequired { get; set; }
}
with the following mappings for
UserCourse mapping :
builder
.HasOne(nav => nav.User)
.WithMany(self => self.UserCourses)
.HasForeignKey(fk => fk.UserId)
.OnDelete(DeleteBehavior.Cascade);
builder
.HasOne(nav => nav.Course)
.WithMany(self => self.CourseUsers)
.HasForeignKey(fk => fk.CourseId)
.OnDelete(DeleteBehavior.Cascade);
and the User mapping
builder
.HasMany(nav => nav.Courses)
.WithMany(nav => nav.Users);
When trying to create a new migration I'm not exactly sure why I'm getting this.
Cannot use table 'UserCourse' for entity type 'UserCourse' since it is
being used for entity type 'UserCourse(Dictionary<string, object>)'
and potentially other entity types, but there is no linking
relationship. Add a foreign key to 'UserCourse' on the primary key
properties and pointing to the primary key on another entity typed
mapped to 'UserCourse'.
I understand what the error is, but not sure how to force the UserCourse mapping to use the User mapping generated join table or vice-versa
Also, I need the direcat mapping for OData, and the indirect mapping using the join entity to conduct operations on DbSet<UserCourse>
The public virtual ICollection<User> Users{ get; set; } in Course entity and the the public virtual ICollection<Course> Courses { get; set; } in Users entity are redundant. The entities should look more like this
public class Course
{
public long Id { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class User
{
public long Id { get; set; }
public virtual ICollection<UserCourse> UserCourses { get; set; }
}
public class UserCourse
{
public long UserId { get; set; }
public User User { get; set; }
public long CourseId { get; set; }
public Course Course { get; set; }
}
And the OnModelCreating method should have this code
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<UserCourse>()
.HasKey(uc => new { uc.UserId, uc.CourseId });
modelBuilder.Entity<UserCourse>()
.HasOne(uc => uc.Course)
.WithMany(c => c.Users)
.HasForeignKey(uc => uc.CourseId);
modelBuilder.Entity<UserCourse>()
.HasOne(uc => uc.User)
.WithMany(c => c.Courses)
.HasForeignKey(uc => uc.UserId);
}
If you use EF core 5 you can directly skip the join table. It will be generated and handled by EF behind the scenes. More on the topic here https://www.thereformedprogrammer.net/updating-many-to-many-relationships-in-ef-core-5-and-above/

The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Users_Agencies_UserID"

im using entity framework core 5.0 and i created my one to many relationship with fluent api.
im getting that error when i try to create a new user in my project.
let me show u to my User class:
public class User : Base
{
[Key]
public int UserID { get; set; }
public string UserName { get; set; }
public string UserSurname { get; set; }
public string UserPassword { get; set; }
public string UserEMail { get; set; }
public int? AgencyID { get; set; }
public virtual Agency Agency { get; set; }
}
public class UserConfiguration : IEntityTypeConfiguration<User>
{
public void Configure(EntityTypeBuilder<User> builder)
{
builder.HasKey(user => user.UserID);
}
}
and here its a Agency class which is related to User class:
public class Agency : Base
{
[Key]
public int AgencyID { get; set; }
public string AgencyName { get; set; }
public string AgencyPhoto { get; set; }
public string AgencyEMail { get; set; }
public string AgencyPhone { get; set; }
public int AgencyExportArea { get; set; }
public virtual ICollection<User> Users { get; set; }
}
public class AgencyConfiguration : IEntityTypeConfiguration<Agency>
{
public void Configure(EntityTypeBuilder<Agency> builder)
{
builder.HasKey(agency => agency.AgencyID);
builder.HasMany(us => us.Users)
.WithOne(us => us.Agency)
.HasForeignKey(au => au.UserID)
.IsRequired(false)
}
}
i know,im getting that error SqlException: The INSERT statement conflicted with the FOREIGN KEY constraint "FK_Users_Agencies_UserID". The conflict occurred in database "anan", table "dbo.Agencies", column 'AgencyID'. because there is a no data in Agency table. The thing which im trying to do is make that AgencyID foreign key optional as a nullable. in User class u can see i defined that AgencyID as a nullable.
do i really need to define that relationship as a one-to-one or zero or is there a another way to do that ?
if i have to define that relationship as a one-to-one or zero,can u show me the way how can i do that.
Since you are using EF core 5 you don't need:
public class UserConfiguration : IEntityTypeConfiguration<User>
and
public class AgencyConfiguration : IEntityTypeConfiguration<Agency>
All this code is reduntant. You have a standart one-to-many relation that EF core recognizes and configures by default. Remove all of this code and everything will be fine.
But if you are a student and need to do everything hard way, you can add this reduntant code:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>(entity =>
{
entity.HasOne(d => d.Agency)
.WithMany(p => p.Users)
.HasForeignKey(d => d.AgencyId)
.OnDelete(DeleteBehavior.ClientSetNull);
});
}
And since you are interested in a configuration, these are another redundant attributes:
public class User : Base
{
[Key]
public int UserID { get; set; }
.....
public int? AgencyID { get; set; }
[ForeignKey(nameof(AgencyId))]
[InverseProperty("Users")]
public virtual Agency Agency { get; set; }
}
public class Agency : Base
{
[Key]
public int AgencyID { get; set; }
.....
[InverseProperty(nameof(User.Agency))]
public virtual ICollection<User> Users { get; set; }
}

Composite key Entity Framework 7 "The Property Cannot Be added to Entity Type

I'm trying to make a enitity that manages membership of a user in a organization with a role. I want to restrict a user to have only one membership in an organization. I'm doing this by creating a composite key. However i get the error when i try to create the initial migrations:
InvalidOperationException: The property 'User' cannot be added to the entity type 'OrganizationLogin' because a navigation property with the same name already exists on entity type 'OrganizationLogin'.
The entity for membership
public class OrganizationLogin
{
public int OrganizationLoginId { get; set; }
public OrganizationRole Role { get; set; }
public Organization Organization { get; set; }
public OmegaUser User { get; set; }
}
My DBContext where I try to define the composite key:
public class OmegaContext : IdentityDbContext<OmegaUser,OmegaRole,int>
{
public DbSet<Log> Logs { get; set; }
public DbSet<Organization> Organizations { get; set; }
public DbSet<OrganizationLogin> OrganizationLogins { get; set; }
public DbSet<OrganizationRole> OrganizationRoles { get; set; }
public OmegaContext()
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<OrganizationLogin>(orgLogin =>
{
orgLogin.HasAlternateKey(o => new {o.User, o.Organization});
});
}
}
If i remove the OnModelCreating code, the migrations are created succesfully.
EDIT: As mentioned in the comments, the problem was that i was referencing the class and not a property that had the key of the entities
As requested, here is my solution:
public class OrganizationUnitMember
{
public int OrganizationUnitMemberId { get; set; }
public int UserId { get; set; }
public int OrganizationUnitId { get; set; }
[ForeignKey("UserId")]
public virtual OmegaUser User { get; set; }
[ForeignKey("OrganizationUnitId")]
public virtual OrganizationUnit OrganizationUnit { get; set; }
public int RoleId { get; set; }
[ForeignKey("RoleId")]
public virtual OrganizationRole Role { get; set; }
}
And the DbContext:
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<OrganizationUnit>(
orgUnit =>
{
orgUnit.HasOne(ou => ou.Parent)
.WithMany(ou => ou.Children)
.OnDelete(DeleteBehavior.Restrict)
.HasForeignKey(ou => ou.ParentId);
});
builder.Entity<OrganizationUnitMember>(member =>
{
member.HasAlternateKey(m => new {m.OrganizationUnitId, m.UserId});
});
}
I had to add the ids of the referenced entities

Unable to retrieve metadata and multiplicity is not valid role in Entity Framework

Is it necessary to specify all the required relationship for mapping in the fluent API?
I have four classes in my model. To be specific: they are => BUS, PASSENGER, ROUTE, RESERVATION.
public class Bus
{
[Key]
public int Id { get; set; }
[Display(Name="Route")]
public int RouteId { get; set; }
..............
[ForeignKey("RouteId")]
public virtual Route Route{ get; set; }
public virtual ICollection<Reservation> Reservations { get; set; }
}
public class Passenger
{
public int Id { get; set; }
.........
.........
public virtual Reservation Reservation { get; set; }
}
public class Reservation
{
public int Id { get; set; }
public int BusId { get; set; }
public int PassengerId { get; set; }
.................
..............
[ForeignKey("BusId")]
public virtual Bus Bus { get; set; }
[ForeignKey("PassengerId")]
public virtual Passenger Passenger { get; set; }
}
public class Route
{
public int Id { get; set; }
.............
.............
public virtual ICollection<Bus> Busses { get; set; }
}
I have modeled my DbContext File as below:
public class TicketContext: DbContext
{
public TicketContext()
: base("TicketDbContext")
{
}
public DbSet<Bus> Busses { get; set; }
public DbSet<Passenger> Passengers { get; set; }
public DbSet<Reservation> Reservations { get; set; }
public DbSet<Route> Routes { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure Code First to ignore PluralizingTableName convention
modelBuilder.Conventions.Remove<PluralizingTableNameConvention>();
//configure the primary key for "Bus" ENTITY
modelBuilder.Entity<Bus>()
.HasKey(p=>p.Id);
//configure one to many relationship between "Bus" and "Passenger" ENTITY
modelBuilder.Entity<Bus>()
.HasMany(p => p.Reservations)
.WithRequired(p => p.Bus);
//configure the primary key for "Passenger" ENTITY
modelBuilder.Entity<Passenger>()
.HasKey(p => p.Id);
//configure one to one relationship between "Passenger" and "Reservation" ENTITY
modelBuilder.Entity<Passenger>()
.HasRequired(i => i.Reservation)
.WithRequiredPrincipal(i => i.Passenger);
//configure the primary key for "Reservation" ENTITY
modelBuilder.Entity<Reservation>()
.HasKey(p => p.Id);
//configure the primary key for "Route" ENTITY
modelBuilder.Entity<Route>()
.HasKey(p => p.Id);
//configure one to many relationship between "Route" and "Bus" ENTITY
modelBuilder.Entity<Route>()
.HasMany(r => r.Busses)
.WithOptional(r => r.Route);
}
}
After that when I wanted to create a controller for Bus with model class Bus and with the DbContext specified visual studio gives me the following error.
What have I done wrong here? Do I have to specify relationship for all entity?

Entity Framework CodeFirst many to many relationship with additional information

I have the following model :
class Contract
{
string ContractID{get;set;}
ICollection<Part> Parts{get;set;}
}
class Part
{
string PartID{get;set;}
ICollection<Contract> Contracts{get;set;}
}
the problem is that the relationship between Part and Contract also contains the following additional information :
class ContractParts
{
Contract{get;set;}
Part{get;set;}
Date{get;set;} //additional info
Price{get;set;} //additional info
}
How would I write the Entity Context for this ?
In such case you must model your entities this way:
public class Contract
{
public virtual string ContractId { get; set; }
public virtual ICollection<ContractPart> ContractParts { get; set; }
}
public class Part
{
public virtual string PartId { get;set; }
public virtual ICollection<ContractPart> ContractParts { get; set; }
}
public class ContractPart
{
public virtual string ContractId { get; set; }
public virtual string PartId { get; set; }
public virtual Contract Contract { get; set; }
public virtual Part Part { get; set; }
public virtual string Date { get; set; } //additional info
public virtual decimal Price { get; set; } //additional info
}
In derived context you must define:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<ContractPart>()
.HasKey(cp => new { cp.ContractId, cp.PartId });
modelBuilder.Entity<Contract>()
.HasMany(c => c.ContractParts)
.WithRequired()
.HasForeignKey(cp => cp.ContractId);
modelBuilder.Entity<Part>()
.HasMany(p => p.ContractParts)
.WithRequired()
.HasForeignKey(cp => cp.PartId);
}
Perhaps a better way to do is this answer? Create code first, many to many, with additional fields in association table
It doesn't require fluent APIs and also sets up the PK on join table.

Categories

Resources