I need to setup a one-to-many relationship with entity framework.
I am trying to build a recipe, but a recipe CAN be composed of recipes.
How to achieve that with EF ?
public class Recipe
{
public Recipe()
{
Deleted = false;
Recipes = new List<Recipe>();
}
[Key]
public int RecipeId { get; set; }
public virtual List<Recipe> Recipes { get; set; }
}
I don't need the whole recursive thing(parent, child) only the child I'm interested in. Is it feasible using EF ? Can anyone point me to the right direction
Ex:
Recipe A
Recipe B => A
Recipe C
Recipe D => B
Recipe E => B, C
This will be a many-to-many relationship, because each recipe can have multiple and multiple parent recipes. When you create your child recipes, you will have to assign a parent to it, so you must define the parent relationship as well.
You could try the InverseProperty dataannotation, for more info check out this question:
Entity Framework 4.1 InverseProperty Attribute
Related
I have an existing data model in EF Core 6, which has a self-referencing many-to-many relationship. It's set up as follows:
public class Item
{
[Key]
public long Id { get; set; }
public ICollection<Item> DependentItems { get; set; }
public ICollection<Item> DependsOnItems { get; set; }
}
In my DbContext, I have overridden the OnModelCreating(ModelBuilder) method to configure the many-to-many relationship as follows:
builder.Entity<Item>()
.HasMany(o => o.DependentItems)
.WithMany(o => o.DependsOnItems)
.UsingEntity<Dictionary<string, object>>("ItemDependencies",
o => o.HasOne<Item>().WithMany().HasForeignKey("ItemId").OnDelete(DeleteBehavior.Restrict),
o => o.HasOne<Item>().WithMany().HasForeignKey("DependsOnItemId").OnDelete(DeleteBehavior.Restrict)
).ToTable("ItemDependencies");
This works well, I am using both navigation properties to find parent and/or child relationships in various places in the application. Users recently asked me to add a text column with some comments to the relationship.
I am trying to figure out how I can accomplish this in EF, while maintaining relationships which have already been created and stored in the ItemDependencies table. I'm not quite sure how I can achieve this, some pointers would be appreciated.
Imagine a model of User that can have Parents and also can have Children.
How would you model such a case in EF Core?
I tried with something like that (pseudo-code)
public class User
{
public ICollection<Relation> Relations {get;set;}
public ICollection<User> Parents => Relation.Where(r => r.Relation == 'Parents')
public ICollection<User> Children => Relation.Where(r => r.Relation == 'Children')
}
public class Relaction
{
public User User1 {get;set;}
public Guid User1Id {get;set;}
public User User2 {get;set;}
public Guid User2Id {get;set;}
public Relation Relation {get;set;} //some enum or sth to indicate relation type
}
But in such modeling, I'm not able to force EF DbContext to fetch into User.Relations data where UserId is in User1Id and in User2Id.
Any idea?
What you are asking for is a classic many-to-many self relationship - (1) user as parent can have many users as children, and (2) user as child can have many users as parents.
Thus it is modelled with one main entity and one join (linking) entity similar to what you have shown. The linking entity does not need special indicator because the two FKs determine the role. i.e. lets change your example with more descriptive names:
public class User
{
public Guid Id { get; set; }
}
public class UserRelation
{
public User Parent { get; set; }
public User Child { get; set; }
public Guid ParentId { get; set; }
public Guid ChildId { get; set; }
}
Now, in pseudo code, given User user, then
user.Parents = db.Users.Where(u => user == u.Child)
user.Children = db.Users.Where(u => user == u.Parent)
EF Core 5.0+ allows you to hide the join entity (it still exists, but is maintained implicitly) and model the relationship with the so called skip navigations, which are the natural OO way of representing such relationship, e.g. the model becomes simply
public class User
{
public Guid Id { get; }
public ICollection<User> Parents { get; set; }
public ICollection<User> Children { get; set; }
}
This is all needed to create such relationship.
However the name of the join table and its columns by convention won't be what normally you would do - in this case, they would be "UserUser" table with "ParentsId" and "ChildrenId" columns.
If you use migrations and don't care about the names, then you are done and can safely skip the rest.
If you do care though, luckily EF Core allows you to change the defaults with fluent configuration (even though in a not so intuitive way):
modelBuilder.Entity<User>()
.HasMany(e => e.Parents)
.WithMany(e => e.Children)
.UsingEntity<Dictionary<string, object>>("UserRelation",
je => je.HasOne<User>().WithMany()
.HasForeignKey("ParentId").IsRequired().OnDelete(DeleteBehavior.Restrict),
je => je.HasOne<User>().WithMany()
.HasForeignKey("ChildId").IsRequired().OnDelete(DeleteBehavior.Restrict),
je => je.ToTable("UserRelations")
.HasKey("ParentId", "ChildId")
);
Here Dictionary<string, object> is the shared type EF Core will use to maintain the join entity in memory (change tracker). And is the most annoying thing in the above configuration since in a future they might change their minds and use different type (there are actually plans to do that in EF Core 6.0), so you'll have to update your mappings. Note that this does not affect the database design, just the memory storage type in EF Core change tracker.
So, because of that and the fact that in some scenarios it is better to work directly with the join entity, you could actually combine both approaches (explicit join entity and skip navigations) and get the best of both worlds.
To do than, you add the explicit entity and (optionally) navigations from/to it. The next is w/o collection navigations from User to UserRelation (with fully defined navigation you would need two ICollection<UserRelation> properties there):
public class User
{
public Guid Id { get; }
public ICollection<User> Parents { get; set; }
public ICollection<User> Children { get; set; }
}
public class UserRelation
{
public User Parent { get; set; }
public User Child { get; set; }
public Guid ParentId { get; set; }
public Guid ChildId { get; set; }
}
and required minimal fluent configuration
modelBuilder.Entity<User>()
.HasMany(e => e.Parents)
.WithMany(e => e.Children)
.UsingEntity<UserRelation>(
je => je.HasOne(e => e.Parent).WithMany(), // <-- here you would specify the corresponding collection nav property when exists
je => je.HasOne(e => e.Child).WithMany(), // <-- here you would specify the corresponding collection nav property when exists
je => je.ToTable("UserRelations")
);
The result is the same database model, but with different in-memory representation of the join entity and ability to query/manipulate it directly. Actually you can do the same with implicit entity, but in type unsafe way using sting names and object values which need to be cast to the appropriate type. This probably will improve in the future if they replace Dictionary<string, object> with some generic type, but for now explicit entity combined with skip navigations looks the best.
You can find (I guess better than mine) explanation of all this in the official EF Core documentation - Many-to-many and the whole Relationships section in general.
I am using EF Core and I need to map child entities to the same table, because they are of the same type.
Imagine the following scenario of class hierarchy:
public class Grandpa
{
public Guid Id { get; set; }
public Father Robert { get; set; }
public Father Jack { get; set; }
}
public class Father
{
public Guid Id { get; set; }
// I use this to reference Grandpa
public Guid GrandpaId { get; set; }
public List<Son> MySons { get; set; }
}
public class Son
{
public Guid Id { get; set; }
// I use this to reference Father
public Guid FatherId { get; set; }
public string Name { get; set; }
}
Now the issue is the following: in my scenario (the above is the abstraction replicating my real scenario), I cannot make Robert and Jack an array - they need to be there separately. The other thing is, the Grandpa entity owns 2 Father entities, and each Father entity owns a list of Son entities.
I want to tell ModelBuilder to generate my tables in such a way that there is ONE TABLE for all Father entities, and ONE TABLE for all Son entities - because they are all the same entities, and they should never be placed in different tables as their primary keys and foreign keys (IDs) clearly reference who they belong to.
Now, you know, in plain SQL, it's easy to achieve such a structure, very simple to write the SQL code for this. However, how do I do it with EF Core?
I've tried:
1) Specifying all primary keys and foreign keys in model builder, per entity
2) Setup the owned property hierarchy in ModelBuilder using
modelBuilder.Entity<Father>()
.ToTable("Fathers");
modelBuilder.Entity<Son>()
.ToTable("Sons");
modelBuilder.Entity<Grandpa>(g =>
{
x.OwnsOne(y => y.Robert, z =>
{
z.OwnsMany(a => a.MySons, b =>
{
b.HasKey(k=>k.Id);
b.ToTable("Sons");
});
z.ToTable("Fathers");
});
x.OwnsOne(y => y.Jack, z =>
{
z.OwnsMany(a => a.MySons, b =>
{
b.HasKey(k=>k.Id);
b.ToTable("Sons");
});
z.ToTable("Fathers");
});
});
However, there are errors saying:
Cannot use table 'Fathers' for entity type 'Grandpa.Father#Robert' since it is being used for entity type 'Grandpa.Father#Jack' and there is no relationship between their primary keys.
I must mention that these entities are part of the same DbContext - and they need to remain so, preferably.
The way EF Core maps or fails to map entities is wrong - if I remove the table name specifications, it just creates separate tables for my entities of the same type, which is wrong. I am not even in the case of having the same entity declared in a different namespace/duplicated, but it's actually the same namespace, the same entity, the Grandpa entity has two Father entities, and each Father entity has a List<Son>.
Suppose I have two classes that have a many-to-one relationship
public class ParentObject{
public int Id { get; set; }
public List<ChildObject> Children { get; set; }
...
}
public class ChildObject{
public int Id { get; set; }
public ParentObject Parent { get; set; }
}
When I add the migration and update the database, the result is two tables:
ParentObject
Id : int
...
ChildObject
Id : int
ParentObjectId : int
...
Now I want to search for all of the children of a particular parent. In SQL, I can do this without having to join in the parent table simply by querying on the ChildObject.ParentObjectId column.
SELECT * FROM [ChildObject] c WHERE c.ParentObjectId = parentId
But in EF Core, the only way I've found to do this is to use .Include(c => c.Parent) and then .FirstOrDefault(c => c.Parent.Id == parentId). This creates a join to the ParentObject table.
Is there a way to query the ChildObject table without the .Include()?
Simply use the navigation property to access the parent PK:
var children = db.Children
.Where(c => c.Parent.Id == parentId)
.ToList();
If you include some other Parent entity property in Where, OrderBy, Select (or the Parent itself inside Select or Include), of course EF Core will create a join. But if you access only the PK of the related entity, then EF Core is smart enough the use the FK instead and avoid the join.
Please note that the returned Child objects won't have Parent property populated (will be null) except the context is not already tracking a Parent entity instance with that PK.
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.