Entity Framework One-Many Relationship on Same Entity - entity-framework

I need to define Organizational chart schema in Entity Framework.
PersonelJob Entity model is:
public class PersonelJob : BaseEntity
{
public Int64 ID { get; set; }
public string Name { get; set; }
public Int64? ParentId { get; set; }
public virtual PersonelJob Parent { get; set; }
public virtual ICollection<PersonelJob> Childs { get; set; }
}
As you can see each job could be a job parent and have some job children.
How could map this entity to Database, with Fulent Api?

Override the OnModelCreating method on your context and add this configuration:
modelBuilder.Entity<PersonelJob>()
.HasOptional(pj => pj.Parent)
.WithMany(pj=>pj.Childs)
.HasForeignKey(pj => pj.ParentId);

Related

How can I name a navigation property a different name from it's entity name in my EF POCO?

I have a POCO Entity named Employee.
And then I have a second POCO Entity named Case.
I want a navigation property that looks like instead this:
public class Case : BaseEntity
{
public long EmployeeId { get; set; }
public virtual Employee Employee{ get; set; }
like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
public virtual Employee InitialContact { get; set; }
I want to name my property InitialContact. Not Employee.
But I get this error when EF tries to create the Database:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 1:
I got it to work like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
The primary key is ID in my BaseEntity. Not EmployeeId.
But I have second part to my question.
Here is my Complete Employee POCO:
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Hrsa.Core.Generic.Model.Framework.Concrete;
using Microsoft.AspNetCore.Mvc.ModelBinding;
namespace Hrsa.Core.Generic.Model.Lerd
{
public class Employee : BaseEntity
{
[BindNever]
public string Email { get; set; }
[BindNever]
public long OrganizationId { get; set; }
[BindNever]
public string Supervisor { get; set; }
[BindNever]
public string SupervisorEmail { get; set; }
[BindNever]
public string FirstName { get; set; }
[BindNever]
public string LastName { get; set; }
public string Notes { get; set; }
[BindNever]
public long BargainingUnitId { get; set; }
[BindNever]
public long PayPlanId { get; set; }
[BindNever]
public long GradeRankId { get; set; }
[BindNever]
public long PositionTitleId { get; set; }
[BindNever]
public long SeriesId { get; set; }
public bool IsUnionEmployee { get; set; }
public virtual Organization Organization { get; set; }
public virtual BargainingUnit BargainingUnit { get; set; }
public virtual PayPlan PayPlan { get; set; }
public virtual GradeRank GradeRank { get; set; }
public virtual PositionTitle PositionTitle { get; set; }
public virtual Series Series { get; set; }
public virtual ICollection<UnionHours> UnionHours { get; set; }
public virtual ICollection<Case> Cases { get; set; }
[NotMapped]
public string UnionEmployeeYesNo => (IsUnionEmployee) ? "Yes" : "No";
}
}
I want my Employee to have many Cases:
public virtual ICollection<Case> Cases { get; set; }
Here is my complete Cases POCO:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
}
So now my DB looks like this:
So I have my InitialContactId in Cases ok.
But now I need my Case to have many Employees.
So I add this in to my Case POCO:
public virtual ICollection<Employee> Employees { get; set; }
Now it looks like this:
public class Case : BaseEntity
{
public long InitialContactId { get; set; }
[ForeignKey("Id")]
public virtual Employee InitialContact { get; set; }
public DateTime InitalConsultDate { get; set; }
public Guid AppUserId { get; set; }
public virtual AppUser LerSpecialist { get; set; }
public virtual ICollection<Employee> Employees { get; set; }
}
Now when I run it, I get this error again:
Unable to determine the relationship represented by navigation property 'Case.InitialContact' of type 'Employee'. Either manually configure the relationship, or ignore this property from the model.
Update 2:
I found this article for a Many-Many relationship in .Net Core 1:
http://www.learnentityframeworkcore.com/configuration/many-to-many-relationship-configuration
So now I have a bridge lookup entity:
public class EmployeeCase
{
[ForeignKey("Id")]
public long EmployeeId { get; set; }
public Employee Employee { get; set; }
[ForeignKey("Id")]
public long CaseId { get; set; }
public Case Case { get; set; }
}
Employee POCO:
Changed:
public virtual ICollection<Case> Cases { get; set; }
to:
// Mapping - Collection of Cases
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
Case POCO:
Changed:
public virtual ICollection<Employee> Employees { get; set; }
to:
// Mapping - Collection of Employees
public virtual ICollection<EmployeeCase> EmployeeCases { get; set; }
In my AppDbContext
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
}
Now when I run I get this error:
An exception of type 'System.Data.SqlClient.SqlException' occurred in Microsoft.EntityFrameworkCore.Relational.dll but was not handled in user code
Additional information: Introducing FOREIGN KEY constraint 'FK_EmployeeCase_Employees_EmployeeId' on table 'EmployeeCase' may cause cycles or multiple cascade paths. Specify ON DELETE NO ACTION or ON UPDATE NO ACTION, or modify other FOREIGN KEY constraints.
Could not create constraint or index. See previous errors.
Update 3:
Finally got my tables the way I want with this piece of code from:
Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths - why?
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
// Get rid of Cascading Circular error on ModelBuilding
foreach (var relationShip in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
{
relationShip.DeleteBehavior = DeleteBehavior.Restrict;
}
#region Many-to-Many Employees Cases
modelBuilder.Entity<EmployeeCase>()
.HasKey(ec => new { ec.EmployeeId, ec.CaseId });
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Employee)
.WithMany(e => e.EmployeeCases)
.HasForeignKey(ec => ec.EmployeeId);
modelBuilder.Entity<EmployeeCase>()
.HasOne(ec => ec.Case)
.WithMany(c => c.EmployeeCases)
.HasForeignKey(ec => ec.CaseId);
#endregion
base.OnModelCreating(modelBuilder);
}
Update 4:
This did not work after all.
Remvoving the delete behavior for everything messes up my other relationships and I get errors.
How can I fix this?
This is disgusting.
So wishing I did not go Core.
Entity Framework uses conventions to guess how to map your C# model to database objects.
In your case you violate convention by custom name, so you should explain Entity Framework how to map this stuff.
There are two possible ways: attributes and fluent API. I'd suggest to use the latter one.
See section "Configuring a Foreign Key Name That Does Not Follow the Code First Convention" here: Entity Framework Fluent API - Relationships
I have made it a habit of explicitly defining my relationships as EF does not always get them the way I want. I like to create a Mapping folder that contains my entity maps. The fluent api works great for this and inherits from EntityTypeConfiguration.
Try this.
public class CaseMap : EntityTypeConfiguration<Case>
{
public CaseMap()
{
HasKey(m => m.Id)
HasRequired(m => m.InitialContact)
.WithMany(e => e.Cases)
.HasForeignKey(m => m.InitialContactId);
}
}
Almost forgot. You need to tell your DbContext where to find these mappings. Add this to your DbContexts OnModelCreating method.
modelBuilder.Configurations.AddFromAssembly(typeof(MyContext).Assembly);
This is what worked finally for the Cascading Delete circular references on the many-to-many in EF Core:
// Get rid of Cascading Delete Circular references error.
var type = modelBuilder.Model.GetEntityTypes().Single(t => t.Name == "Hrsa.Core.Generic.Model.Lerd.EmployeeCase");
foreach (var relationship in type.GetForeignKeys())
{
relationship.DeleteBehavior = DeleteBehavior.Restrict;
}
You have to get the Entity representing the many to many lookup only.
And from there restrict the DeleteBehavior.

