Entity Framework Code first mapping - Non foreign key - entity-framework

I came up with following scenario today, where i find difficulty in configuring code first fluent mapping
public class Employee
{
public int Id { get; set; }
public string ReferralCode { get; set; }
public string ReferedByCode{ get; set; }
public virtual ICollection<Employee> ReferedEmployee { get; set; }
public virtual Employee ReferedBy { get; set; }
}
Each employee will be referred by some other employee, like that employee will have many referred employees. The referral will be based on Employee Code not on ID.
How to configure fluent mapping for this?

I do not believe it is possible to configure a navigation property to have its foreign key related to a column/property other than the primary key of the related entity.
Your options are:
Change the primary key to ReferralCode
Keep the primary key as Id and do the following:
Add a ReferredById column and use this as the foreign key.
Enable migrations (if they aren't already enabled).
Add a migration for the addition of the ReferredById column and edit this so that it runs a SQL script in a call to the Sql() method to update the value in this new column on the basis of the existing ReferredByCode column.

Related

How to change foreign key suffix in Entity Framework Core?

In EF Core with a code-first approach, by default column referencing another entity has an Id suffix - for example PersonId.
Is it possible - and if so, how? - to change it to _id, so to person_id?
Create the foreign key explicitly under the name you want - in your case Parent_Id. Keep a navigation property and foreign key property.
public int Parent_ID { get; set; }
public virtual Parent Parent { get; set; }
Map the foreign key relations using .HasForeignKey(). Something similar as below
builder.HasOne(d => d.Prop)
.WithMany(p => p.NavigationProp)
.HasForeignKey(d => d.ForeignKeyProp)
.OnDelete(DeleteBehavior.ClientSetNull)
.HasConstraintName("FK_ConstraintName");
If you prefer data annotation, you could also use
[Column("Parent_ID")]
public int ParentID { get; set; }
To add to WisdomSeeker's answer, you can use a [ForeignKey] annotation to point at a shadow property for the FK.
Given a class like a Course with a Person reference for a Teacher:
public class Course
{
[Key]
public int Id {get; set;}
// other fields.
[ForeignKey("person_id")]
public virtual Person Teacher { get; set; }
}
Alternatives as above would be:
[ForeignKey("Teacher")]
public int person_id { get; set; } // Not recommended naming convention in code.
public virtual Person Teacher { get; set; }
or
[Column("person_id"), ForeignKey("Teacher")]
public int TeacherId { get; set; }
public virtual Person Teacher { get; set; }
I generally avoid adding FK fields into classes as this leads to two sources of truth for what Teacher is assigned to a course. You have course.TeacherId and course.Teacher.Id, which could differ on update prior and after a SaveChanges. Shadow properties help avoid confusion and keep data updates consistent.
Using [Column] is common in Db-First implementations where you want to use a C# naming convention for properties to use in-code, but abide by existing/desired DB naming conventions in the database. I don't generally recommend using DB naming conventions in C# classes.

entity-value object, one to many relationship using entity framework fluent api

I have employee Entity with property Jobs as foreign key
public class Employee
{
public Guid Id { get; set; }
public ICollection<Job> Jobs { get; set; }
}
and Job (Shared Value Object in DDD)
public class Job
{
public Guid Code { get; set; }
}
the Code property isn't unique itself and i should include Employee_Id column in Jobs table in database to be unique. but i don't want Employee_Id be in model.
using fluent api i used HasMany method on employee (as following) then Employee_Id was included in Jobs table
modelBuilder.Entity<Employee>().HasMany(e => e.Jobs);
but i cannot became it primary key. is there any way doing this?
I know in ddd matter probably i should define a relation table between Employee and Job.

Name generated for foreign key overwrites existing column name

I am using Code First EF v6.0. I have a table MyTable, which should have 3 columns:
Id (the private key), Name and OtherTable_Name (the foreign key).
[Key]
public int Id { get; set; }
[Index(IsUnique = true)]
[MaxLength(400)]
public string Name { get; private set; }
public virtual OtherTable ARandomStringName { get; set; }
I was getting a weird bug where Name was populated with the value of OtherTable_Name, and no foreign key was generated.
I renamed the private key of OtherTable to OtherTableName. Now I get a database table which has Id, Name, and OtherTable_OtherTableName.
This solves my immediate problem - missing foreign key, incorrect Name value - but I don't understand why it was happening in the first place. (And I'd rather be able to refer to the field sensibly as OtherTable.Name, not OtherTable.OtherTableName.)
When does EF prefix foreign key column names with OtherTable_, and when does it not? This bug has not happened on any pair of tables where both have "Id" as the private key column - is it anything to do with the field being the private key in one table and not the other? What should I be doing to avoid this kind of bug happening elsewhere?
You can use DataAnnotations to override EF conventions. Make sure you have a reference to
using System.ComponentModel.DataAnnotations
then simply add the ForeignKey attribute to your NavigationProperty
public int OtherTableId { get; set; }
[ForeignKey("OtherTableId")]
public virtual OtherTable OtherTableItem { get; set; }

