2

I have a tree of objects whose size is potentially infinite.

The entity Category has other Category entities as children. In other words, a category can have subcategories: the depth is infinite.

Here below you can see a simplified version of my entity

public class ProductCategory : IEntity<ProductCategory>, IDeletable
{
    public Guid ProductCategoryId { get; private set; }

    public virtual ICollection<ProductCategory> Children { get; set; }

    public virtual ProductCategory Father { get; set; }
}

I used to have lazy loading until some hours ago and so I was able to easily get children of children of children.

Now I have disabled lazy loading because I had problems with it, and my query to get the children of a category is the follwing.

public IEnumerable<ProductCategory> GetAllChildrenOfCategory(Guid categoryId)
{
    var results = GetAllProductCategoriesQuery()
        .Where(elt => elt.FatherId.Equals(categoryId))
        .Include(elt => elt.Father)
        .Include(elt => elt.Children);

    return results.ToList().CloneEachElement();
}

I get all the children of the category. However, I don't have the children of the children of the children...

Now I have two questions:

  1. Is it possible to write a query so that you have the entire tree of categories?

  2. Alternatively, is it possible to configure entity framework so that it always give you the children of category once you extract it from the database, so that I neither have to explictly include the navigation properties?

  3. Other options...?

Wai Ha Lee
  • 8,598
  • 83
  • 57
  • 92
Errore Fatale
  • 978
  • 1
  • 9
  • 21
  • What database are you using? Are you able to use stored procedures? – markpsmith Oct 08 '15 at 11:38
  • "Is it possible to write a query so that you have the entire tree of categories?" and "...the depth is infinite." contradicts each other. – Robert Oct 08 '15 at 12:11
  • 1
    @Robert are you joking, right? "The depth is infinite" means that there are no limits in the depth of the tree. It's obvious that a tree can not be infinite: the concept of infinity in itself is abstract, in practice you can never reach "infinite" by definition. – Errore Fatale Oct 08 '15 at 12:20
  • @markpsmith sql server and yes, I can create stored proecedures. – Errore Fatale Oct 08 '15 at 12:21
  • What issues were you running into with lazy loading? – DDiVita Oct 08 '15 at 12:37
  • If you can't get it sorted using LINQ it might be worth having a look at HierarchyId and Common Table Expressions in SQLServer 2008. [http://blogs.msdn.com/b/simonince/archive/2008/10/17/hierarchies-with-hierarchyid-in-sql-2008.aspx](http://blogs.msdn.com/b/simonince/archive/2008/10/17/hierarchies-with-hierarchyid-in-sql-2008.aspx) – markpsmith Oct 08 '15 at 12:39
  • @DDiVita this: http://stackoverflow.com/questions/33011241/automapper-is-destroying-my-application-it-is-throwing-automapperexception-ever – Errore Fatale Oct 08 '15 at 12:52
  • Does this answer your question: https://stackoverflow.com/questions/2266473/how-to-do-recursive-load-with-entity-framework ? – Bart De Boeck May 21 '20 at 17:16

4 Answers4

0

As far as I recall, you can use strings instead of lambda expressions in order to include properties for eager loading. It is not very elegant but it will give you a workaround:

1) Find out which is the deepest level you have, then construct a corresponding include string such as "Father.Father.Father" and use it for your includes. I cannot guarantee that it works, but I give it a fair chance.

2) In code first, I don't think there is such an option. It might exist in the EDMX, but I am not at all an expert in this. I would propose you to use an extension method to do the job:

private static readonly queryStringForMaxDepth = WriteAMethodToFindIt();

public static IQueryable<ProductCategory> DeepProductCategories(this DbContext context){
     return context.ProductCategories.Include(queryStringForMaxDepth);
}

which you will be able to use as follows:

context.DeepProductCategories()
       .Where(...)

I hope this gives you some ideas.

Roland Buergi
  • 1,157
  • 9
  • 23
0

i have done this before by using view in MsSql. than i called the view directly like:

dbContext.Database.SqlQuery<ProductCategory>("select * from GetCategoryTree where RootCategoryId=1");

so this was giving me exactly what i was looking for. so it was also queryable. here is the one of the sources i used: sql recursive query parent child

and here is another sample

Someone else asked For Recursive Queries before in msdn forums

DLL_Whisperer
  • 815
  • 9
  • 22
0

As a possible solution to your issue I suggest you use Explicit Loading. This way you can load the subcategories from an specific category every time that you need it:

var cat=context.FirstOrDefault(c=>c.ProductCategoryId ==categoryId);

// Load the subcategories related to a given category
context.Entry(cat).Collection(p => p.Children).Load(); 
ocuenca
  • 38,548
  • 11
  • 89
  • 102
  • Does this load all children of children of children? Is it recursive? Or does this only load the children for the single object (cat) which is given as arguments? – Errore Fatale Oct 08 '15 at 12:29
  • That's going to load one level, but if you select an specific subcategory from the Children collection, you can repeat the same process to load the sub-sub-categories and so on with all levels. This way you can avoid to load all the data at the same time, it could be an expensive process if you have a lot of data in your DB. – ocuenca Oct 08 '15 at 12:34
0

The including...

FatherRepository.All().Including(x => x.Childs, x => x.Childs.Select(y => y.ChildChild));

Father class...

public class Father
{
    public int Id { get; set; }

    #region Navigations Properties
    public virtual List<Child> Childs { get; set; }
    #endregion
}

Child class...

public class Child
{
    public int Id { get; set; }
    public int ChildChildId { get; set; }
    public int FatherId { get; set; }

    #region Navigations Properties
    public virtual Father Father { get; set; }
    public virtual ChildChild ChildChild { get; set; }
    #endregion

}

ChildChild class...

public class ChildChild
{
    public int Id { get; set; }
}
Alexis Diel
  • 1,246
  • 1
  • 9
  • 15
  • Can you add a little explanation to your answer? What is AllFatherIncluding(...)? – Errore Fatale Oct 08 '15 at 12:59
  • Sure, `AllFatherIncluding` it's an alias to `FatherRepository.All().Including`, Where `FatherRepository.All()` returns an `IEnumerable` like yours `GetAllProductCategoriesQuery()` – Alexis Diel Oct 08 '15 at 13:07
  • Why three classes? I have only one class related with itself. The depth doesn't seem to be potentially infinite in your case... you only have three levels. – Errore Fatale Oct 08 '15 at 13:19
  • About your class, if you do a circular relation, yes, will be infinite, in any case, not only in mine. – Alexis Diel Oct 08 '15 at 13:55
  • Are you able to provide an example which works in the case of a circular relation? – Errore Fatale Oct 08 '15 at 13:58
  • My answer it's about your question number 1. Is it possible to write a query so that you have the entire tree of categories? `GetAllProductCategoriesQuery().Where(elt => elt.FatherId.Equals(categoryId)).Include(elt => elt.Father).Include(elt => elt.Children.Select(x => x.Children.Select(y => y.Children.Select(z => z.Children))));` – Alexis Diel Oct 08 '15 at 13:59
  • 1
    Loading a entire tree with circular reference, always will be infinite. – Alexis Diel Oct 08 '15 at 14:06