In Entity Framework, three entities have 1 to many relationships as grandparent, children and grandchildren.
How do you obtain an the grandparent object from a grandchild's primary key?
Thank you,
Newby to EF
You could do something like the following assuming the grandchildren have a reference (as a DbSet) to their parents, and those have a reference to their parents in return:
myGrandChildren.SelectMany(x => x.Parents).SelectMany(x => x.Parents);
What SelectMany does here is for each grandchildren select all its parents and return them as a single list (instead of as a list of lists - it concats them).
If you just have one grand child - you only need one SelectMany:
grandChild.Parents.SelectMany(x => x.Parents);
Query to context will be (grandchildId it is grandchild's primary key):
var grandparent = context
.Set<GrandParent>()
.SingleOrDefault(gp => gp.Children.Any(c => c.Children.Any(gc => gc.Id == grandchildId)));
if I understand, your classes looks like this:
class GrandParent
{
...
public List<Child> Children {get; set;}
}
class Child
{
...
public List<GrandChild> Children {get; set;}
}
class GrandChild
{
...
public int Id {get; set;}
}
Related
I have this:
Class Order
{
long ID;
...
}
Class Item
{
long Id;
IdProduct;
decimal Amount;
...
}
How could I map this in EF Core using fluent API? Or is it not possible and I have to have in the childs a property for the ID of the parent? But in my case, from a point of view of DDD I don't need this property in the children because the children has not need to know about the parent.
Thanks.
Assuming you have a collection of 'Item' in Order ...
class Order
{
public long ID { get; set; }
public List<Item> Items { get; set; }
}
Order Config:
builder.HasMany<Item>(p => p.Items)
.WithOne()
.HasForeignKey("OrderId");
This will create FK property OrderId on the Item without it being explicitly declared in the class itself.
I am new to entity framework and am having a hard time trying to figure out how to query with a join when my models look like this (drastically simplified)
class Customer
{
public int Id {get; set;}
public Vehicles Vehicles {get; set;}
}
class Vehicles
{
public List<Vehicle> Items {get; set;}
}
class Vehicle
{
public int Id {get; set;}
public int CustomerId {get; set;}
}
If I put the List<Vehicle> on the customer class directly. I am able to do fluent mapping like this
builder.Entity<Customer>()
.HasMany(x => x.Items)
.WithOne()
.HasForeignKey(x => x.CustomerId);
Which then I can do this and I get back a customer object with vehicles
db.Customers.Include(x => x.Items).FirstOrDefault(x => x.Id == 1);
What I am not understanding is how to do this with my original set of models. I would like to keep them the way they are if possible. I have tried doing various versions of this in my onModelCreating method with no luck.
builder.Entity<Customer>(t =>
{
t.OwnsOne(x => x.Vehicles, v =>
{
v.HasMany(x => x.Items).WithOne().HasForeignKey(x => x.CustomerId);
});
});
It's possible to map the original classes, but in quite counterintuitive way.
Since the Vehicles class is just a container, mapping it as owned entity as you have tried seems the most natural way. However currently EF Core does not allow owned entity to be at the principal side of the relationship, and in your case this is needed.
So instead you need to map the Vehicles class as regular "entity" sharing the same table with the Customer - the so called table splitting. You have to do explcitly all that EF Core does implicitly for owned entities - define a shadow property and map is a both PK and FK for the one-to-one relationship with the Customer. You'd need also the explicitly map the Vehicle.CustomerId as a FK because from EF point of view the Vehicle is related to Vehicles rather than to Custome, hence the conventional FK property / column name assumed will be VehiclesId. Note that with this model you'll never be able to define an inverse navigation property Customer of the Vehicle.
With that being said, here is the fluent configuration needed:
modelBuilder.Entity<Vehicles>(builder =>
{
// Table
builder.ToTable(modelBuilder.Entity<Customer>().Metadata.Relational().TableName);
// PK
builder.Property<int>("Id");
builder.HasKey("Id");
// One-to-one relationship with Customer
builder.HasOne<Customer>()
.WithOne(e => e.Vehicles)
.HasForeignKey<Vehicles>("Id");
// One-to-many relationship with Vehicle
builder.HasMany(e => e.Items)
.WithOne()
.HasForeignKey(e => e.CustomerId);
});
and usage:
db.Customers
.Include(x => x.Vehicles.Items) // <--
// ...
Use .Join
See this question for some examples:
Entity Framework Join 3 Tables
I have an EF Core 2.1 Code First model with a "parent-child" type relationship between two classes:
class Parent
{
public int Id {get; set;}
public string Name {get; set;}
}
class Child
{
public int Id {get; set;}
public string Description { get; set; }
public Parent Parent { get; set; }
}
I want to load a certain Parent, and make sure that all its Child entities are loaded too. However, there is no navigation property to Child, and I cannot modify the classes, so I can't add one.
dbContext.Parents
.Include(p => p.???)
.Find(1);
I suppose I could do a second query where I look everything up in reverse:
dbContext.Children.Where(c => c.Parent.Id == loadedParent.Id)
but that does not seem very efficient, especially when you load multiple parents and do something horrible like this:
var parentIds = loadedParents.Select(p => p.Id);
var children = dbContext.Children.Where(c => parentIds.Contains(c.Parent.Id));
Is there a way to make sure entities are loaded when you only have a "child-to-parent" navigation property?
make sure that all its Child entities are loaded too
So load the Child entities:
var children = dbContext.Children.Include(c => c.Parent)
.Where(c => c.Parent.Id == 1).ToList();
Or use wider selection criteria than c.Parent.Id == 1 if you want to get multiple parents.
If necessary you can list the loaded parents by accessing the Local collection:
va parents = dbContext.Parents.Local;
Using EF 2.0 Core, code first, I have the following entity which defines a self-referencing table:
class EntityX
{
public int EntityXId { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
//navigation properties
public EntityX Parent { get; set; }
public ICollection<EntityX> Children { get; set; }
}
I want to retrieve all EntityX objects and their children in the form of a 'tree'
I can do that using:
var entities = context.EntityX
.Include(p => p.Parent)
.Include(p => p.Children)
.Where(p => p.Parent == null);
When I call entities.ToList() this gets me what I want: a list of parent entities with their children edit only 'first' generation children. When I omit the Where() clause, I get all entities and their children.
I do not understand why the Where() clause works. Objects that are part of the Children collection have a Parent. Why are they not omitted?
Edit: my question was answered but please be aware that I was wrong in my perception of how Include() works.
LINQ applies Where condition only to the objects in the collection being queried. Whatever else you choose to load with the Include method is not subject to the same condition. In fact, EF provides no direct way of restricting the associated entities (see this Q&A).
That is how the top-level query brings you what you expected. EF retrieves the children recursively via a separate RDBMS query using the parent ID to get all its children. The restriction from the Where method does not make it to the child query.
I'm attempting to query 5 parent records, then a summary of all child categories and counts:
context.Parent
.Take(5)
.GroupJoin(inner: context.Child,
outerKeySelector: parent => parent.Id,
innerKeySelector: child => child.ParentId,
resultSelector: (parent, children) => new SummaryResult
{
Id = parent.Id,
Name = parent.Name,
Children = parent.Children
.GroupBy(c => c.Category)
.Select(group => new ChildCategorySummary
{
Category = group.Key,
Count = group.Count()
})
});
This works as expected from LinkPad---I get one query for the first 5 Parent records then five queries summarizing each of the groups of children.
However, in EF7, I get this query:
exec sp_executesql N'SELECT [child].[Id], [child].[Category], [t].[Id]
FROM (
SELECT TOP(#__p_0) [s0].*
FROM [Parent] AS [s0]
) AS [t]
LEFT JOIN [Child] AS [child] ON [t].[Id] = [child].[ParentId]
ORDER BY [t].[Id]',N'#__p_0 int',#__p_0=5
and then this, five times:
SELECT [p].[Id], [p].[Category]
FROM [Child] AS [p]
I didn't expect the left join in the first query, which gives me all the child records.
Is this a problem with my query?
Partially figured this out. The child class has both Parent and ParentId:
public class Child
{
[Key]
public virtual Guid Id { get; set; }
[Required]
public virtual Parent Parent { get; set; }
public virtual Guid ParentId { get; set; }
}
So the selector should use the required field Parent, not 'ParentId`:
outerKeySelector: parent => parent,
innerKeySelector: child => child.parent
However, I now have a different error, which seems to be an EF Core bug:
System.ArgumentException : Property 'MyApp.Models.Parent Parent' is not defined for type 'MyApp.Models.Parent'
Parameter name: property
at System.Linq.Expressions.Expression.Property(Expression expression, PropertyInfo property)
at System.Linq.Expressions.Expression.MakeMemberAccess(Expression expression, MemberInfo member)
...
I think this is fixed in a prerelease version of EF Core 2.