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

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

Related

because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them

I have implemented following generic model as base model and two models derived from it and i need one to many relationship. I have used fluent api to create the relationships but i get the error mentioned in the title.
Would you please see if you find any thing wrong in my code.
public abstract class ModelBase<T> : BaseEntity, IModelBase<T>
{
public virtual T Id { get; set; }
}
[Table("VehicleModel", Schema = "Tracker")]
public class VehicleModelModel : ModelBase<int>
{
public string Company { get; set; }
public string ModelName { get; set; }
public byte LiPerKM { get; set; }
public virtual ICollection<VehicleModel> Vehicles { get; set; }
[Table("Vehicle", Schema = "Tracker")]
public class VehicleModel : ModelBase<Guid>
{
public string VehicleName { get; set; }
public string LicensePlate { get; set; }
public int VehicleModelId { get; set; }
public virtual VehicleModelModel Model { get; set; }
public virtual TrackerModel Tracker { get; set; }
[Required]
public virtual DriverModel Driver { get; set; }
modelBuilder.Entity<VehicleModel>()
.HasRequired<VehicleModelModel>(s => s.Model)
.WithMany(s => s.Vehicles)
.HasForeignKey(s => s.VehicleModelId);
Here I get this error:
entity types 'VehicleModelModel' and 'VehicleModel' cannot share table 'VehicleModels' because they are not in the same type hierarchy or do not have a valid one to one foreign key relationship with matching primary keys between them.

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

EF Code first : set optional one to one relationship with data annotation

I've the following situation I try to solve : I've 2 tables, a Course table with some fields and a CourseDescription table which is optional (so Course may have a CourseDescription but CourseDescription must have a Course). I'm trying to set this up. So far, here's what I have :
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public virtual CourseDescription CourseDescription { get; set; }
}
public class CourseDescription
{
[Key, ForeignKey("Course")]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
public int CoursesID { get; set; }
[ForeignKey("CoursesID")]
public Course Course { get; set; }
}
This "works" meaning that EF doesn't complains about my model but the relation is not properly done because EF associate the PK of CourseDescription with the PK of Course. In my database, this is not the case (ex : CourseDescription.ID=1 is associated with CourseDescription.CoursesID=3, not 1).
Is there a way to fix that with data annotation ? I know I can use the fluent API but I don't want to override the model building just for that (unless there's no other way).
Thanks
Well, I think you have two choices:
Configure an one to many relationship
If you want to map the FK of the relationship between Course and CourseDescription, and you don't want to declare that FK property as Key of the CourseDescription entity, then, you don't have other choice that configure an one-to-many relationship. In that case your model would be like this:
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public virtual ICollection<CourseDescription> CourseDescriptions { get; set;}
}
public class CourseDescription
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
[ForeignKey("Course")]
public int CourseID { get; set; }
public Course Course { get; set; }
}
Configure an one-to-one relationship but not map the FK of the
relationship
The only way that EF lets you map the FK in an one-to-one relationship is when the FK is declared as a PK too, so if you want to have diferent Ids in both entities and you want to stablish an one-to-one relationship, then you could do something like this:
public class Course
{
[Key, Column("Key_Course")]
public int ID { get; set; }
public string Name { get; set; }
public CourseDescription CourseDescription { get; set;}
}
public class CourseDescription
{
[Key]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
[Required]
public Course Course { get; set; }
}
And work with the navigations properties.
It looks like you should not use ForeignKey attribute for ID property of CourseDescription class as you don't want to have an association between primary keys. Try to remove it.
Edit: It looks like I misunderstood the question previous time.
You can have your CourseDescription this way.
public class CourseDescription
{
[Key, ForeignKey("Course")]
public int ID { get; set; }
public string Description { get; set; }
public string PreRequis { get; set; }
public Course Course { get; set; }
}
In this case you don't need to have CoursesID field. Entities will be connected by primary keys.

Entity Framework one-to-one relationship - Unable to determine the principal

I have 2 models:
public class TransactionHistory : IDbEntity {
[Key]
public int ID { get; set; }
public ItemHistory ItemHistory { get; set; }
}
public class ItemHistory : IDbEntity {
[Key]
public int ID { get; set; }
public int TransactionHistoryID { get; set; }
public TransactionHistory TransactionHistory { get; set; }
}
There's a one to one relationship between TransactionHistory and ItemHistory, ItemHistory MUST have a TransactionHistory but TransactionHistory may or may not have an ItemHistory.
I want to be able to do this in code:
var test = db.ItemHistory.Include(x => x.TransactionHistory).ToList();
As well as:
var test2 = db.TransactionHistory.Include(x => x.ItemHistory).ToList();
But I only want a single FK on the ItemHistory table.
With the code I've listed I get this error:
Unable to determine the principal end of an association between the types 'InventoryLibrary.DomainModels.TransactionHistory' and 'InventoryLibrary.DomainModels.ItemHistory'. The principal end of this association must be explicitly configured using either the relationship fluent API or data annotations.
How is this achieved in Entity Framework code first data annotations?
Firstly, you have to mark foreign keys by virtual keyword to enable overrides.
public class TransactionHistory : IDbEntity
{
[Key]
public int ID { get; set; }
public virtual ItemHistory ItemHistory { get; set; }
}
public class ItemHistory : IDbEntity
{
[Key]
public int ID { get; set; }
public int TransactionHistoryID { get; set; }
[Required]
public virtual TransactionHistory TransactionHistory { get; set; }
}
If HistoryItem must have Transaction History, add DataAnnotation [Required], which makes it non-nullable.
Finally, wonder, if you want to have one-to-one relationship. I imagine you'd like to have many transaction history entries. Am I right? If not - let me know.
To create one-to-many relationship, use IEnumerable<> type.

Defining foreign key constraints with Entity Framework code-first

I have following entity class called Code. It stores categories of different kinds - the data for which I would have otherwise needed to create many small tables e.g. User Categories, Expense Categories, Address types, User Types, file formats etc.
public class Code
{
public int Id { get; set; }
public string CodeType { get; set; }
public string CodeDescription { get; set; }
public virtual ICollection<Expense> Expenses { get; set; }
public virtual ICollection<Address> Addresses { get; set; }
:
: // many more
}
The class Expense looks like this:
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public double Amount { get; set; }
public DateTime ExpenseDate { get; set; }
}
With the above class definitions, I have established 1:many relation between Code and Expense using the CategoryId mapping.
My problem is, I want to map the SourceId field in Expense to the Code object. Which means, Expense object would contain
public Code Source { get; set; }
If I use this, at runtime I get an error about cyclic dependencies.
Can someone please help?
You will need to disable cascading delete on at least one of the two relationships (or both). EF enables cascading delete by convention for both relationships because both are required since the foreign key properties are not nullable. But SQL Server doesn't accept multiple cascading delete paths onto the same table that are introduced by the two relationships. That's the reason for your exception.
You must override the convention with Fluent API:
public class Code
{
public int Id { get; set; }
//...
public virtual ICollection<Expense> Expenses { get; set; }
//...
}
public class Expense
{
public int Id { get; set; }
public int CategoryId { get; set; }
public virtual Code Category { get; set; }
public int SourceId { get; set; }
public virtual Code Source { get; set; }
//...
}
Mapping with Fluent API;
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Category)
.WithMany(c => c.Expenses)
.HasForeignKey(e => e.CategoryId)
.WillCascadeOnDelete(false);
modelBuilder.Entity<Expense>()
.HasRequired(e => e.Source)
.WithMany()
.HasForeignKey(e => e.SourceId)
.WillCascadeOnDelete(false);