How to load all the children of the specific parent? - c#

There are a lot of related questions in SO, but no one helped me.
I have an entity like this:
class TaskObject
{
public int Id { get; set; }
public string Title { get; set; }
public int? ParentId { get; set; }
public TaskObject Parent { get; set; }
public ICollection<TaskObject> Children { get; set; }
}
And I want to get all the elements of the specific branch as a list.
For example, I have a tree like this:
Task1
Task2
Task4
Task5
Task3
Task6
The result of my query would be Task2 Task4 Task5.
I wrote a function that makes it, but I think there is a better solution:
private void getSubtree(TaskObject parent, List<TaskObject> items)
{
items.Add(parent);
context.Entry(parent).Collection(t => t.Children).Load();
foreach (var child in parent.Children)
{
getSubtree(child, items);
}
}
Is there smth like context.Entry(parent).GetAllChildren() in EF Core?

No there is no any method like that (GetAllChildren()) in EntityFramework.
When you are working with tree, you can cache some paths to improve you searches and navigation.
If you are going to search some nodes then load their path until top of tree:
define a property for tree's node and save all nodes until top of tree. when you move a node you should update that path property too.

Related

How to merge linq queries?

I have a class which is self-referenced.
public class MyPerfectClass
{
public bool IsActive { get; set; }
public Guid? ParentId { get; set; }
public MyPerfectClass? Parent { get; set; }
private readonly List<MyPerfectClass> _children = new();
public IReadOnlyCollection<MyPerfectClass> Children => _children.AsReadOnly();
}
I need to get all 'MyPerfectClass' where ChildItem's IsActive is true. I can write two seperate queries but can't get parents & childrens based on childrens filtering. Any suggestion?
Is this what you are looking for?
List<MyPerfectClass> parentsList = new List<MyPerfectClass>();
var results = parentsList.Where(p => p.Children.Any(c => c.IsActive));
ALL, ANY and other queries can be used on nestled objects.
MyPerfectClass having an instance of MyPerfectClass to define parent seems a little off. The Parent object instance on the MyPerfectClass may not share the children defined on MyPerfectClass where parent is defined. May lead to confusion.

How to implement recursive self join entity framework?

I have Menu table in database which have self referencing foreign key i.e. ParentID. Below is my Menu class (DB First Approach)
public partial class Menu
{
public Menu()
{
this.Menu1 = new HashSet<Menu>();
this.Products = new HashSet<Product>();
}
public int MenuID { get; set; }
public string Name { get; set; }
public Nullable<int> ParentID { get; set; }
public virtual ICollection<Menu> Menu1 { get; set; }
public virtual Menu Menu2 { get; set; }
public virtual ICollection<Product> Products { get; set; }
}
I want to implement following things,
I want entire hierarchy using menu id e.g "if I pass 7 then result should be all the child and sub-child of menu id 7"
If I pass 7 then I want all parent and super parent of menu id 7.
I found several article on StackOverflow before posting this question but they were asking to implement Code First Approach. Here are the questions posted on Stackoverflow before Entity Framework Self Join, Most efficient method of self referencing tree using Entity Framework
I am not sure if you realize this but Menu1 is your parent Menu and Menu2 are your children menus. (I would recommending renaming both Menu1 and Menu2 properties to parent and children).
I believe all of the solutions you have linked have a solution you can use to solve your problem.
Code Sample:
void GetParents(Menu current) {
dbContext.Entry(current).Reference(m => m.Menu2).Load();
while (current.Menu2 != null) {
current = current.Menu2;
dbContext.Entry(current).Reference(m => m.Menu2).Load();
}
}
void GetChildren(Menu current) {
if (current == null) {
return;
} else {
dbContext.Entry(current).Collection(m => m.Menu1).Load();
foreach (var menu in m.Menu1) {
GetChildren(menu);
}
}
}
Something like this should help you get all parents and all children of a Menu instance called current. Note the efficiency is terrible. But that is a different problem. I don't optimize code until my performance tests indicate the bottlenecks in my application.
Fun quote: "Premature optimization is the root of all evil." - Donald Knuth

Query children and its children until there are no children simplified

Let's say we have an entity with the following properties:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public int? ParentId { get; set; }
}
A child is any Foo that has a non-null ParentId. There can be several levels of parent/child relationships, up to 5 at this point.
Is there a simple LINQ way or general methodology to begin with a Foo and get all of its children, and its children's children, and it's children's children's children, etc?
Currently, I'm looping through each child of the beginning parent, then looping again to get all of that child's children, etc. This is tedious and doesn't appear to be the correct way of accomplishing what I want.
I prefer to maintain a string property that represents a place in the hierarchy. For example, if the value was "/99/42" then you know the item belongs to parent 42 which belongs to 99. The reason this is nice is because then you can flatten out the whole collection of Foos and just query that field for those beginning with "/99" and you'll get the entire branch of the hierarchy. So your class would look like this:
public class Foo
{
public int Id { get; set; }
public string Name { get; set; }
public string Path { get; set; } //eg: /99/42
}
Hope that helps.
So if all you have is one Foo object then no, you cannot get all of it's descendants. At the very least, you'd need to have a sequence of all Foo objects as well in order to find the nodes that have that one object as a parent, since your Foo object doesn't already have a reference to it's children. If you do have that sequence, then it's not particularly hard.
You can use ToLookup on the sequence of all foos to create a lookup of ID to all children of that ID value:
var allFoos = new List<Foo>();
var childrenLookup = allFoos.ToLookup(foo => foo.ParentId);
To get a sequence of all descendants for a particular child you now have simple tree traversal:
public static IEnumerable<Foo> Descendants(Foo foo, ILookup<int?, Foo> childrenLookup)
{
var stack = new Stack<Foo>();
stack.Push(foo);
while (stack.Any())
{
var next = stack.Pop();
yield return next;
foreach (var child in childrenLookup[next.Id])
stack.Push(child);
}
}

Searching if all properties exist in a list of entities using Linq

I have the following entities:
[Table("Entities")]
public abstract class Entity {
public int EntityID { get; set; }
public string Description { get; set; }
public virtual ICollection<Tag> Tags { get; set; }
}
And the Tag Entity:
public class Tag {
public int TagID { get; set; }
public int EntityID { get; set; }
public string TagValue { get; set; }
}
As the above makes sort of clear Tags are not reused just stored as strings. This has made determining whether Entities share tags slightly more difficult (and slow).
I have a working search to return a list of entities in which the entities contain any of the tags:
List<string> searchTags = new List<String> {"test1", "test2"};
entities = (_entityRepository.Entities
.Where(o=>o.Tags.Any(f=>searchTags.Contains(f.TagValue)));
Now I also need to return a list of Entities which contain all of the tags in the list. As a non-property variable cannot be passed into a Contains method I cannot just reverse the order of the call with an all, but this is basically what I am trying to achieve:
entities = (_entityRepository.Entities
.Where(o=>o.Tags.All(f=>f.TagValue.Contains(searchTags)));
I think I have just hit the point where I need to correct the DB schema to re-use Tags which should provide a general performance benefit when filtering my entities on lists of Tags, but I still wanted to ask the following questions:
Am I over complicating this and is there a simple Linq statement which accomplishes this or,
Is this something for which I would should use predicate builder to set my criteria?
This can be done like this:
var query = _entityRepository.Entities.Select(e => e);
query = searchTags.Aggregate(query, (current, tag) => current.Where(e => e.Tags.Any(t => t.TagValue == tag)));
var result = query.ToList();

AutoMapper - mapping child collections in viewmodel

I have a viewmodel that needs to display a certain IEnumerable field as semicolon-separated textbox. At first I thought of using DefaultModelBinder to transform it, but I had trouble thinking how to achieve it in both directions (dto <-> viewmodel).
Nicknames is the field I'm trying to display as one textbox separated by semicolon.
public class Parent
{
public IEnumerable<Child> Children { get; set; }
}
public class Child
{
public IEnumerable<string> Nicknames { get; set; }
}
So I decided to try AutoMapper, I created two ViewModels:
public class ParentViewModel
{
public IEnumerable<ChildViewModel> Children { get; set; }
}
public class ChildViewModel
{
public string Nicknames { get; set; }
}
Then, I created mappings, like this for the children (omitted the other-way conversion for brevity)
Mapper.CreateMap<Child, ChildViewModel>().ForMember(
d => d.Nicknames, o => o.ResolveUsing<ListToStringConverter>().FromMember(s => s.Nicknames);
Then, for the parent, created a naive map (again, omitted the other-way)
Mapper.CreateMap<Parent, ParentViewModel>();
I truly expected the child mappings occur automatically, but they don't, I've already created too much "proper" code to solve a really simple problem which in any other simpler/older non-MVC environment, I'd be done with a long time ago :) How can I proceed and tell AutoMapper to transform the children without writing another "children member resolver".
Have I overthought this and there's a simpler way?
Thank you!
try
Mapper.CreateMap<Parent, ParentViewModel>();
Mapper.CreateMap<Child, ChildViewModel>();
var v = Mapper.Map<Parent, ParentViewModel>(parent);
Found this solution https://stackoverflow.com/a/7555977/1586498, that works for me:
Mapper.CreateMap<ParentDto, Parent>()
.ForMember(m => m.Children, o => o.Ignore()) // To avoid automapping attempt
.AfterMap((p,o) => { o.Children = ToISet<ChildDto, Child>(p.Children); });
The ToISet function is defined in the above link.
Simpler examples 'just work' in LinqPad - so more investigation is required.
A complete listing of a working program:
public class Child{ public string Name {get; set; }}
public class ChildDto{ public string NickName {get; set; }}
public class Parent{ public virtual IEnumerable<Child> Children {get; set; }}
public class ParentDto{ public IEnumerable<ChildDto> Kids {get; set; }}
private static void Main()
{
AutoMapper.Mapper.CreateMap<Parent, ParentDto>().ForMember(d=>d.Kids, opt=>opt.MapFrom(src=>src.Children));
AutoMapper.Mapper.CreateMap<Child, ChildDto>().ForMember(d=>d.NickName, opt=>opt.MapFrom(src=>src.Name));
var pList = new HashSet<Parent>{
new Parent{ Children = new HashSet<Child>{new Child{Name="1"}, new Child{Name="2"}}},
new Parent{ Children = new HashSet<Child>{new Child{Name="3"}, new Child{Name="4"}}},
};
var parentVm = AutoMapper.Mapper.Map<IEnumerable<Parent>, IEnumerable<ParentDto>>(pList);
parentVm.Dump();
}

Categories