Loading grandchild object in child collection, EF [duplicate] - c#

This question already has answers here:
Include Grandchildren in EF Query
(2 answers)
Closed 7 years ago.
Model:
public class Parent
{
public int Id { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public int IdGrandChild { get; set; }
public virtual Grandchild Grandchild { get; set; }
}
public class Grandchild
{
public int Id { get; set; }
}
I have an existing Parent entity with a Children collection. After some operations i want to load newly added children objects to Parent's collection from database like this
_context.Entry(parent).Collection(f => f.Children).Load();
But when i am doing this like that, the Grandchild object in every newly added collection element is null. I also tried to include grandchild in this way but still the Grandchild object is null.
_context.Entry(parent).Collection(f => f.Children).Query().Include(c => c.Grandchild).Load();
How to correctly load the new items to the Parent's Children collection including Grandchild objects?
EDIT:
I dont know why this question was marked as duplicate?
My problem is: I already have existing (loaded/tracked) parent entity in one context instance (form) and then in another context instance (form) I modified entity child's collection ( add or remove child). Finally I want to load these newly added entries to parent entity collection in first context instance using one of this previously written methods but after that the newly added object are loaded but their grandchild's are nulls.
I don't know how to correctly load these new child objects to existed (tracked) parent entity without getting a nulls.

I usually use the following(wihout load)
Parent parent = _context.Parent.Include(p => p.Children.Grandchild).FirstOrDefault();
and if my Grandchild was a collection, I would use
Parent parent = _context.Parent.Include(p => p.Children.Select(c => c.Grandchild).FirstOrDefault();

Related

C# linq to entity include with condition and ordering

I have the following (simplified) setup:
Public class parent
{
public string name{get;set;}
public list<child> childList {get;set;}
}
Public class child
{
public int id {get;set;}
public bool imported{get;set;}
public dateTime? timeSpan {get;set;}
}
and I have this query:
var relevant = context.parent
.include(x => x.child.OrderByDescending(y => y.id).FirstOrDefaultAsync(z => z.imported == false && timeSpan == null)
.Where(x => x.child != null);
Which does not work.
Basically, I am trying to include all the parents children, but order them by id descending and then check if the first one (eg newest one) has imported == false and timeSpan == null, and only include the parent rows that have a child that meets this condition.
I know I could do this:
var relevant = context.parent
.include(x => x.child);
and then extract the data I need, but is it possible to do it in one using Linq?
As you are using the tag linq-to-entities I assume you are using entity framework.
It seems to me that you have modeled a one-to-many relation between Parent and Child: every Parent has zero or more Children, and every Child belongs to exactly one Parent.
It could also be that you have a many-to-many relation. The classes are slightly different (and the database will have an extra table that you don't have in your DbContext), but the problem remains the same.
It could be because of your simplifications, but I see some odd things in your classes that might cause your problems.
In entity framework a proper one-to-many relation is modelled as follows:
public class Parent
{
public int Id {get;set;}
public string name{get;set;}
// Every parent has zero or more Children
public virtual ICollection<Child> Children {get;set;}
}
public class Child
{
public int id {get;set;}
public bool Imported{get;set;}
public DateTime? TimeSpan {get;set;}
// every Child belongs to exactly one Parent using foreign key
public int ParentId {get; set;}
public Parent Parent {get; set;}
}
The collection of Children in your Parent can't be a List. What would ChildList[3] mean?
Besides, this Collection should be virtual (See SO: Understanding code first virtual properties)
You wrote:
Basically, I am trying to include all the parents children, but order
them by id descending and then check if the first one (eg newest one)
has imported == false and timeSpan == null, and only include the
parent rows that have a child that meets this condition.
A bit difficult to understand, but it seems that you have a sequence of Parents, and you want only those Parents and their children, where the Child with the highest ChildId is not Imported and has a null TimeSpan.
var result = dbContext.Parents
.Select(parent => new
{
// Take all Parent properties you need in your end result, for example
Id = parent.Id,
Name = parent.Name,
Children = parent.Children
.OrderByDescending(child => child.Id),
})
.Select(parent => new
{
Id = parent.Id,
Name = parent.Name,
Children = parent.Childrent,
NewestChild = parent.Children.FirstOrDefault(),
})
// keep only the parents you want to keep:
.Where(parent => parent.NewestChild != null
&& !parent.NewestChild.Imported
&& parent.NewestChild.TimeSpan == null));

NHibernate save children element FK going NULL

So, I have a list of children on my parent object, and I want to persist them on my SQL Server. When I run the application for the first time, all the children get their FK correctly, but when I run it again and no new parent is added, the new child(of an existing parent) doesn't get it's parent FK, just NULL. How can I map the parent FK on my child mapping for those situations?
I've tried the Inverse() method, but as I need the parent key to be generated all children gets null anyway. I need something like, if the parent is new, then the parent will update it's children FK, but when only the child is new I would need it to do the Inverse() method, is it possible?
Some more info:
Every time I call the ParentPersist method, and it cascades as needed. I've added the AddChild() method to set the ParentId when a new child is added to the list, it's working as I debugged it, so the child is setting it's ParentId correctly.
The objects are like the following:
public class Parent
{
public virtual int Id { get; set; }
...
public virtual IList<Child> Children{ get; set; }
public virtual void AddChild(Child ch)
{
ch.IdParent = this.Id;
Children.Add(ch);
}
}
public class Child
{
public virtual int Id { get; set; }
...
public virtual int IdParent {get;set;}
}
And my mapping:
public class ParentMapping : ClassMap<Parent>
{
public ParentMapping ()
{
Id(cso => cso.Id).GeneratedBy.Identity();
...
HasMany(cso => cso.Children).KeyColumn("IdParent").Cascade.SaveUpdate().Not.LazyLoad();
}
}
public class ChildMapping : ClassMap<Child>
{
public ChildMapping ()
{
Id(cso => cso.Id).GeneratedBy.Identity();
...
}
}
Your logic (e.g. Add() method in Parent, Inverse() mapping) was OK. You were almost there. There is only one BUT...
In general, the proper (if not only correct) solution is to use objects to express realtion and not just the ValueType/int values. That's why we call it ORM - Object-relational mapping
Object in C# should look like this:
public class Parent
{
...
// correct mapping of the children
public virtual IList<Child> Children{ get; set; }
// this method uses the below updated Child version
public virtual void AddChild(Child ch)
{
// this is replaced
// ch.IdParent = this.Id;
// with this essential assignment
ch.Parent = this;
Children.Add(ch);
}
}
public class Child
{
...
// instead of this
// public virtual int IdParent {get;set;}
// we need the reference expressed as object
public virtual Parent Parent { get; set; }
}
So, now, once we have objects in place, we can adjust the mapping like this:
// parent
public ParentMapping ()
{
...
HasMany(cso => cso.Children)
.KeyColumn("IdParent")
.Inverse() // this is essential for optimized SQL Statements
.Cascade.SaveUpdate() // All delete orphan would be better
.Not.LazyLoad();
}
...
// Child
public ChildMapping ()
{
...
References(x => x.Parent, "IdParent"); // it is a to use Inverse()
}
With this Business Domain Model and the mapping (Inverse(), assigning bothe relation ends in Add() method...), NHibernat will have enough information to always (insert, update) issue proper SQL statements
NOTE: One could ask why to map Parent Parent { get; set; } and not just the int IdParent { get; set; }... In fact, if we would have existing Parent (with NOT transient ID, i.e. > 0) - there won't be any difference. The trick/problems would appear on a new Parent insertion. Almost always, assignement of the children comes before the Parent is persiseted (flushed), and its ID is recieved from DB (sql server identity). And that could/would cause the child.IdParent == 0 ...
We should remember, that in general - ORM is about objects, i.e. relation is represented by Reference types.

Joining two datatables as parent-child in linq

I need to build a typed list of parent-child objects that are read from two different Excel sources: One describes parent object, another describes child objects. The hierarchy is only 2 layers ever.
Reading into excel is not the issue, as it is read into 2 untyped datatables, but joining the information is.
The structure is very plain:
Parent has an ID and some text fields
Children have a parentID (so its 1-m) and some text fields
The objects that these are to be populated into looks like this:
public class ParkingSites
{
public List<ParkingLot> Lots { get; set; }
public ParkingSites(List<ParkingLot> arg)
{
Lots = arg;
}
}
public class ParkingLot
{
public List<Bay> Bays{ get; set; }
public int Id { get; set; }
public List<string> ParkingLotDetails { get; set; }
public ParkingLot()
{
}
}
public class Bay
{
public List<string> BayDetails { get; set; }
public int ParentId { get; set; }
public Bay()
{
}
}
The excel sources have a fixed column order with the parent sheet's first column being the parentId, and the first column on the child sheet also being the parentId.
EDIT: After playing around a bit, I just made both parent and child classes typed, as the initial reason for leaving them mostly untyped lead to more problems than it prevented. This is part of a larger project where the untypedness is a better solution for our problem on the other classes with data that is not hierarchial.
You can simply group the list of children by the parent id, and then iterate over the parents and add each child that belongs to it.
For example, you could use ToLookup:
// assuming you have all Bay instances in a collection called bays
var baymap = bays.ToLookup(b => b.ParentId);
// and all ParkingLot instances in a collection called lots
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.Id]);
or, using the first element in the details lists:
var baymap = bays.ToLookup(b => b.BayDetails[0]);
foreach(var lot in lots)
lot.Bays.AddRange(baymap[lot.ParkingLotDetails[0]]);
or, using Where without a lookup (probably slower, depends on your data):
foreach(var lot in lots)
lot.Bays.AddRange(bays.Where(b => b.ParentId == lot.Id));

Data Mapping an Adjacency List Model with AutoMapper [duplicate]

This question already has answers here:
How to assign parent reference to a property in a child with AutoMapper
(2 answers)
Closed 10 years ago.
So have have an model object TreeNode:
Public Class TreeNode{
Public int NodeId {get;set;}
Public String Name {get;set;}
Public int ParentId {get;set;}
Public TreeNode Parent {get;set;}
Public List<TreeNode> Children {get;set;}
}
This structure is powered by a databases using an Adjacency List Pattern. I'm using a WCF service with AutoMapper to populate my Model classes.
I want to do something like this:
public static void ConfigureMappings()
{
Mapper.CreateMap<TreeNodeDto, Taxonomy>()
.AfterMap((s, d) =>
{
//WCF service calls to get parent and children
d.Children = Mapper.Map<TreeNodeDto[], TreeNode[]>(client.GetTreeChildren(s)).ToList();
d.Parent = Mapper.Map<TreeNodeDto, TreeNode>(client.GetTreeParent(s));
});
}
But obviously this causes an infinite loop (it does work if I only map children tho). Is there any way to populate my tree structure using AutoMapper?
I found this partial solution. At first I though it was what I was looking for but after further inspection it only works if you start at the top of the tree. It doesn't populate the Parent nodes if you start in the middle.
How to assign parent reference to a property in a child with AutoMapper
public static void ConfigureMappings()
{
Mapper.CreateMap<TreeNodeDto, Taxonomy>()
.AfterMap((s, d) =>
{
//WCF service calls to get parent and children
d.Children = Mapper.Map<TreeNodeDto[], TreeNode[]>(client.GetTreeChildren(s)).ToList();
foreach( var child in d.Children)
{
child.Parent = d;
}
}

Mapping child object inside parent object

I'm having a bit of an issue. I don't quite know how to handle the situation so I'll just explain a simplified scenario and hopefully you can help me.
I'm trying to map a parent database object to a parent bll object. In this parent database object, there is a foreign key to the ID of the child, and in my parent bll object I use the child bll object (containing more than just an ID).
So here are my bll objects:
public class Parent
{
public int ID { get; set; }
public Child Child { get; set; }
}
public class Child
{
public int ID { get; set; }
public string FirstName { get; set; }
}
And here is my mapper class/method:
public class ParentMapper
{
public Parent MapFromSource(ParentDatabaseObject parentDO)
{
Parent parent = new Parent();
parent.ID = parentDO.ID;
parent.Child = ???;
return parent;
}
}
I don't think it's very important what the ParentDatabaseObject looks like in this case, I'd just like to know how I should map the parent.Child object.
I have considered the following:
parent.Child = new Child();
parent.Child.ID = doParent.Child.Id;
parent.Child.FirstName = doParent.Child.FirstName;
Which doesn't feel right, 'cause I kind of have the urge to put this in my ChildMapper, which leads me to my second way of implementing this (assuming I have a seperate child mapper and have an instance of it called childMapper):
parent.Child = childMapper.MapFromSource(parentDO.Child);
But I kind of have the feeling that using this way of mapping is making my code a bit tightly coupled, because I'd be using my ChildMapper in my ParentMapper.
So I guess my question is: how should I implement this kind of mapping. Is this last method correct or is there something even better? I'm already discarding the first thing I tried.
Thanks for your help!
(I did research before posting this question and this was the closest I could find:
Data Mapper for Child Objects , but I wasn't really content with the only answer in there)
Shouldn't it be better -
parent.Child = childMapper.MapFromSource(parentDO.FoeignKeyToTheChild);
I think you should have methods to get object by Id.
EDIT : If your mapper doesn't DataAccess code, then you have to map the child within your Repository. As your Repository already have DataObjects ready, you can do it the following way -
ParentMapper:
public class ParentMapper
{
public Parent MapFromSource(ParentDo parentDo)
{
Parent parent = new Parent();
parent.Id = parentDo.Id;
return parent;
}
}
ChildMapper:
public class ChildMapper
{
public Child MapFromSource(ChildDo childDo)
{
Child child = new Child();
child.Id = childDo.Id;
child.FirstName = childDo.FirstName;
return child;
}
}
Repository:
public class Repository
{
//you already have parentDo
//you already have childDo
public Parent GetParent()
{
Parent parent = parentMapper.MapFromSource(parentDo);
parent.Child = childMapper.MapFromSource(childDo);
return parent;
}
public Child GetChild()
{
Child child = childMapper.MapFromSource(childDo);
return child;
}
}
Otherwise, your Mapper must have access to DataAccess code.

Categories