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");
Related
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);
}
I have the following model:
public class User
{
public int Id { get; set; }
public Customer Customer { get; set; }
...
}
public class Customer
{
public int Id { get; set; }
public int UserId { get; set; }
public User User { get; set; }
...
}
What I want to have is the Customer has to have an User, but can only have one, and the User does not have to have a Customer.
I would like to do it with the Fluent API, but I can't manage to get it to work so that both Customer and User have their Id properties be Identity Fields.
When you are configuring an one-to-one relationship, Entity Framework requires that the primary key of the dependent also be the foreign key, in your case it would be:
public class User
{
public int Id { get; set; }
public virtual Customer Customer { get; set; }
...
}
public class Customer
{
[Key, ForeignKey("User")]
public int UserId { get; set; }
public virtual User User { get; set; }
...
}
But you want each entities with its own PK, so, EF lets you do that but you should delete the UserId property from Customer entity, because, as I said before, in this kind of relationship the FK must be PK too. To configure properly your relationship use the Required data annotation as #Claies recommend you in his comment:
public class User
{
public int Id { get; set; }
public virtual Customer Customer { get; set; }
...
}
public class Customer
{
[Key]
public int Id { get; set; }
[Required]
public virtual User User { get; set; }
...
}
Or you can use Fluent Api, the configuration would be:
modelbuilder.Entity<Customer>().HasRequired(c=>c.User).WithOptional(u=>u.Customer);
Another thing, I recommend you define the navigation properties as virtual. This way, when you consult those properties the first time, they will be lazy loaded. Check this post for more info.
Update 1:
When the key property is an integer, Code First defaults to
DatabaseGeneratedOption.Identity. If you want, you can configure explicitly what you need using the [Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)] attributes on the Customer Id.
public class Customer
{
[Key,DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
Or you can use Fluent Api:
modelbuilder.Entity<Customer>().HasKey(t => t.Id)
.Property(t => t.Id)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
Update 2:
I don't understand why is throwing you that exception. I just tested with both variants (Data Annotations and Fluent Api) and everything works well. This is the code generated by Migrations:
public partial class changeCustomerIdToIdentity : DbMigration
{
public override void Up()
{
DropIndex("dbo.Customers", new[] { "Id" });
DropPrimaryKey("dbo.Customers");
AlterColumn("dbo.Customers", "Id", c => c.Int(nullable: false, identity: true));
AddPrimaryKey("dbo.Customers", "Id");
CreateIndex("dbo.Customers", "Id");
}
public override void Down()
{
DropIndex("dbo.Customers", new[] { "Id" });
DropPrimaryKey("dbo.Customers");
AlterColumn("dbo.Customers", "Id", c => c.Int(nullable: false));
AddPrimaryKey("dbo.Customers", "Id");
CreateIndex("dbo.Customers", "Id");
}
}
I'm afraid your error is happened due to your DB schema. The Id on your Customers table must be FK too. The error means that you have some relation between your entities where foreign key property in dependent entity is defined as store generated, and that is because you are trying change the Id of your Customer entity as Identity, which is FK in your DB.
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.
I am using code first migrations in an asp.net mvc program.
I am using the default authenticion and roles which is provided in the project.
Now when I enabled migrations it automatically generated a migration class which generates all of the tables etc.
Here is an excample of the specific table which I wqish to edit.
CreateTable(
"dbo.AspNetUserRoles",
c => new
{
UserId = c.String(nullable: false, maxLength: 128),
RoleId = c.String(nullable: false, maxLength: 128),
})
.PrimaryKey(t => new { t.UserId, t.RoleId })
.ForeignKey("dbo.AspNetRoles", t => t.RoleId, cascadeDelete: true)
.ForeignKey("dbo.AspNetUsers", t => t.UserId, cascadeDelete: true)
.Index(t => t.RoleId)
.Index(t => t.UserId);
Now I would like to add a description field to this table. It would be really easy if i just added it in the database but then I will loose my code first migrations.
1> Where does Entity framework get all its commands for the initial migration? Because there are no models in my project that I can see which specify the tables it creates.
2> How can I modify or edit some of the original tables which are generated? I have tried just editing the initial migrations folder but that does not work?
(Just my thinking) Is it not that maybe the Roles and users models are stored in the framework and that is where it gets the structure of the tables from? If so Can i not extend the default model to add more attributes? Cause I know you can do it for the ApplicationUser, I have done so before, here is an example of it:
// You can add profile data for the user by adding more properties to your ApplicationUser class, please visit http://go.microsoft.com/fwlink/?LinkID=317594 to learn more.
public class ApplicationUser : IdentityUser
{
[Required]
[MaxLength(50)]
[Display(Name = "Email Address")]
public string email { get; set; }
}
Thats how i can add a email address field to the default user. Can I not maybe do this with the roles as well.
You dit it perfectly for the ApplicationUser, just continue like that for the other stuff. If you want to add description to the table of the users. Just add it to the ApplicationUser model. Don't mind the foreign keys and virtual properties.
public class ApplicationUser : IdentityUser
{
[Required]
public string FirstName { get; set; }
[Required]
public string LastName { get; set; }
public string GroupName { get; set; }
[Required]
public string Email { get; set; }
[Required]
[StringLength(15)]
public string Phone { get; set; }
public string Remark { get; set; }
public DateTime? BirthDate { get; set; }
public DateTime ValidFrom { get; set; }
public DateTime ValidUntil { get; set; }
// Foreign keys
[ForeignKey("Bank")]
public string AccountNumber { get; set; }
[ForeignKey("Address")]
public int? AddressId { get; set; }
public virtual Bank Bank { get; set; }
public virtual Address Address { get; set; }
public virtual ICollection<Request> Requests { get; set; }
}
For editing the role class, you should inherit IdentityRole on your class and add the properties:
public class ApplicationRole : IdentityRole
{
public string Description { get; set; }
}
The framework will generate new migration classes which will be ran when you use the Update-Database command.
You have to change your identityManager (Use ApplicationRole here):
public class IdentityManager
{
public bool RoleExists(string name)
{
var rm = new RoleManager<ApplicationRole>(new RoleStore<ApplicationRole>(new ApplicationDbContext()));
return rm.RoleExists(name);
}
public bool CreateRole(string name)
{
var rm = new RoleManager<ApplicationRole>(new RoleStore<ApplicationRole>(new ApplicationDbContext()));
var idResult = rm.Create(new ApplicationRole(name));
return idResult.Succeeded;
}
}
You have to overwrite the Role in ApplicationDbContext doing like following (don't forget the new):
public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
{
public ApplicationDbContext()
: base("DefaultConnection")
{
}
...
public DbSet<Product> Products { get; set; }
new public DbSet<ApplicationRole> Roles { get; set; }
}
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; }
}