EF 5.0 Code First Two way navigation withought foreign key id in child - entity-framework

I have following classes
public class Employer
{
[Key]
public Int64 EmployerID { get; set; }
public String CompanyName { get; set; }
public virtual List<Employee> Employees { get; set; }
}
public class Employee
{
[Key]
public Int64 EmployeeID { get; set; }
public String EmployeeName { get; set; }
public virtual Employer EmployerInfo { get; set; }
}
In the Database context I have set the relation as
modelBuilder.Entity<Employer>()
.HasMany(p => p.Employees)
.WithRequired()
.Map(x => x.MapKey("EmployerID"));
After executing some actions, database gets created with Employee table having EmployerID as foreign key and one extra key EmployerInfo_EmployerID.
Now when I fetch employer data, I am getting employee details with it.
But when I tried to fetch employee data I am getting EmployerInfo as null. This is because I need relationship from Employee to EmployerInfo.
How do I set the bi-directional relationship in this context?

You need to update your fluent so your relationship mapping contains both ends:
modelBuilder.Entity<Employer>()
.HasMany(p => p.Employees)
.WithRequired(e => e.EmployerInfo)
.Map(x => x.MapKey("EmployerID"));

Related

How to create grandparent foreign key in EFCORE

I have an employee, with one hourly paying job, each hourly has multiple timecards. I would like the timecards to link to both the employee and Hourly.
public class Employee
{
public int Id { get; set; }
}
public class Hourly
{
public int EmployeeId { get; set; }
public List<Timecard> Timecards{ get; set; }
}
public class Hourly
{
public int HourlyId{ get; set; }
public int EmployeeId { get; set; }
}
How do I specify this relationship in EF.
The code appears to set the employeeID but causes issues with the migration and the Hourly is now set to null.
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<Timecard>()
.HasOne<HourlyPC>()
.WithMany(pc => pc.Timecards)
.HasForeignKey(t => t.EmployeeId)
.HasPrincipalKey(pc => pc.EmployeeId);
}
Violates 3NF, meaning duplicated data that can lead to problems such as data anomalies. One hack would be to include the Employee FK in a composite PK for Job. That way, when a Timecard has a FK to Job, it also has a FK to Employee. Perhaps you could use a job code for a second field for inclusion in the composite Job PK or reference another entity, an example of which is below where Position is the normalized details of a Job w/o employee specific data (e.g. hourly rate) and Job relates an Employee to a Position with the employee-specific details:
public class Employee
{
public int Id { get; set; }
}
public class Job
{
public int EmployeeId { get; set; }
public Employee Employee { get; set; }
public string PositionId { get; set; }
public Position Position { get; set; }
public ICollection<TimeCard> TimeCards { get; set; }
public decimal HourlyRate { get; set; }
}
public class TimeCard
{
public Id { get; set; }
public int EmployeeId { get; set; }
public Employee Employee { get; set; }
public string PositionId { get; set; }
public Job Job { get; set; }
}
Config:
// configure Job
// configure relationshipt to Position
modelBuilder.Entity<Job>()
.HasOne(j => j.Position)
.WithMany()
.IsRequired();
// configure relationship to Employee
modelBuilder.Entity<Job>()
.HasOne(j => j.Employee)
.WithMany()
.IsRequired();
// create composite PK using the two FK's
modelBuilder.Entity<Job>()
.HasKey(j => new { j.EmployeeId, j.PositionId });
// configure TimeCard
// configure nav prop to Employee
modelBuilder.Entity<TimeCard>()
.HasOne(tc => tc.Employee);
// configure relationship with Job
modelBuilder.Entity<TimeCard>()
.HasOne(tc => tc.Job)
.WithMany(j => j.TimeCards)
.HasForeignKey(tc => new { tc.EmployeeId, tc.PositionId })
.IsRequired();
That might need a bit of tweaking but that's the nuts and bolts of it.

Asp net - Delete record from db using OnDelete()

Trying to create simple CRUD app using Asp Net Core. I have 2 entities:
Department and Employee( one to many ). I need to delete record from Department table. But when Im trying to delete record using OnDelete(DeleteBehavior.Restrict) or OnDelete(DeleteBehavior.ClientSetNull) i have exception:
UPDATE or DELETE in table"Departments" violates foreign key constraint
"FK_Employees_Departments_DepartmentCode" table"Employees"
How can i fix this problem ?
Entity Employee:
public class Employee
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required(ErrorMessage = "Input fullname of employee")]
public string FullName { get; set; }
[Required(ErrorMessage = "Input date of birth")]
public DateTime DateOfBirth { get; set; }
[Required(ErrorMessage = "Input code")]
public string Code { get; set; }
[Required(ErrorMessage = "Input fullname of employee")]
public int Salary { get; set; }
public string DepartmentCode { get; set; }
public Department Department { get; set; }
}
Entity Department:
public class Department
{
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
[Required(ErrorMessage = "Input name of department")]
public string Name { get; set; }
[Required(ErrorMessage = "Input code of department")]
public string Code { get; set; }
public ICollection<Employee> Employees { get; set; }
public Department()
{
Employees = new List<Employee>();
}
}
Context class settings:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Department>()
.HasMany<Employee>(d => d.Employees)
.WithOne(e => e.Department)
.HasForeignKey(e => e.DepartmentCode)
.OnDelete(DeleteBehavior.Restrict);
modelBuilder.Entity<Department>()
.HasKey(d => d.Code);
modelBuilder.Entity<Employee>()
.HasKey(e => e.Code);
modelBuilder.Entity<Department>()
.HasIndex(d => d.Name).IsUnique();
}
Of all fields in the DeleteBehavior enum only two actually add cascaded foreign key behavior to the database: Cascade and SetNull. All other options create foreign keys with no action on delete, but differ in what EF will do to its tracked entities.
In your case it should probably be SetNull because I assume that Employees can exist without Department. This setting will allow you to delete a Department object without loading its Employees. The database will set their DepartmentCode to null.
The delete behavior configured in EF can only be applied tho Entities that are tracked by EF change tracking. So you would need to load all Employees that belong to the department to make this work as expected.
BUT The database foreign key definition also defines the on delete action (cascading, set null, do nothing) So even if you code within your context a set null strategy, the DB still might apply different strategy for on delete. EF core defaults to cascade delete.

How to configure one to one relation using only fluent api without conventions

Is it possible to configure one to one relationship using fluent api on database which does not meet convention requirements?
Below I give you sample of database and generated models.
Be aware of that tables do not define any constraints and indices except primary keys.
Tables:
create table Person (
PersonKey int primary key
)
create table Address (
AddressKey int primary key,
owner int not null // normally should be foreign key to Person
)
Code first models generated from db:
public partial class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int PersonKey { get; set; }
}
public partial class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AddressKey { get; set; }
public int Owner { get; set; }
}
To be able to navigate from Address to Person, navigation property was added to Address class:
public partial class Address
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int AddressKey { get; set; }
public int Owner { get; set; }
public virtual Person Person { get; set; }
}
If program tries execute this query:
var Addresss = context.Addresss.Include(x => x.Person).ToList();
runtime raises exception: "Invalid column name 'Person_PersonKey'". Because context do not configure any custom mappings it tries to find foreign key by convention but Owner property does not meet convention requirements, hence the exception. So there is a need to add mappings.
If relationship between Person and Address would be one to many we could add such a configuration:
modelBuilder.Entity<Address>()
.HasOptional(x => x.Person)
.WithMany()
.HasForeignKey(x => x.Owner);
and query defined above would execute correctly. But what if Person class would have navigation property to Address so we would have bidirectional one to one relation:
public partial class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
public int PersonKey { get; set; }
public virtual Address Address { get; set; }
}
So above configuration will not work and my question is, is it possible to configure it without changing db and property names and if yes what configuration needs to be applied using only fluent api?
Here is my suggested code, I hope I understand you correctly!
public partial class Person
{
public int PersonKey { get; set; }
public Address Address {get;set;}
}
public partial class Address
{
public virtual Person Person { get; set; }
public int PersonId { get; set; }
public string AddressInfo {get;set;}
}
modelBuilder.Entity<Person>()
.HasKey(a => a.PersonKey);
modelBuilder.Entity<Course>()
.Property(c => c.CourseId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);
modelBuilder.Entity<Address>()
.HasKey(a => a.PersonId);
modelBuilder.Entity<Person>()
.HasRequired(p => p.Address)
.WithRequiredPrincipal(a => a.PersonId);

Entity framework 5 foreign key mapping convention

I have 2 entities Role and Permission with association one-to-many accordingly.
public class Role
{
public int Id { get; set; }
public bool IsAdmin { get; set; }
public virtual ICollection<Permission> Permissions { get; set; }
}
public class Permission
{
public int Id { get; set; }
public string Code { get; set; }
public string GroupName { get; set; }
public virtual Role Role { get; set; }
}
And created mapping classes for them inherited from EntityTypeConfiguration class.
When I run my application EF created database for me and foreign key for these entities above was Role_Id.
How can I change existing or add new convention to get ride of the underscore in foreign key?
So I want to have RoleId as a foreign key for my entities.
I don't want use data annotation attributes and don't want to add extra property to Permission class (public int RoleId { get; set; }) in order to use it in mapping like this:
HasRequired(x => x.Role).WithMany(y => y.Permissions).HasForeignKey(o => o.RoleId);
Thanks,
Alexey
Entity framework currently doesn't support custom global conventions but you can overwrite the name of the key in fluen API:
modelBuilder.Entity<Permission>()
.HasRequired(x => x.Role)
.WithMany(y => y.Permissions)
.Map(m => m.MapKey("RoleId"));

One to many relationship error

I have the following model, but I keep getting an error:
Unhandled Exception: System.InvalidOperationException: A relationship
multiplici ty constraint violation occurred: An EntityReference can
have no more than one r elated object, but the query returned more
than one related object. This is a no n-recoverable error.
public class Tournament
{
public long TournamentId { get; set; }
public string Title { get; set; }
public virtual User CreatedBy { get; set; }
}
public class User
{
public int UserId { get; set; }
}
modelBuilder.Entity<Tournament>()
.HasRequired(t => t.CreatedBy)
.WithOptional()
.Map(c => c.MapKey("CreatedById")); // correct column name
Your model fluent configuration entry is incorrect. Change it as follows
modelBuilder.Entity<Tournament>()
.HasRequired(t => t.CreatedBy)
.WithMany()
.Map(c => c.MapKey("CreatedById")); // correct column name
You'll have better luck managing Foreign keys if you modify you model a bit:
public class Tournament
{
public long TournamentId { get; set; }
public string Title { get; set; }
public virtual int CreatedById {get;set;}
public virtual User CreatedBy { get; set; }
}
and your mapping would look more like this:
modelBuilder.Entity<Tournament>()
.HasRequired(t => t.CreatedBy)
.WithMany()
.HasForeignKey(t => t.CreatedById); // correct column name
This way, when you create a new Tournament Entity you need only pass in the CreatedById and not the entire User object.
This can also happen if you have lazy loading enabled and not specifying all the navigation properties as Overridable (C# Virtual).