How to configure one to one relation using only fluent api without conventions - entity-framework

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);

Related

Foreign table using EF core

I have a advertiser model like:
public class Advertiser
{
public int AdvertiserId { get; set; }
public string Name { get; set; } = string.Empty;
public Address AddressId { get; set; }
}
Inside this class I have a builder as:
public class AdvertiserConfiguration : IEntityTypeConfiguration<Advertiser>
{
public void Configure(EntityTypeBuilder<Advertiser> builder)
{
builder.ToTable("Advertisers");
builder.HasKey(x => x.AdvertiserId);
builder.Property(x => x.Name).IsRequired().HasMaxLength(250);
builder.HasOne(x => x.AddressId);
}
}
And address model like:
public class Address
{
public int AddressId { get; set; }
....
}
So that I want to do is a simple foreign key on the Advertiser table so I check msdn reference
And it says that I should use HasOne and WithMany methods in order to use HasForeignKey, but I do not understand why? it is necessary to use them to do a simple foreign key connection? if yes, what fields should I use on HasOne and WithMany? Thanks!
In ef for a relation you define a "navigation property" on both sides of the related objects and a "foreign key property". So your entities should look like this
public class Advertiser
{
public int AdvertiserId { get; set; }
public Address? Address { get; set; }
public int AddressId { get; set; }
...
}
public class Address
{
public int AddressId { get; set; }
public virtual ICollection<Advertiser>? Advertisers { get; set; }
...
}
and your entity configuration
builder
.HasOne(adv => adv.Address)
.WithMany(adr => adr.Advertisers)
.HasForeignKey(adv => adv.AddressId);
That way you define which properties are the connected objects and how ef should resolve this from the database (by using the foreign key).
Now you can use code like this
foreach(var advertiser in address.Advertisers)
{
...
}
or
var street = advertiser.Address.Street;
...
You won't want to do all the navigation manually by requerying the database e. g. for the connected advertisers after you read an address.
Remember to Include navigation properties in your queries, when they will be used after/outside of the queries.

creating 1-1 relationship between two class using code first and migration

Well, it is 1st time i am trying to create 1-1 relationship between two tables using code first. I took some help online and come across the following classes mapping.
Than I ran migration and found something wrong. E.g. The migration says that primary key for StudentDetails is Id from Student table whereas I am looking to have primary key StudentId. Also, the foreign key is being created in opposite way.
Please can someone highlight what is wrong here or is it me who perceived it wrong.
I need to use Id from student class as Foreign key in StudentDetails class.
public class Student
{
public bool isPass{get;set;}
public virtual StudentReport Report { get; set; }
}
public class StudentReport
{
[Key, ForeignKey("Student")]
public Guid Id { get; set; }
public Guid? StudentReportId { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public virtual Student Student { get; set; }
}
When i run my migration, i get the following outcome which looks not good.
public partial class StudentReport : DbMigration
{
public override void Up()
{
CreateTable(
"dbo.StudentReport",
c => new
{
Id = c.Guid(nullable: false, identity: true),
StudentReportId = c.Guid(),
RollNumber = c.String(),
StudentType = c.String(),
})
.PrimaryKey(t => t.Id)
.ForeignKey("dbo.Student", t => t.Id)
.Index(t => t.Id);
}
In an one to one relationship one end must be the principal and the another one is the dependent. If you are going to declare a FK property in the dependent entity, EF requires that property should be PK too:
public class Principal
{
[Key]
public int Id{get;set;}
public virtual Dependent Dependent{get;set;}
}
public class Dependent
{
[Key, ForeignKey("Principal")]
public int PrincipalId{get;set;}
public virtual Principal Principal{get;set;}
}
If you want to have both entities with their own PKs, and also use Id from Student entity as FK in StudentReport class, then you can try with this model:
public class Student
{
[Key]
public Guid Id { get; set; }
public bool isPass{get;set;}
}
public class StudentReport
{
[Key]
public Guid StudentReportId{ get; set; }
[ForeignKey("Student")]
public Guid StudentId { get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
public virtual Student Student { get; set; }
}
I guess what you really need is an one to many relationship because an student could have 0 or many reports.
Check this link. It could help you understand better how to use the FK properties and the name conventions that have by default Code First.
Update 1
If you want to create an one to one relationship and both entities have their owns PKs, then you can't define a FK property in the dependent entity due to the restriction I explain at the begin of my answer. A solution for what you need could be using the Required attribute and deleting the FK property:
public class Student
{
[Key]
public Guid Id { get; set; }
public bool isPass{get;set;}
public virtual StudentReport StudentReport { get; set; }
}
public class StudentReport
{
[Key]
public Guid StudentReportId{ get; set; }
public string RollNumber { get; set; }
public string StudentType { get; set; }
[Required]
public virtual Student Student { get; set; }
}
Update 2
Are you sure? The migration code that I get is this:
AddForeignKey("dbo.StudentReports", "StudentReportId", "dbo.Students", "Id");
Which is not ok yet because Code First is still configuring by convention the PK of StudentReport as FK. To avoid that you can add this Fluent Api configuration to your context:
modelBuilder.Entity<StudentReport>()
.HasRequired(sr => sr.Student)
.WithOptional(s => s.StudentReport)
.Map(c=>c.MapKey("Student_Id"));
This way Code First will generate this migration code:
AddColumn("dbo.StudentReports", "Student_Id", c => c.Guid(nullable: false));
CreateIndex("dbo.StudentReports", "Student_Id");
AddForeignKey("dbo.StudentReports", "Student_Id", "dbo.Students", "Id");

Entity Framework 5 One-to-one with named foreign key but using attributes

Is it possible to accomplish this using just attributes?
I need the Class2 table to have its own primary key of Id and a column called Class2Id that is the foreign key to Class1.Id.
public class Class1
{
public virtual int Id { get; set; }
public virtual Class2 Class2 { get; set; }
}
public class Class2
{
public virtual int Id { get; set; }
public virtual Class1 Class1 { get; set; }
}
I can get it to work using the fluent mappings using:
modelBuilder.Entity<Class1>()
.HasRequired(x => x.Class2)
.WithRequiredPrincipal(x => x.Class1)
.Map(x => x.MapKey("Class1Id"));
According to "Programming Entity Framework: Code First" book by Julia Lerman, it should be possible. The configuration depends if it is optional 1-1 relationship or required 1-1 relationship.
It is done by using
[Key]
and
[ForeignKey]
data annotations applied on dependent end.
The book contains following example:
public class PersonPhoto
{
[Key]
[ForeignKey("PhotoOf")]
public int PersonId { get; set; }
public byte[] Photo { get; set; }
public string Caption { get; set; }
}

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"));

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

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"));