Been looking for a solution for this but haven\'t been able to find one so far.
I\'m fairly sure its possible with one linq call but having trouble working it out.
If you're using Entity Framework and have Navigation Properties, you could do the following. It's not clear from the question whether this is the case though.
var query = db.YourTable
.Where(x => x.Parent != null && x.Parent.ValidFlag == 1)
.GroupBy(x => x.ParentId)
.Select(g => new { ParentId = g.Key, Children = g.ToList() })
.ToList();
for your sample data, this will work :
var validData = from d in data
where (!d.ParentID.HasValue && d.IsValid) //select all valid parents
|| (d.ParentID.HasValue && data.Where(p => !p.ParentID.HasValue && p.IsValid).Select(p => p.ID).Contains(d.ParentID.Value)) //select children
select d;
but it won't work if there are multi-level hierarchies in your data, and you want to select the sub-children too.
another thing, i'm not sure if the above will work on linq-to-sql or another linq provider, but it does work for in-memory data.
I was going to use a GroupJoin, but this should satisfy your requirement.
var query = dataContext.YourTable.Where(x => x.ValidFlag == 1 &&
(x.ParentId == null ||
dataContext.YourTable.Where( y => y.ParentId == x.Id)
.First().ValidFlag == 1))
.ToList();
.
This should do it. Create a generic list of the type of your object that contains that data structure. Then use the .Where extension which returns an IEnumerable of the same type.
List<YourObject> list = new List<YourObject>();
IEnumerable<YourbObject> validItems = list.Where(x=>x.ValidFlag=1);
This should do the trick, (edit: see below for version that doesn't use Distinct.)
(from parents in collection
from all in collection
where
parents.ValidFlag == 1 &&
parents.ParentId == null &&
all.ValidFlag == 1 &&
(all.ParentId == null || all.ParentId == parents.Id)
select all).Distinct();
The above code should hopefully generate something quite similar to what it itself looks like in SQL, maybe with the exception of the distinct which might cause it to return more data that is actually needed to be filtered on the client. Something that might become an issue if there's a lot data, chiefly if there's a lot of parents because it will return duplicates of those)
Here is the query reworked without the distinct call
from parents in collection // parents is only used to decide which children to get
from all in collection // this is where we will actually grab our data from
where
parents.ValidFlag == 1 && // only include parents that are valid
parents.ParentId == null && // and that are parents
all.ValidFlag == 1 && // only include entries that are valid
(
(all.ParentId == null && all.Id == parents.Id) || // If entry is a parent, match with itself to limit returns to 1
all.ParentId == parents.Id // otherwise, parentid should match one of the valid parents.
)
select all
Thinking about this the "wrong" way around, the SQL you want is:
SELECT * FROM MyTable
WHERE IsValid = 1 AND
(ParentID IS NULL -- Parents
OR ParentID IN (SELECT ID FROM MyTable WHERE IsValid = 1 AND ParentID IS NULL))
-- Children
so the LINQ you want is:
var result = from d in MyTable
where d.ValidFlag == 1
&& (d.ParentId == null
|| (from p in MyTable where p.ValidFlag == 1 && p.ParentId == null
&& p.Id == d.ParentId select p.Id).Any())
select d;
(Not quite the same SQL, but effectively so.)