The INSERT statement conflicted with the FOREIGN KEY constraint

I'm rather new to Entity Framework (code-first). Here are my two entities-
public class Employee
{
public Employee() { }
public long Id {get; set;}
public string Fullname {get; set;}
public virtual ICollection<Attendance> Attendances { get; set; }
}
public class Attendance
{
public Attendance() { }
public DateTime CheckinDateTime { get; set; }
public DateTime? CheckoutDateTime { get; set; }
public long EmployeeId { get; set; }
[ForeignKey("Id")]
public virtual Employee Employee{ get; set; }
}
Employee has one-to-many relation with Attendance.
I've tried to create a new Attendance data-
var attendance = new Attendance()
{ EmployeeId = 1,
CheckinDateTime = today.CurrentDateTime
};
DbContext.Attendances.Add(attendance);
DbContext.SaveChanges(); //Exception here.
I have an Employee record in database.
Why I'm getting the exception?
Code First enables you to describe a model by using C# or Visual Basic .NET classes. The basic shape of the model is detected by using conventions. Conventions are sets of rules that are used to automatically configure a conceptual model based on class definitions when working with Code First. The conventions are defined in the System.Data.Entity.ModelConfiguration.Conventions namespace.
You can further configure your model by using data annotations or the fluent API. Precedence is given to configuration through the fluent API followed by data annotations and then conventions. For more information see Data Annotations, Fluent API - Relationships, Fluent API - Types & Properties and Fluent API with VB.NET.
Here you find more about Entity Framework Code First Conventions
You set wrong ids name as FK and PK, you need add primary key for Attendance also,follow code first conventions name, change your model like:
public class Employee
{
public Employee()
{
Attendances = new List<Attendance>();
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long EmployeeId { get; set; }
public string Fullname { get; set; }
public virtual ICollection<Attendance> Attendances { get; set; }
}
public class Attendance
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public long AttendanceId { get; set; }
public DateTime CheckinDateTime { get; set; }
public DateTime? CheckoutDateTime { get; set; }
[Required]
[ForeignKey("Employee")]
public long EmployeeId { get; set; }
public virtual Employee Employee { get; set; }
}
ForeignKey attribute is applied on Attendance navigation property to specify foreignkey property name for Attendance property.
Without DataAnnotation we can use Fluent API for configuration our relationship. Ofcourse you need use code first convention names
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//one-to-many
modelBuilder.Entity<Attendance>()
.HasRequired<Employee>(e => e.Employee) // Attendance entity requires Employee
.WithMany(a => a.Attendances); // Employee entity includes many Attendances entities
}
If your model not contains convention name, using Fluent API can use .HasForeignKey() and set specific name FK
public class Attendance
{
public long AttendanceId { get; set; }
public DateTime CheckinDateTime { get; set; }
public DateTime? CheckoutDateTime { get; set; }
//Not first code convention name
public long EmpId { get; set; }
public virtual Employee Employee { get; set; }
}
.
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
//one-to-many
modelBuilder.Entity<Attendance>()
.HasRequired<Employee>(e => e.Employee)
.WithMany(a => a.Attendances)
.HasForeignKey(e => e.EmpId);
}

