Can someone shed light onto this?
I have three objects. Firstly, a Root object
public class Root
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
}
This is the basis for my composite key, used in Parent,Child:
public class Parent
{
[Key, Column(Order = 0)]
public int RootId { get; set; }
public Root Root { get; set; }
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public ICollection<Child> Children { get; set; } = new Collection<Child>();
}
public class Child
{
[Key, Column(Order = 0)]
public int RootId { get; set; }
public Root Root { get; set; }
[Key, Column(Order = 1)]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public int ParentId { get; set; }
[ForeignKey("RootId, ParentId")]
public Parent Parent { get; set; }
}
I add a new parent object with multiple children
Parent result = new Parent
{
RootId = rootId
};
foreach (var child in children)
{
result.Children.Add(new Child
{
RootId = rootId
});
}
_dbContext.Parents.Add(result);
_dbContext.Save();
However, I get an exception whenever I have multiple Root objects in my database:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.Children_dbo.Parents_RootId_ParentId". The conflict occurred in database "Test", table "dbo.Parents".
When I delete the foreign key constraint in my database, I can see that EF is somehow ignoring the RootId = rootId that I set. It uses a different one instead. The ParentId is correctly set, but obviously this results in a FK that doesn't exist.
I want to commit the parent and its children in one Save call. I've tried adding the children to _dbContext.Children after adding the parent to _dbContext.Parents, I've tried first adding the Children, to no avail.
Can someone shed light on this? I'm running out of ideas on what to check to understand what I'm doing wrong..
Edit: I'm using Entity Framework 6.1.3.
Related
I have created 3 tables using Code First Approach. I get the following Model Validation Exception when i execute a Find on student table.
Student_courses_Target_Student_courses_Source: : The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
public class University
{
[Key]
public string Uni_ID { get; set; }
public virtual List<Course> Courses { get; set; }
}
public class Course
{
[Key]
[Column(Order = 1)]
public string Course_ID { get; set; }
[Key,ForeignKey("uni")]
[Column(Order = 2)]
public string Uni_ID { get; set; }
public virtual ICollection<Student> Students { get; set; }
public virtual University uni { get; set; }
}
public class Student
{
[Key,ForeignKey("course"), Column(Order = 1)]
public string Course_ID { get; set; }
[ForeignKey("course"),Column(Order = 2)]
public string Uni_ID { get; set; }
[Key]
[Column(Order = 3)]
public string Student_ID { get; set; }
public virtual Course course { get; set; }
}
By my understanding , the exception means that i have not mapped my foreign keys in student table to the primary keys in course table. But i have done it . Is there an issue as to how the 'Uni_ID' occurs as Primary key in both University and Course Tables and perhaps i have gone wrong in referencing it as foreign key in the Student table ?
I want to make VM_hostname,datetime and name properties as a composite Key for Disk class . At the same time VM_hostname and datetime of Disk class should refer to VM_hostname and datetime of VirtualMachine class (ie Foreign keys) .
I did this but it gives me this exception :
The ForeignKeyAttribute on property 'datetime' on type 'WebJob1.Historical.Disk' is not valid. The navigation property 'Datetime' was not found on the dependent type 'WebJob1.Historical.Disk'. The Name value should be a valid navigation property name
Anyone have a clue ? Also, please note that im using Data Annotation.
public class VirtualMachine
{
[Key]
[Column(Order = 0)]
public string VM_Hostname { get; set; }
[Key]
[Column(Order = 1)]
public DateTime Datetime;
public virtual List<Disk> disks { get; set; }
}
public class Disk
{
[Key,ForeignKey("VirtualMachine"),Column(Order = 0)]
public string VM_hostname { get; set; }
[Key,ForeignKey("Datetime"), Column(Order = 1)]
public DateTime datetime { get; set; }
[Key, Column(Order = 2)]
public string name { get; set; }
public virtual VirtualMachine VirtualMachine{ get; set; }
}
The main difference between your question and the one I suggested as duplicate is that your ForeignKey attributes don't refer -
from a primitive property to a navigation property
from a navigation property to a primitive property
In your case, the reference is from a primitive property to another primitive property, in another type. Also, little detail, VirtualMachine.Datetime should be a property, not a member. But I have to admit that the "duplicate" didn't cover your case.
So let's try to make this into a comprehensive answer how to handle this situation in Entity Framework 6. I'll use an abstracted model to explain the various options:
public class Parent
{
public int Id1 { get; set; } // Key
public int Id2 { get; set; } // Key
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
public int Id1 { get; set; } // Key
public int Id2 { get; set; } // Key
public int Id3 { get; set; } // Key
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
There are three options to setup the mappings.
Option 1
Data annotations, ForeignKey attribute:
public class Parent
{
[Key]
[Column(Order = 1)]
public int Id1 { get; set; }
[Key]
[Column(Order = 2)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
[Key]
[Column(Order = 0)]
public int Id1 { get; set; }
[Key]
[Column(Order = 1)]
public int Id2 { get; set; }
[Key]
[Column(Order = 2)]
public int Id3 { get; set; }
public string Name { get; set; }
[ForeignKey("Id1,Id2")]
public virtual Parent Parent { get; set; }
}
As you see, here the ForeignKey attribute refers from a navigation property to primitive properties. Also, the absolute numbers in the column order don't matter, only their sequence.
Option 2
Data annotations, InverseProperty attribute:
public class Parent
{
[Key]
[Column(Order = 1)]
public int Id1 { get; set; }
[Key]
[Column(Order = 2)]
public int Id2 { get; set; }
public string Name { get; set; }
public virtual List<Child> Children { get; set; }
}
public class Child
{
[Key]
[Column(Order = 0)]
[InverseProperty("Children")]
public int Id1 { get; set; }
[Key]
[Column(Order = 1)]
[InverseProperty("Children")]
public int Id2 { get; set; }
[Key]
[Column(Order = 2)]
public int Id3 { get; set; }
public string Name { get; set; }
public virtual Parent Parent { get; set; }
}
InverseProperty points from one or more properties in a type at one end of a relationship to a navigation property in the type on the other end of the relationship. Another way to achieve the same mapping is to apply [InverseProperty("Parent")] on both key properties of Parent.
Option 3
Fluent mapping:
modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithRequired(c => c.Parent)
.HasForeignKey(c => new { c.Id1, c.Id2 });
As said in the comments, fluent mapping is less error-prone than data annotations. Data annotations offer too many options to configure mappings and it's not always easy to see which parts are connected. That's why fluent mapping is my favorite.
Entity Framework Core
In EF-core (current version 3.1.6) composite primary keys can't be modeled by data annotations. It throws a run-time exception:
Entity type 'Parent' has composite primary key defined with data annotations. To set composite primary key, use fluent API.
So for EF-core only option 3 is feasible. The mapping is almost identical:
modelBuilder.Entity<Parent>().HasKey(p => new { p.Id1, p.Id2 });
modelBuilder.Entity<Child>().HasKey(p => new { p.Id1, p.Id2, p.Id3 });
modelBuilder.Entity<Parent>()
.HasMany(p => p.Children)
.WithOne(c => c.Parent) // Different here
.HasForeignKey(c => new { c.Id1, c.Id2 });
I'm working with Entity Framework and identity framework (IdentityUser, IdentityRole). I have a table with a composite key (table Country) that points to the Users table.
Unfortunately, EF can only build a relation when all keys are the same, otherwise you'll get this:
The number of properties in the Dependent and Principal Roles in a relationship constraint must be identical.
So, how can I handle this? Right now I have tried to add this composite key to the ApplicationUser:IdentityUser too, but in this way I have to add the composite key to all entities of the identity framework (that means user, role, claim, login, ...).
Here are my model classes:
class Country
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
public int ID { get; set; }
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 2)]
public int Version { get; set; }
[Required]
[ForeignKey(nameof(Chief))]
[Column(Order = 1)]
public string Chief_Id { get; set; }
[Required]
[ForeignKey(nameof(Chief)), Column(Order = 2)]
public int Chief_Version { get; set; }
public virtual ApplicationUser Chief{ get; set; }
}
class ApplicationUser : IdentityUser
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 1)]
public override string Id
{
get { return base.Id; }
set { base.Id = value; }
}
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.None)]
[Column(Order = 2)]
public int Version { get; set; }
}
Nico
Look into your foreign key nameof(Chief), you might not have the full relationship set up.
There is another question that might help here.
I am trying to create a relationship between two tables but keep getting the following error:
The ForeignKeyAttribute on property 'CallLogId' on type
'Ylp.Web.ParkingApi.DataLayer.Entities.ApiCallLogDetailEntity' is not
valid. The navigation property 'ApiCallLog' was not found on the
dependent type
'Ylp.Web.ParkingApi.DataLayer.Entities.ApiCallLogDetailEntity'. The
Name value should be a valid navigation property name.
DbContextMapping:
modelBuilder.Entity<ApiCallLogDetailEntity>()
.HasRequired<ApiCallLogEntity>(p => p.ApiCallLog);
Primary table:
[Table("ApiCallLog")]
public class ApiCallLogEntity
{
[Key, Column(Order = 0)]
public string CallLogId { get; set; }
[Key, Column(Order = 1)]
public string UserId { get; set; }
[Required]
public string CallFilterId { get; set; }
[Required]
public DateTime LastUpdated { get; set; }
[Required]
public int Count { get; set; }
public virtual ICollection<ApiCallLogDetailEntity> Details { get; set; }
}
foreign table:
[Table("ApiCallLogDetail")]
public class ApiCallLogDetailEntity
{
[ForeignKey("ApiCallLog")]
public string CallLogId { get; set; }
[Required]
public string PrametersHashCode { get; set; }
[Required]
public DateTime LastUpdated { get; set; }
public ApiCallLogEntity ApiCallLog { get;}
}
The foreign key must refer to the whole primary key of the parent table. In your parent table you have a composite primary key which includes CallLogId and UserId. The message is confusing, but this can be part of the error. Is it really necessary to include the UserId in the PK?
Another error is that you have not defined the PK in the dependent table. If the UserId is also necessary on the PK, include it in the dependent table, and make it part of the FK.
Is there any way to save a child object in a parents child object collection and also setting it to a navigation property on the parent without round tripping to the database? example below doesn't work
public class Parent
{
int Id { get; set; }
int? ChildId { get; set; }
Child Child { get; set; }
public virtual ICollection<Child> Children { get; set; }
}
public class Child
{
int Id { get; set; }
public Parent Parent { get; set; }
}
....
var p = new Parent();
var c = new Child();
p.Child = c;
p.Children.Add(c);
Context.Set<Parent>().Add(p);
Context.SaveChanges();
EDIT
The example above throws this error when 'savechanges()' is called.
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values.