So I have the following code:
ParentModel.cs
public class ParentModel {
public int ParentModelID {get; set;}
...other fields here
public ChildModel ChildModel {get; set;}
}
ChildModel.cs
public class ChildModel{
[ForeignKey("ParentModel")]
public int ChildModelID {get; set;}
...other fields and navigation properties here
public int ParentModelID {get; set;}
public ParentModel ParentModel {get; set;}
}
So the database gets generated successfully. The problem occurs when I try to save data. For example I save data to the ParentModel first and it gets save successfully. But when I save inside ChildModel, even when my data contains the ParentModel's id, it gives me the following error:
The INSERT statement conflicted with the FOREIGN KEY constraint "FK_dbo.ChildModels_dbo.ParentModels_ChildModelID". The conflict occurred in database "MyDatabaseName", table "dbo.ParentModels", column 'ParentModelID'. The statement has been terminated.
use following with fluent api
public class ParentModel {
public int ParentModelID {get; set;}
...other fields here
public virtual ChildModel childModel {get; set;}
}
public class ChildModel{
public int ParentModelID {get; set;}
public int ChildModelID {get; set;}
...other fields and navigation properties here
public virtual ParentModel parentModel {get; set;}
}
Then i will use fluent api to create relationship
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Configure ParentModelID as PK for ChildModel
modelBuilder.Entity<ChildModel>()
.HasKey(e => e.ParentModelID);
// Configure ParentModelID as FK for ChildModel
modelBuilder.Entity<ParentModel>()
.HasOptional(s => s.childModel)
.WithRequired(ad => ad.ParentModelID);
}
You almost had it; you want a shared primary key, which you have set up. The problem is you are presumably setting the the ChildModel.ParentModelID property with the parent's ID. Remove that property and set ChildModel.ChildModelID to the ParentModelID of the Parent entity - ChildModelID is the FK to the ParentModel entity. You'll also need to make ChildModel.ParentModel required.
public class ParentModel
{
public int ParentModelID {get; set;}
public ChildModel ChildModel {get; set;}
}
public class ChildModel
{
[ForeignKey("ParentModel")]
public int ChildModelID {get; set;}
[Required]
public ParentModel ParentModel {get; set;}
}
var parent = new ParentModel();
dbContext.Set<ParentModel>().Add( parent );
dbContext.SaveChanges();
var child = new ChildModel()
{
ChildModelID = parent.ParentModelID
};
dbContext.Set<ChildModel>().Add( child );
dbContext.SaveChanges();
See this answer for another example.
Related
I have a table that contains 2 foreign key that reference separately to 2 different table.
I would like to return the result of all person that has course of "Science".
How to retrieve the record back using LINQ?
This is what i gotten so far:
return
_ctx.Person
.Include(u => u.Course
.Where(ug=>ug.CourseName== "Science"));
This is not working as it shows the error.
The Include path expression must refer to a navigation property
defined on the type
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<Person> Persons { get; set; }
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<Course> Courses { get; set; }
}
This is the mapping table. Only contains 2 foreign key from 2 different table.
I could not use this table inside the solution.As the code first won't generate this table as it doesn't contain it's own PK.
//This is not shown in the EntityFramework when generating Code First.
public class PersonCouseMap
{
public int PersonID {get; set;}
public int CourseID {get; set;}
}
Update : this works after I switched the entity.
return _ctx.Course
.Include(u=>u.Person)
.Where(ug=>ug.CourseName == "Sciene");
Anyone can explain why it won't work the another way round.
I need to display a List of Person who have course of "Science",
not Course Science that has a list of user.
The original query does not work because you've pushed the Where predicate inside the Include expression, which is not supported as indicated by the exception message.
The Include method is EF specific extension method used to eager load related data. It has nothing to do with the query filtering.
To apply the desired filter person that has course of "Science" you need Any based predicate since the Person.Courses is a collection:
return _ctx.Person
.Where(p => p.Courses.Any(c => c.CourseName == "Science"));
To include the related data in the result, combine it with Include call(s):
return _ctx.Person
.Include(p => p.Courses)
.Where(p => p.Courses.Any(c => c.CourseName == "Science"));
It looks like there is no relations between these two entites, you can establish a relationship by making the following changes to your code:
Here I am assuming that you want to establish Many-to-Many relationship between these two tables by having a third entity PersonCourseMap
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<CoursePersons> Courses { get; set; }
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<PersonCourse> Courses { get; set; }
}
public class PersonCourseMap
{
public int PersonID {get; set;}
public int CourseID {get; set;}
public virtual ICollection<Person> Persons { get; set; }
public virtual ICollection<Course> Courses { get; set; }
}
After making above changes you can simply navigate through properties.
Include Foreign Key Mapping
public class Course
{
public int CourseID {get; set;}
public string CourseName {get; set;}
public virtual ICollection<Person> Person {get; set}
}
public class Person
{
public int PersonID {get; set;}
public virtual ICollection<Course> Course {get; set;}
}
using System.ComponentModel.DataAnnotation.Schema;
public class PersonCouseMap
{
[ForeignKey("Person")]
public int PersonID {get; set;}
[ForeignKey("Course")]
public int CourseID {get; set;}
public virtual ICollection<Person> Person {get; set;}
public virtual ICollection<Course> Course {get; set;}
}
I have the following models (and corresponding DTOs):
public class Link
{
public int Id {get; set;}
public int FirstLinkId {get; set;}
public int SecondLinkId {get; set;}
public virtual Link FirstLink {get; set;}
public virtual Link SecondLInk {get; set;}
}
public class OtherObject
{
public int Id {get; set;}
public int LinkId {get; set;}
public string Name {get; set;}
public virtual Link Link {get; set;}
}
In my scenario, I can have a Link object where FirstLink and/or SecondLink can be null, references to other objects, or references to the same object.
Now I want to load an OtherObject entity from the db using EF. I load the entity itself and also the Link object associated with it. This is done perfectly by EF.
In this particular case, both FirstLink and SecondLink are the same as Link, therefore, when automapping from model to dto it just keeps on mapping into oblivion.
My mapping is:
Mapper.CreateMap<OtherObject, OtherObjectDto>().Bidirectional()
.ForMember(model => model.LinkId, option => option.Ignore());
where Bidirectional() is this extension:
public static IMappingExpression<TDestination, TSource> Bidirectional<TSource, TDestination>(this IMappingExpression<TSource, TDestination> expression)
{
return Mapper.CreateMap<TDestination, TSource>();
}
Is there way to tell Automapper not to map further down the tree in this case?
The way I would handle this is to create separate DTO objects for the children:
public class Employee
{
public int Id {get; set;}
public string Name { get; set; }
public Employee Supervisor {get; set; }
}
public class EmployeeDto {
public int Id {get; set;}
public string Name { get; set; }
public SupervisorDto Supervisor { get; set; }
public class SupervisorDto {
public int Id {get; set;}
public string Name { get; set; }
}
}
Mapper.CreateMap<Employee, EmployeeDto>();
Mapper.CreateMap<Employee, EmployeeDto.SupervisorDto>();
Don't let your DTOs be recursive/self-referential. Be explicit in your structure on how deep you want it to go.
EF can't do recursive joins, you're only doing one level, so don't make your DTOs go nuts with infinitely deep relationships. Be explicit.
I have the following model:
public class Customer
{
public int Id {get; set;}
public string Name {get; set;}
public int AddressId {get; set;}
public virtual Address Address {get; set;}
public virtual ICollection<CustomerCategory> Categories {get; set;}
}
public class CustomerCategory
{
public int Id {get; set;}
public int CustomerId {get; set;}
public int CategoryId {get; set;}
public virtual Category Category {get; set;}
}
public class Address
{
public int Id {get; set;}
public string Street{get; set;}
public virtual PostCode PostCode {get; set;}
}
From the above, and using GraphDiff, I want to update the customer aggregate as follows:
dbContext.UpdateGraph<Customer>(entity,
map => map.AssociatedEntity(x => x.Address)
.OwnedCollection(x => x.Categories, with => with.AssociatedEntity(x => x.Category)));
But the above is not updating anything!!
What is the correct way to use GraphDiff in this case?
GraphDiff basically distinguishes two kinds of relations: owned and associated.
Owned can be interpreted as "being a part of" meaning that anything that is owned will be inserted/updated/deleted with its owner.
The other kind of relation handled by GraphDiff is associated which means that only relations to, but not the associated entities themselves are changed by GraphDiff when updating a graph.
When you use the AssociatedEntity method, the State of the child entity is not part of the aggregate, in other words, the changes that you did over the child entity will not be saved, just it will update the parent navegation property.
Use the OwnedEntity method if you want to save tha changes over the child entity, so, I suggest you try this:
dbContext.UpdateGraph<Customer>(entity, map => map.OwnedEntity(x => x.Address)
.OwnedCollection(x => x.Categories, with => with.OwnedEntity(x => x.Category)));
dbContext.SaveChanges();
If I have the following model:
public class Customer
{
public int Id {get; set;}
public int CustomerTypeId {get; set;}
public virtual CustomerType {get; set;}
}
Should the Dto exclude foreign Id's to look like this:
public class CustomerDto
{
public int Id {get; set;}
public virtual CustomerType {get; set;}
}
And when using Graphdiff to update the object graph, will EF know that CustomerType maps to CustomerTypeId?
Yes, you need to use it but you can avoid virtual member declaration. If you use AutoMapper, then the mapping will be done automatically. So, your Dto will look like this:
public class CustomerDto
{
public int Id {get; set;}
public int CustomerTypeId {get; set;}
}
And the mapping:
Mapper.CreateMap<Customer, CustomerDto>();
Mapper.CreateMap<CustomerDto, Customer>();
I'm coding in ef 5 code first solution and i have a model as follow:
public class User
{
public int Id {get;set;}
public int Role1Id {get; set;}
public Role Role1 {get; set;}
}
and another model:
public class Role
{
public int Id { get; set;}
public string Title {get; set;}
}
also i configure this model in another class as follow:
public class UserConfig : EntityTypeConfiguration<User>
{
public UserConfig()
{
ToTable("User", "dbo");
// Here i want introduce Role1 as navigation property for Role1Id property
}
}
here is the question:How can i config User model to introduce Role1 as navigation property for Role1Id property?
You can use annotation:
public class User
{
public int Id {get;set;}
public int Role1Id {get; set;}
[ForeignKey("Role1Id")]
public Role Role1 {get; set;}
}
EF should configure it automatically as long as the id matches the field generated in the database schema.
You could try to configure it in UserConfig with the following:
HasRequired(user => user.Role1)
.WithRequiredDependent()
.Map(mapping => mapping.MapKey("Role1Id");
This configures it to be required. You can use the HasOption method if it's not required as well.