EF: Unable to determine the principal end of an association between the types

Unable to determine the principal end of an association between the types. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
Models:
`
[Table("Employees")]
public class Employee : Entity
{
public string Name { get; set; }
public int? AbsenceId { get; set; }
[ForeignKey("AbsenceId")]
public virtual Absence Absence { get; set; }
}
[Table("Absences")]
public class Absence : Entity
{
public DateTime From { get; set; }
public DateTime To { get; set; }
public string Reason { get; set; }
public int? SubstituteId { get; set; }
[ForeignKey("SubstituteId")]
public virtual Employee Substitute { get; set; }
}
`
The Employee have a Absence that can have a Employee that is not same Employee that have a Absence mentioned.
Any solution for this case?
Well, first of all.. You do not need to specify ForeignKey when you are following the Entity Framework conventions. By convention, EF will reocognize the fact that your Navigation property is called Foo and your ForeignKey will be called FooId.
However, the real problem is that you are attempting to create a 1:1 association between two entities and EF does not support associations like this.
EF only supports 1:1 associations with shared primary keys, that is where both tables have the same primary key and one table's PK is a FK to the other table's PK.
If you think about this, it makes sense. There is no native 1:1 relationship in SQL that does not have a shared primary key. If you add a FK in one table to the other, it creates a 1:Many. You can simulate a 1:1 by creating a unique constraint on the FK but EF does not support constraints.
Looking at your model. Do you really want a 1:1 anyways? Can an employee really only have a single absence? Ever? Probably not. You probably want Absence to be a 1:Many. So remove AbsenceId and change Absence to:
public virtual List<Absence> Absences { get; set; }

Fluent mapping for EF when foreign key not suffixed with ID

How to implement fluent mapping for the below scenario, I tried but it ends in vain.
I have two table Product and State, Product have column name State which hold StateCode like "WA", "NJ" etc which of string type. So i need to populate the State column into StateCode and the related State object into State property of the product entity.
Below is the classes i am using. I don't want to change the columns of table
public class Product
{
public int ID { get; set; }
public State State { get; set; }
public string StateCode { get; set; }
public string Description { get; set; }
}
public class State
{
public int Id { get; set; }
public string Code{get;set;}
public string Description{get;set;}
}
I tried the below mapping for Product
this.Property(t => t.StateCode).HasColumnName("State");
HasRequired(t => t.State).WithMany().HasForeignKey(t => t.StateCode);
No, currently it's not possible to have a Foreign Key column that refers to non PK in entity framework. Check this feature suggestion.
If you really want to have that feature, you need to have custom Seed that execute.
alter table Products add constraint FK_Products_States foreign key(State) references States(Code)
But you will not be able to populate State object. Putting public State State { get; set; } property will automatically create a Foreign Key column State_ID that refers to States::ID.
Otherwise you need to change the StateCode to be StateId (integer) that refers to State::Id.
The type of ForeignKey and PrimaryKey of referenced table must be the same. So you need to set State class Id property type to string. In EF you can only use foreig keys pointing to primary keys.