At first I created a Person model which only contained the basic properties:
[Table("SGDB_Persons")]
public class Person {
public int Id { get; set; }
[Required]
public string Firstname { get; set; }
[Required]
public string Lastname { get; set; }
[Required]
public Department Department { get; set; }
[Required]
public SourceType SourceType { get; set; }
After I noticed I'm missing something I've added a new PersonData Property:
[Required]
public PersonData PersonData { get; set; }
Unfortunately EF won't update the Database at all - PersonData which at first contained an object of type Person got updated so there is no Person property anymore. On the other hand, EF does not create a new Column for PersonData_Id.
Additionally the ID column is not auto-incrementing (all other table's Id Column do). What's confusing me is the following Constraing which gets created inside my Person table:
CONSTRAINT [FK_dbo.SGDB_Persons_dbo.SGDB_PersonData_Id] FOREIGN KEY ([Id]) REFERENCES [dbo].[SGDB_PersonData] ([Id])
I tried everything (at least I think so). I dropped all tables / the whole database manually, reinstalled EF, executed manual migrations but nothing seems to work.
I think it's this problem which causes me to not be able to seed my database with the following code:
protected override void Seed(PersonContext context) {
base.Seed(context);
var dep = new DepartmentContext().Departments.First();
var status = new Status("Test");
var persondata = new PersonData(status);
context.Status.Add(status);
context.PersonData.Add(persondata);
for (int i = 0; i < 10; i++) {
var person = new Person {
Firstname = $"TestPersonFirstname{i}",
Lastname = $"TestPersonLastname{i}",
SourceType = COM.SourceType.Manual,
Department = dep,
PersonData = persondata
};
context.Persons.Add(person);
}
context.SaveChanges();
}
Everytime this code get's executed I'm getting an Exception:
The member with identity 'SGDB.DAL.Contexts.Person_Department' does not exist in the metadata collection. Parameter name: identity.
I don't know if both problems are related to the same problem but both need to be resolved :)
Thanks in advance!
Update 1
My Solution is divided into a few different Projects:
BLL, DAL, COM, UI
DataContexts are located inside the DAL project, Models inside the COM project.
Department Model:
[Table("SGDB_Departments")]
public class Department {
public int Id { get; set; }
[Required]
public string Costcenter { get; set; }
[Required]
public string Abbreviation { get; set; }
public string Description { get; set; }
public string FullDepartmentName {
get {
return $#"{Division.Abbreviation}\{Abbreviation}";
}
}
[Required]
public virtual Division Division { get; set; }
}
PersonData Model:
[Table("SGDB_PersonData")]
public class PersonData {
public PersonData(Status status) {
Status = status;
}
public int Id { get; set; }
public DateTime Limit { get; set; }
public Person Responsible { get; set; }
[Required]
public Status Status { get; set; }
}
The Person table (as you can see) has got a Department_Id column (EF inserted automatically).
Clarification
A Person object contains a PersonData object as additional Information for this Person. A Person may / may not has a Responsible Person (so PersonData.Responsible is not a Navigation Property to the Parent Person).
Additionaly if possible I don't want to have a foreign Key inside the PersonData table.
As I figured out I'd have to modify
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal(e => e.Responsible)
.WillCascadeOnDelete(true);
to
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithMany()
.WillCascadeOnDelete(true);
I'll try this and report in if it solved my problem.
Update 2
The member with identity 'SGDB.DAL.Contexts.Person_Department' does not exist in the metadata collection.
Your model defines one-to-one relationship between Person and PersonData with later being required and the former - optional. EF always uses the required side of the one-to-one relationship as principal and optional part as dependent. Hence it thinks PersonaData is the principal and Person - dependent and reflects that in database table design.
You need the opposite and also both sides being required. When both sides are required or optional, EF cannot automatically derive the principal/dependent side and there is no way to specify that via data annotations (attributes), so you need a fluent API setup.
Override your DbContext OnModelCreating and add something like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal(e => e.Responsible)
.WillCascadeOnDelete(true);
base.OnModelCreating(modelBuilder);
}
What it does is to tell EF that both sides of the Person->PersonData relationship are required and Person is the principal. This should make again your Person.Id column auto-increment and should resolve the person-data part of the problem.
The other thing I've noticed is this line:
var dep = new DepartmentContext().Departments.First();
while all other parts of the same procedure are using a variable called context. This might/might not be a problem, just check it out.
UPDATE: From the clarification in the updated question it turns out you have two relationships between Person and PersonData, so you need separate configuration for each of them like this:
modelBuilder.Entity<Person>()
.HasRequired(e => e.PersonData)
.WithRequiredPrincipal()
.WillCascadeOnDelete(true);
modelBuilder.Entity<PersonData>()
.HasOptional(e => e.Responsible)
.WithOptionalDependent() // or WithMany()
.WillCascadeOnDelete(false);
Please note that there is no way to not introduce additional FK column in the PersonData table. It's needed to represent the Responsible relation, so you'll end up with a table column called Responsible_Id.
Related
OK so this should be simple. I have a class
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
}
These two navigation properties are also primary keys for the table.
Declaring PRoductId and CategoryIds are redundat. How can get configure the primary keys using the nav properties?
edit: Stupid me. I forgot something very important in my question above. Those two above are to point out the config. Then we have a third fk thats the selected config for the combination of Product and category. So above entity must be a materialized entity
public class ProductConfig
{
public Category { get;set; }
public Product { get;set; }
public ProductCategoryType { get; set; }
}
Declaring ProductId and CategoryId are redundant. How can get configure the primary keys using the nav properties?
Shortly - you can't. While EF6 supports shadow property based FKs, it does not provide a way to configure the PK (and many other column related settings) using the shadow property names - [Key], [Column]data annotations cannot be applied on navigation property and HasKey fluent API requires primitive property selector expression. In general EF6 does not support shadow properties in PK.
All these limitations have been removed in EF Core. But in EF6, redundant or not, you must define the actual primitive properties in the entity and map them to the composite PK.
You have only to set up a relationship between Product and Category entities by navigation properties. EF will set up the correct table structure by its own as many-to-many relationship. So no own relationship entity is needed.
Please check this out: many-to-many-relationship in EF
e.g.:
Product class:
public class Product
{
// other properties
public virtual ICollection<Category> Categories { get; set; }
}
Category class:
public class Category
{
// other properties
public virtual ICollection<Product> Products { get; set; }
}
Or did I misunderstood your question?
EDIT:
IF you need an separate entity like your ProductConfig, than you should try to set it as a unique index constraint by following:
modelBuilder
.Entity<ProductConfig>()
.HasIndex(pc => new {pc.Category, pc.Product})
.IsUnique();
For further information you should read this: HasIndex - Fluent API
EDIT 2 (after getting info solution is for EF < 6.2 needed):
Well after your last question edit, another solution approach is needed.
Here we go...
You need a structure like followed:
Product
public class Product
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
Category
public class Category
{
// other properties
public virtual ICollection<ProductConfig> ProductConfigs { get; set; }
}
ProductConfig
public class ProductConfig
{
// other properties
public virtual Category { get; set; }
public virtual Product { get; set; }
public virtual ProductCategoryType { get; set; }
}
To set up a unique constraint in EF < 6.2 you have to do it like that way:
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Category)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 1) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.Product)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 2) { IsUnique = true }));
modelBuilder.Entity<ProductConfig>()
.Property(e => e.ProductCategoryType)
.HasColumnAnnotation(
IndexAnnotation.AnnotationName,
new IndexAnnotation(new IndexAttribute("YourIndex", 3) { IsUnique = true }));
in EF 6.2:
modelBuilder.Entity<Person>()
.HasIndex(p => new { p.Category, p.Product, p.ProductCategoryType })
.IsUnique();
EDIT 3
If you have no primary key in your ProductConfig class or you used mine in the example where I added none, because I thought you already have that class.
It is possible to set up multiple properties as key. That will result in unique combinations too.
You would archive that with the following - instead of the index stuff:
modelBuilder.Entity<ProductConfig>()
.HasKey(pc => new { pc.Category, pc.Product, pc.ProductCategoryType });
For further information check out the MS docs.
You could also add an Id as primary key, than the indexes are needed.
This question already has answers here:
EF Core returns null relations until direct access
(2 answers)
Closed 5 years ago.
I'm using .net core 2 mvc, I tried to build many-to-many relationship between Users and Steps.
the relationship is doen but when I query for the record I get user = null.
Hier is my code:
(applicationUser model):
public class ApplicationUser : IdentityUser
{
public string Name { get; set; }
public List<StepsUsers> StepUser { get; set; }
}
(Steps model):
public class Steps
{
public int Id { get; set; }
[Required]
public string Name { get; set; }
public List<StepsUsers> StepUser { get; set; }
}
StepsUsers model:
public class StepsUsers : IAuditable
{
public int StepId { get; set; }
public Steps Step { get; set; }
public string UserId { get; set; }
public ApplicationUser User { get; set; }
}
In DbContext I did this :
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
builder.Entity<StepsUsers>()
.HasKey(s => new { s.StepId, s.UserId });
builder.Entity<StepsUsers>()
.HasOne(su => su.Step)
.WithMany(s => s.StepUser)
.HasForeignKey(su => su.StepId);
builder.Entity<StepsUsers>()
.HasOne(su => su.User)
.WithMany(s => s.StepUser)
.HasForeignKey(su => su.UserId);
}
public DbSet<MyApp.Models.StepsUsers> StepsUsers { get; set; }
Now, when I query for an instance of StepsUsers with specific StepId I get all de fields correct except the User field is null
var stepUsers = await _context.StepsUsers.Where(s => s.StepId == id).ToListAsync();
I did the same code for another two tables and it works fine, I don't know why it is like this, any suggestion 1?
The cause of your problems is that your forgot to declare your To-many relations as virtual. Another improvement would be to declare them as virtual ICollection instead of List. After all, what would ApplicationUser.StepUser[4] mean?
If you configure a many-to-many relationship according to the entity framework conventions for many-to-many, you don't need to mention the junction table (StepsUsers). Entity framework will recognize the many-to-many and will create the junction table for you. If you stick to the code first conventions you won't even need the fluent API to configure the many-to-many.
In your design every ApplicationUser has zero or more Steps and every Step is done by zero or more ApplicationUsers.
class ApplicationUser
{
public int Id {get; set;}
// every ApplicationUser has zero or more Steps:
public virtual ICollection<Step> Steps {get; set;}
public string Name {get; set;}
...
}
class Step
{
public int Id {get; set;}
// every Step is performed by zero or more ApplicationUsers:
public virtual ICollection<ApplicationUser> ApplicationUsers {get; set;}
public string Name {get; set;}
...
}
public MyDbContext : DbContext
{
public DbSet<ApplicationUser ApplictionUsers {get; set;}
public DbSet<Step> Steps {get; set;}
}
This is all entity framework needs to know to recognize that you configured a many-to-many relationship. Entity framework will create the junction table for you and the foreign keys to the junction table. You don't need to declare the junction table.
But how am I suppose to do a join if I don't have the junction table?
The answer is: Don't do the join. Use the collections instead.
If you want all ApplicationUsers that ... with all their Steps that ... you would normally do an inner join with the junction table, and do some group by to get the Application users. Ever tried method syntax to join three tables? They look hideous, difficult to understand, error prone and difficult to maintain.
Using the collections in entity framework your query would be much simpler:
var result = myDbContext.ApplicationUsers
.Where(applicationUser => applicationUser.Name == ...)
.Select(applicationUser => new
{
// select only the properties you plan to use:
Name = applicationUser.Name,
Steps = applicationUser.Steps
.Where(step => step.Name == ...)
.Select(step => new
{
// again fetch only Step properties you plan to use
Name = step.Name,
...
})
.ToList(),
});
Entity framework will recognize that joins with the junction table is needed and perform them for you.
If you want Steps that ... with their ApplicationUsers who ... you'll do something similar:
var result = myDbContext.Steps
.Where(step => ...)
.Select(step => new
{
Name = step.Name,
... // other properties
ApplicationUsers = step.ApplicationUsers
.Where(applicationUser => ...)
.Select(applicationUser => new
{
...
})
.ToList(),
});
In my experience, whenever I think of performing a query with a of DbSets using entity framework, whether it is in a many-to-many, a one-to-many or a one-to-one relation, the query can almost always be created using the collections instead of a join. They look simpler, they are better to understand and thus better to maintain.
I need to insert an entity WITH a related entity inside, both in a single DbSet.Add invocation.
One-to-many between Course and CourseProfesor (CourseProfesor is the entity connecting Courses and Profesors)
Entities:
public class Course
{
public Course() { }
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int ID { get; set; }
...
public virtual ICollection<CourseProfesor> Profesors { get; set; }
}
public class CourseProfesor
{
public CourseProfesor() { }
[Key]
public int ID { get; set; }
[Required, Index, Column(Order = 0)]
public int CourseID { get; set; }
[Required, Index, Column(Order = 1)]
public int ProfesorID { get; set; }
...
[ForeignKey("CourseID")]
public virtual Course Course { get; set; }
[ForeignKey("ProfesorID")]
public virtual Profesor Profesor { get; set; }
}
Mappings:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Course>().HasMany(x => x.Profesors).WithRequired(x => x.Course);
modelBuilder.Entity<CourseProfesor>().HasRequired(x => x.Course).WithMany(x => x.Profesors);
modelBuilder.Entity<CourseProfesor>().HasRequired(x => x.Profesor).WithMany(x => x.Courses);
}
Controller:
public ActionResult Add(Course course, int profesorId = 0)
{
if (profesor > 0)
{
course.Profesors = new List<CourseProfesor>();
course.Profesors.Add(new CourseProfesor() { Course = course, CourseID = 0, ProfesorID = profesorId, From = DateTime.Now, Role = ... });
}
Facade.Create(course);
return Json(new {statusText = "Course Added"});
}
Facade.Create(entity) executes a CreateCommand which will in turn invoke
DbContext.Set(entity.GetType()).Add(entity)
The exception I get:
The changes to the database were committed successfully, but an error occurred while updating the object context. The ObjectContext might be in an inconsistent state. Inner exception message: A referential integrity constraint violation occurred: The property value(s) of 'Course.ID' on one end of a relationship do not match the property value(s) of 'CourseProfesor.CourseID' on the other end
how to assign CourseProfesor.CourseID if I don't know the ID of the course yet, since both are new entities?
As you can see in the controller code, I used to worked that out that by setting only the navigation property and EF would auto-populate foreign key accordingly.
This is important: This was working fine on EF5, I got that error after updating to EF6
Any clues why EF6 throws that exception while EF5 didn't? and how to solve it without having to first create the Course and then the CourseProfesor relationship entity?
A couple of things stand-out:
course.Profesors.Add(new CourseProfesor() { Course = course, CourseID = 0, ProfesorID = profesorId, From = DateTime.Now, Role = ... });
When using navigation properties I steer away from defining the FKs fields in the entities, but if you need to define them, you should avoid setting them. Use the navigation properties only. Setting FKs can be misleading because if you were to pass this Course out with it's CourseProfessors and consume it, there would be a ProfessorID set, but no Professor reference available.
Regarding your specific problem the probable issues would be the CourseID = 0 set above, and the two-way mapping between the Course and CourseProfessor.
modelBuilder.Entity<Course>().HasMany(x => x.Profesors).WithRequired(x => x.Course);
//modelBuilder.Entity<CourseProfesor>().HasRequired(x => x.Course).WithMany(x => x.Profesors);
modelBuilder.Entity<CourseProfesor>().HasRequired(x => x.Profesor).WithMany(x => x.Courses);
Try removing that redundant mapping.
Also the definition of the linking table has an ID as a PK, but isn't set up with a generation option. The two FKs are set up Order=0 and Order=1 as well, which looks odd without considering the PK?
Beyond that, I can't say I like that facade pattern. To eliminate the possibility, what happens if you go to the DB Context Courses DBSet?
context.Courses.Add(course);
context.SaveChanges();
What is wrong in my code that i get below error:
Unable to determine a valid ordering for dependent operations. Dependencies may exist due to foreign key constraints, model requirements, or store-generated values
Code:
Class Food:
public class Food
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public short Id { get; set; }
//some Property
public string Name { get; set; }
public virtual ICollection<Person> Persons { get; set; }
}
Class Person:
public class Person
{
[Key]
[DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
//some Property
public string FirstName { get; set; }
[ForeignKey("BestFoodId")]
public Food BestFood { get; set; }
public short BestFoodId { get; set; }
public virtual ICollection<Food> FavoriteFoods { get; set; }
}
Seed Method:
protected override void Seed(MyContext context)
{
Food food1 = new Food() { Name = "foo1" };
Food food2 = new Food() { Name = "foo2" };
Food food3 = new Food() { Name = "foo3" };
context.Persons.AddOrUpdate(new Person()
{
FirstName = "Jack",
BestFood = food2,
FavoriteFoods = new List<Food>() { food1, food2, food3 }
});
}
Cause of the error: confused associations
This happens because Entity Framework, by convention, assumes that the inverse property of Person.BestFoodId is Food.Persons. Stated differently: Person.BestFood and Food.Persons are assumed to be the two ends of a one-to-many association, having Person.BestFoodId as foreign key.
You can verify that by adding an [InverseProperty] attribute to BestFood:
public class Person
{
...
[ForeignKey("BestFoodId")]
[InverseProperty("Persons")]
public Food BestFood { get; set; }
...
}
This causes the same error.
This error --no valid ordering-- always indicates a chicken-and-egg problem. In your case, EF tries to insert the foods, which need the generated Id of the inserted person as foreign key, while the inserted person needs the generated Id of the inserted foo2 food.
Solution: explicitly mapped association
In reality, Person and Food have two associations:
1-n: Food can be BestFood of n people.
n-m: n Foods can be the FavoriteFoods of m people.
In your model, BestFood doesn't have an inverse property, which could have been something as ...
public virtual ICollection<Person> BestFoodOf { get; set; }
... but it isn't necessary and because it's missing, it obscures how EF infers the associations.
You can fix this by explicitly mapping the associations, for instance in the OnModelCreating override of your DbContext subclass:
modelBuilder.Entity<Person>()
.HasRequired(p => p.BestFood)
.WithMany() // No inverse property
.HasForeignKey(p => p.BestFoodId)
//.WillCascadeOnDelete(false)
;
modelBuilder.Entity<Person>()
.HasMany(p => p.FavoriteFoods)
.WithMany(f => f.Persons)
.Map(m => m.MapLeftKey("PersonId")
.MapRightKey("FoodId")
.ToTable("PersonFavoriteFood"));
I have commented out WillCascadeOnDelete(false). You either have to add this line, or add ...
modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
... to prevent multiple cascaded delete paths (a SQL Server restrictions).
Having this in place, EF knows how to determine a valid ordering for the inserts: it will will first insert the foods, then insert the person (using the generated foo2 Id as foreign key) and then the junction records in the PersonFavoriteFood table.
Looks like you have a circular dependency.
Answers are here:
Unable to determine a valid ordering for dependent operations
Unable to determine a valid ordering for dependent operations?
Entity Framework Code First Circular Dependices
Entity Framework 4: inheritance and Associations
Entity Framework Circular Reference
Code First Circular Reference Foreign Key Configuration
How to configure a self referencing object in entity framework
Optional improvements:
You should declare your navigation property as virtual!
If you are using C# 6.0 or above, change your [ForeignKeyAttribute] Data Annotation definition to [ForeignKey([nameof(BestFoodId))] to avoid errors with hard coded property names. nameof is a really cool compiler feature! :)
I'm using EntityFramework via DbContext and an Exisiting Database.
When I Add an Order entity to my context and call SaveChanges(), I'm encountering an exception of: "A dependent property in a ReferentialConstraint is mapped to a store-generated column. Column: OrderId".
I believe this is happening because of the composite key on my OrderAddress table and I'm hoping there is a way around it...I don't want to create an IDENTITY on that table.
Here are my entities, simplified...
// OrderId is an IDENTITY PK
public class Order
{
public int OrderId { get; set; }
public IList<OrderAddress> Addresses { get; set; }
public int Total { get; set; }
}
// PK on this table is composite key of OrderId and OrderAddressTypeId
public class OrderAddress
{
public int OrderId { get; set; }
public int OrderAddressTypeId { get; set; }
public string Address { get; set; }
}
Here is my Context, simplified...
public class StoreContext : DbContext
{
DbSet<Order> Orders { get; set; }
DbSet<OrderAddress> OrderAddresses { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// Set the Identity for Order
modelBuilder.Entity<Order>()
.Property(x => x.OrderId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.Identity);
// Set composite key for Order Address
modelBuilder.Entity<OrderAddress>()
.HasKey(x => new { x.OrderId, x.OrderAddressTypeId });
}
}
NOTE: I've looked at the other SO questions that are similar and tried the solutions listed with no success. I've verified that my foreign keys are setup correctly in the database. What's different about my question is the use of the composite key.
Thanks in advance for the help.
UPDATE:
This ended up not being related to the composite key at all. There was an additional line in my Context OnModelCreating method that required a child entity, OrderSummary, which is based on a View in my database. The line looked like this...
modelBuilder.Entity<OrderSummary>().HasRequired(x => x.Order).WithRequiredPrincipal(x => x.OrderSummary);
I had never intended for OrderSummary to be a required principal of Order. Changing it to the following fixed the problem...
modelBuilder.Entity<OrderSummary>().HasRequired(x => x.Order);
Unfortunately, the error message from EF was not very specific and lead me on a wild good chase.
Thanks for looking.
This error says that some OrderId property (the exception should contain information about the entity or relation where this happens) is mapped as store generated = it has DatabaseGeneratedOption set to Identity or Computed. If the issue is related to OrderAddress entity try to add this to your mapping definition:
modelBuilder.Entity<OrderAddress>()
.Property(x => x.OrderId)
.HasDatabaseGeneratedOption(DatabaseGeneratedOption.None);