How do i create One-to-One to relationship without having navigation property in dependent entity

I understand the following code creates "One-to-One relationship" between a principal and a dependent entity.
However, I would like to ask:
Is it possible to create one-to-one relationship without including navigation property in the dependent entity?
If yes, than how should I re-write the following code?
public class Student
{
[Key]
public int Id { get; set; }
public string FullName { get; set; }
public StudentReport StudentReport { get; set; }
}
public class StudentReport
{
[Key, ForeignKey("Student")]
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public Student Student { get; set; }
}
To create a one-to-one relationship without a navigation property on the dependent side, you'll need to use the fluent API. For example, in your DbContext class, you can override OnModelCreating and use this to define the relationship:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// I'm assuming the report is optional
modelBuilder.Entity<Student>()
.HasOptional(t => t.StudentReport)
.WithRequired();
}
public class StudentReport
{
public int Id { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
}
See documentation for WithRequired() here

Navigation Properties using two foreign keys

I have a project with several tables in the same database.
public class UserImage
{
[Key]
public int Id { get; set; }
public string OwnerId { get; set; }
[ForeignKey("OwnerId")]
public virtual ApplicationUser Owner { get; set; }
//some fields are removed for brevity
}
public class FriendRequest
{
[Key]
public int Id { get; set; }
public string UserId { get; set; }
public string FutureFriendUserId { get; set; }
[ForeignKey("UserId")]
public virtual ApplicationUser User { get; set; }
[ForeignKey("FutureFriendUserId")]
public virtual ApplicationUser FutureFriendUser { get; set; }
}
public class ApplicationUser : IdentityUser
{
//some fields are removed for brevity
public virtual ICollection<UserImage> UserImages { get; set; }
public virtual ICollection<FriendRequest> FriendRequests { get; set; }
The problem is that I can find the images that belong to a user:
var userStore = new UserStore<ApplicationUser>(db);
var userManager = new UserManager<ApplicationUser>(userStore);
ApplicationUser user = userManager.FindById(User.Identity.GetUserId());
IEnumerable<string> imgs = (from image in user.UserImages select Url.Content(image.ImageUrl)).Skip(skip).Take(5).ToList();
but I can't use the same technique for the FriendRequests. If I search in the database for the rows that have UserId == User.Identity.GetUserId() or some other id, the results are what I expect.
What is the problem?
What you're essentially creating here is a self-referential many-to-many relationship. On your FriendRequest class, you have two properties that are foreign keys to ApplicationUser, but on your ApplicationUser class, you have only a single collection of FriendRequest. Entity Framework has no idea which foreign key should actually compose this collection. As a result, you have to make a few changes to get this working properly.
You must add another navigation property. Essentially, on your ApplicationUser class you'll end up with something like the following:
public virtual ICollection<FriendRequest> SentFriendRequests { get; set; }
public virtual ICollection<FriendRequest> ReceivedFriendRequests { get; set; }
Again, you need a collection for each foreign key.
You'll need to add some fluent config to help Entity Framework determine which foreign key to use for each collection:
public class ApplicationContext : DbContext
{
...
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<FriendRequest>().HasRequired(m => m.User).WithMany(m => m.SentFriendRequests);
modelBuilder.Entity<FriendRequest>().HasRequired(m => m.FutureFriendUser).WithMany(m => m.ReceivedFriendRequests);
}

How should I model one-to-many relation with EntityFramework 4 to work with migration and ASP MVC

I'm trying to use ASP MVC 4 and Entity Framework 4 to create pretty simple web site.
I need to use the migration feature because I will deploy the application to shared hosting (GoDaddy) and I don't want to manually change tables on each change.
What is the correct way to model one-to-many relations? Using the other entity type or the other entity's primary key type?
When I use the other entity type, which is preferred because it keeps the model cleaner, the migration tools worked but the scaffolding of ASP MVC did not. Even when I've manually add drop down to select the other entity ASP MVC did not parse the request right and did not set the other entity property.
This is the two options:
Option1: Use other entity type.
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
public Tenant Tenant { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
}
Option 2: use primary key type.
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
public string TenantID { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
}
I've create MVC controller with scaffolding for the Survey entity in my ASP MVC 4 project. It create the CRUD controller and views. In the view it did not put any field for the Tenant.
After I've add it myself the method Create(Tenant tenant) was called but the Tenant field that was sent by the HTML form did not get parsed by MVC and did not set the Tenant field of the Survey entity.
Ido
These look like you are mapping one-to-one relationships and not one-to-many. If one Survey can have multiple Tenants then:
public class Tenant {
[Key]
public string TenantID { get; set; }
public string Name { get; set; }
public virtual Survey Survey {get; set;}
}
public class Survey {
[Key]
public string SurveyID { get; set; }
[Required]
[StringLength(100, MinimumLength=5)]
public string Title { get; set; }
[Required]
public DateTime CreatedAt { get; set; }
public virtual ICollection<Tenant> Tenant {get; set;}
}
I've found this series of posts which explain how to make EF models so that they will work with both EF and ASP MVC.
The idea is to have both "plain" reference type and strong reference type.
public class Team
{
public int TeamId { get; set; }
// ... other Team properties go here
// Each Team has an optional "next opponent" which is another Team
public int? NextOpponentId { get; set; }
[ForeignKey("NextOpponentId")] public virtual Team NextOpponent { get; set; }
// Each Team also has a required Manager and Administrator, both of which are people
public int ManagerId { get; set; }
public int AdministratorId { get; set; }
[ForeignKey("ManagerId")] public virtual Person Manager { get; set; }
[ForeignKey("AdministratorId")] public virtual Person Administrator { get; set; }
}