I have some problems with EF CodeFirst and relations 0/1 to 0/1. I have 3 "tables" that contains another, and this is optional.
It's similar to:
[DataContract]
public class A
{
[DataMember, Key]
public int Id {get;set;}
[DataMember]
public int? IdD
[ForeignKey("idD")]
public virtual D D {get; set;}
}
[DataContract]
public class B
{
[DataMember, Key]
public int Id {get;set;}
[DataMember]
public int? IdD
[ForeignKey("idD")]
public virtual D D {get; set;}
}
[DataContract]
public class C
{
[DataMember, Key]
public int Id {get;set;}
[DataMember]
public int? IdD
[ForeignKey("idD")]
public virtual D D {get; set;}
}
[DataContract]
public class D
{
[DataMember, Key]
public int Id {get;set;}
public virtual A A { get; set; }
public virtual B B { get; set; }
public virtual C C { get; set; }
}
When I try to made the migration, appears the error:
Unable to determine the principal end of an association between the types 'XXXXXX' and 'XXXXXX'.
The principal end of this association must be explicitly configured using either the relationship
fluent API or data annotations.
I try with DataAnnotations and with Fluent API and appears the same error. On Fluent API I try:
with A, B or C class
this.HasOptional(casv => casv.D)
.WithOptionalDependent(e => e.A)//I try with denendent and Principal
.Map(m => m.MapKey("IdD")) // I try without this
.WillCascadeOnDelete(true);
try this, I think it's better looking than your code:
this.HasOptional(casv => casv.D)
.WithOptionalDependent(e => e.A)
.Map(m => m.MapKey("IdD"))
.WillCascadeOnDelete(true);
Related
I have the following classes: User, Post and UserPost. User and Post have a 1-to-many relationship. There's a third join table called UserPost that tracks the up/down vote each post gets. To ensure each user can only upvote/downvote once, the ID (PK) of this table is a composite key of User and Post ID.
public class User {
public Guid Id {get; set;}
public string UserName {get; set;}
public ICollection<Post> Posts {get; set;}
}
public class Post {
public Guid Id {get; set;}
public string Content {get;set;}
public User User {get; set;}
}
public UserPost {
public Guid Id {get; set;} // This should be a composite key of User ID and Post ID
public Guid PostId {get;set;}
public Guid UserId {get;set;}
public VoteType VoteType {get; set;}
}
public enum VoteType {
Up = 1,
Down = 0
}
In my DB Context class I defined the User/Post relationship like this:
builder.Entity<User>()
.HasMany(u => u.Posts)
.WithOne(p => p.User)
Now I want to define the relationship for the UserPost model but not sure how to go about it. So far I have:
builder.Entity<UserPost>()
.HasKey(x => new { x.UserId, x.PostId })
Does it require anything further?
Write your whole relationship as follows:
public class User
{
public Guid Id {get; set;}
public string UserName {get; set;}
public ICollection<Post> Posts {get; set;}
}
public class Post
{
public Guid Id {get; set;}
public string Content {get;set;}
public Guid UserId {get; set;}
public User User {get; set;}
}
public UserVote // Rename this from UserPost to UserVote to keep naming consistency.
{
public Guid PostId {get;set;}
public Guid UserId {get;set;}
public VoteType VoteType {get; set;}
public Post Post {get; set;}
public User User {get; set;}
}
public enum VoteType {
Up = 1,
Down = 0
}
Now, Fluent API configuration for the UserVote as follows:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<UserVote>(e =>
{
e.HasKey(uv => new { uv.PostId, uv.UserId}); //<-- Here is the composite key.
e.HasOne(uv => uv.Post).WithMany().HasForeignKey(uv => uv.PostId).OnDelete(DeleteBehavior.Restrict);
e.HasOne(uv => uv.User).WithMany().HasForeignKey(uv => uv.UserId).OnDelete(DeleteBehavior.Restrict);
});
}
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");
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; }
}
I am trying out EF and Code First approach
I have an Account model which looks like
public class Account
{
public virtual int AccountID {get; set;}
public virtual string AccountName {get; set;}
public virtual Account ParentAccount {get; set;}
public virtual Contact AccountOwner {get; set}
}
Contact class is a simple class
public class Contact
{
public virtual int ContactID {get; set;}
public virtual string ContactName {get; set;}
}
Now I have no idea how to add IDs to the account class.
Suppose I have set up the context correctly, what modifications are needed to the Account class so that
I can add an Account as the parent of another Account
I can access the ParentAccount of a particular Account
I can access the AccountOwner of a particular Account
I am new to this. Any help would be appreciated
Here's how I would solve this.
Add foreign key properties to your classes. I'm talking about ParentAccountId and AccountOwnerId. To make navigating things a bit easier, I have also added a collection of child accounts to Account and a collection of owned accounts to Contact. Note that it's not necessary to make "normal" properties virtual. Only navigational properties should be made virtual.
public class Account
{
public int AccountID {get; set;}
public string AccountName {get; set;}
public int? ParentAccountId { get; set; }
public int? AccountOwnerId { get; set; }
public virtual Account ParentAccount { get; set; }
public virtual ICollection<Account> ChildAccounts { get; set; }
public virtual Contact AccountOwner { get; set; }
public Account()
{
ChildAccounts = new List<Account>();
}
}
public class Contact
{
public int ContactID { get; set; }
public string ContactName { get; set; }
public virtual ICollection<Account> OwnedAccounts { get; set; }
public Contact()
{
OwnedAccounts = new List<Account>();
}
}
Next, create a mapping for the Account class to explain to EF how to setup the relationships.
public class AccountMapping : EntityTypeConfiguration<Account>
{
public AccountMapping()
{
HasOptional(x => x.ParentAccount).WithMany(x => x.ChildAccounts).HasForeignKey(x => x.ParentAccountId);
HasOptional(x => x.AccountOwner).WithMany(x => x.OwnedAccounts).HasForeignKey(x => x.AccountOwnerId);
}
}
Finally, add the mapping to your DbContext class.
public MyContext : DbContext
{
public DbSet<Account> Accounts { get; set; }
public DbSet<Contact> Contacts { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Configurations.Add(new AccountMapping());
}
}
Note that I have assumed that the AccountOwner and ParentAccount are optional. If they are required, simply change the type of the foreign properties from int? to int and change the HasOptional in the mappings to HasRequired.
public class Account
{
[Key]
public virtual int AccountID {get; set;}
public virtual string AccountName {get; set;}
public virtual Account ParentAccount {get; set;}
public virtual Contact AccountOwner {get; set}
public virtual IEnummerable<Account> ChiledAccount {get; set}
}
public class Contact
{
[Key]
public virtual int ContactID {get; set;}
public virtual string ContactName {get; set;}
}
You can do this by without using Fluent API.That is by default conventions are maintained by EF.B'cos your mappings are simple.
You're having Account (1) : Contact (m) Relationship.
So try with below models
public class Account
{
public int Id {get; set;}
public string AccountName {get; set;}
public virtual ICollection<Contact> AccountOwners { get; set; }
}
public class Contact
{
public int Id {get; set;}
public string ContactName {get; set;}
public virtual Account ParentAccount {get; set;}
}
Then Your database tables will be created like below:
Accounts Table
Id int NotNull
AccountName nvarchar(100) AllowNull
Contacts Table
Id int NotNull
ContactName nvarchar(100) AllowNull
Account_Id int NotNull
If you need to do advance mappings then you have to learn Fluent API.
Hallo everybody,
i have the following simple models.
public class A
{
public B B { get; set; }
public C C { get; set; }
}
public class B
{
public int Id { get; set; }
}
public class C
{
public int Id { get; set; }
}
I get an error while i am trying to get the data:
System.Data.Edm.EdmEntityType: :
EntityType 'A' has no key
defined. Define the key for this
EntityType.
Previous it was done via "RelatedTo". Has anybody a solution for this problem with the help of an example?
Thanks in advance!
Each entity in EF must have a primary key. It looks like A is junction table for many to many so you have multiple choices.
Remove A totaly and let EF handle many-to-many:
public class B
{
public int Id { get; set; }
public virtual ICollection<C> Cs { get; set; }
}
public class C
{
public int Id { get; set; }
public virtual ICollection<B> Bs { get; set; }
}
If you want A as entity you must either define additional key:
public class A
{
public int Id { get; set; }
public B B { get; set; }
public C C { get; set; }
}
or you must include FK properties for B and C and mark them both as composite primary key (should be in db as well):
public class A
{
public int bId { get; set; }
public int cId { get; set; }
public B B { get; set; }
public C C { get; set; }
}
Edit:
Mapping for the last solution
modelBuilder.Entity<A>.HasKey(a => new { a.bId, a.cId });
modelBuilder.Entity<A>.HasRequired(a => a.B)
.WithMany()
.HasForeignKey(a => a.bId);
modelBuilder.Entity<A>.HasRequired(a => a.C)
.WithMany()
.HasForeignKey(a => a.cId);
Anyway if your A looks exactly as you described without any other properties you are definitely doing it wrong. Mapping A is only needed when it contains anything else then navigation properties / FKs for modelling many-to-many relation.