简体   繁体   中英

Get parent object based on child of child criteria and include

Ok so here is a quick version of my tables.

public class Line
{
  // Bunch Of properties
  // ...
  public List<Tool> Tools {get;set;}
}

public class Tool
{
  // Bunch Of properties
  // ...
  public List<Task> Tasks {get;set;}
}

public class Task
{
  // Bunch Of properties
  // ...
  public List<SchedItem> SchedItems {get;set;}
}

public class SchedItem
{
  public DateTime SchedDate {get;set;}
}

What I want is a query that will return all the Lines including child collections and their child collections etc based on whether SchedItem.SchedDate falls within a date range.

I tried the following in LinqPad.

var line = (from p in Lines.Include(x => x.Tools.Select(m => m.Tasks.Select(s => s.SchedItems)))
                        from t in p.Tools
                        from m in t.Tasks
                        from s in m.SchedItems
                        where s.SchedDate >= StartDateQuery &&
                              s.SchedDate <= EndDateQuery 
                        select p);

This seems wrong to me as the returned data is including all SchedItems not just the ones based on the query.

UPDATE 1: Yeah I think I have my understanding wrong. I have set the following on my context configuration

db.Configuration.LazyLoadingEnabled = false;
db.Configuration.ProxyCreationEnabled = false;

When I run the code again I get a list of 33 Line items with no child items. There should only be 1 'Line' item with many child items.

I changed my query to this and I now get the child items but the Line count is still 33 which is wrong.

var line = (from p in Lines
                        from t in p.Tools
                        from m in t.Tasks
                        from s in m.SchedItems
                        where s.SchedDate >= StartDateQuery &&
                              s.SchedDate <= EndDateQuery 
                        select p).Include(x => x.Tools.Select(m => m.Tasks.Select(s => s.SchedItems)));

Try this

var lines = Lines.Where(i => 
    i.Tools.Any(t => 
        t.Tasks.Any(y => 
            y.SchedItems.Any(u => 
                u.SchedDate >= StartDateQuery  && 
                s.SchedDate <= EndDateQuery 
            ) 
        )
    )
);

lines.ForEach(i => new 
{
    i.Tools = i.Tools.Where(t => 
                t.Tasks.Any(y => 
                    y.SchedItems.Any(u => 
                        u.SchedDate >= StartDateQuery  && 
                        s.SchedDate <= EndDateQuery 
                    ) 
                )
            );
    i.Tools.ForEach(t => new
    {
        t.Tasks = t.Tasks.Where(y => 
                        y.SchedItems.Any(u => 
                            u.SchedDate >= StartDateQuery  && 
                            s.SchedDate <= EndDateQuery 
                        ) 
                    );
    });
});

Your code make cartesian product because of couple from statements. I'll skip configuration of DbContext and assume that you have enabled lazy loading. Try this:

var line = from p in ls
           where p.Tools
                .Any(to=> to.Tasks
                    .Any(ta=> ta.SchedItems
                       .Any(s=> s.SchedDate >= StartDateQuery && s.SchedDate <= EndDateQuery)));
           select p;

Another way to accomplish your goal is to add parrent to: SchedItem, Task, Tool, find SchedItems that meet your requirements and navigate through parents to Line. For more information go here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM