Linq selecting parent based on child criteria - linq

The big picture:
I'm working on a search form where the user can choose one or more criteria to filter the search results. One of the criteria is related to a child relationship.
I'm trying to create an extention method to Iqueryable<Parent> so I can use as part of my "chaining".
The method signature (as of now) is:
public static IQueryable<Parent> ContainsChild(this IQueryable<Parent> qry, int[] childrenIDs)
The parent table and a child table:
Parent
ParentID
Name
Description
Child
ParentID (FK)
AnotherID (from a lookup table)
Selection criteria:
int[] ids = new int[3] {1,2,3};
Usage would be something like this:
var parents = repository.All() //returns Iqueryable<Parent>
public IQueryable<Parent> Search(Search seach){
if (search.Criteria1 != null){
parents = parents.FilterByFirstCriteria(search.Criteria1);
}
if (search.ChildrenIDs != null){ //ChildrenIDs is an int[] with values 1,2,3
parents = parents.ContainsChild(search.ChildrenIDs)
}
}
What I'm trying to figure out is how to create the ContainsChild method that returns an IQueryable<Parent> where the parents have at least one child with the AnotherID in the ids array.
(I'm trying to use EF4 to accomplish this)
Any help fully appreciated.

Perhaps this:
public static IQueryable<Parent> ContainsChild(this IQueryable<Parent> qry,
int[] childrenIDs)
{
return qry.Where(p => p.Children.Any(c => childrenIDs.Contains(c.AnotherID)));
}
Edit
Just for fun another way which should give the same result:
public static IQueryable<Parent> ContainsChild(this IQueryable<Parent> qry,
int[] childrenIDs)
{
return qry.Where(p => p.Children.Select(c => c.AnotherID)
.Intersect(childrenIDs).Any());
}
The generated SQL for the first version looks more friendly though, so I'd probably prefer the first version.

Related

Build tree structure from the records

I query database for records in structure as follows
ID | Term | ParentID
In C# code I have following class
public class Tree
{
public string Id { get; set; }
public string Term { get; set; }
public string ParentId { get; set; }
public int Level { get; set; }
public IList<Tree> ChildItems { get; set; }
}
Query returns 5 000 000 records.
I need to build tree of Tree items and populate it.
First at all, I select all items where ParentID is null, and then for every element search parent (if parent doesn't exist I build parent of the parent and so on) and build tree using recursion.
I'm not happy with my algorithm because It takes more than 5 minutes.
Please, let me some advice how to do that, what to use and so on.
This is how the code is now implemented:
private string Handle2(List<Tree> originalTree)
{
IList<Tree> newTree = new List<TreeTest.Models.Tree>();
IList<Tree> treeWithoutParents = originalTree.Where(x => String.IsNullOrEmpty(x.ParentID)).OrderBy(x => x.Term).ToList();
foreach(Tree item in treeWithoutParents)
{
Tree newItem = new Tree { Id = item.ID, Term = item.Term, ParentId = item.ParentID, Level = 0 };
newTree.Add(newItem);
InsertChilds(newItem, originalTree, 0);
}
return "output";
}
private void InsertChilds(Tree item, List<Tree> origTree, int level)
{
++level;
IList<Tree> childItems = origTree.Where(x => x.ParentID == item.Id).ToList();
origTree.RemoveAll(x => x.ParentID == item.Id);
foreach (Tree i in childItems)
{
origTree.Remove(i);
}
foreach (Tree tItem in childItems)
{
if (item.ChildTree == null)
{
item.ChildTree = new List<TreeTest.Models.Tree>();
}
Tree itemToAdd = new Tree { Id = tItem.ID, Term = tItem.Term, ParentId = tItem.ParentID, Level = level };
this.InsertChilds(itemToAdd, origTree, level);
item.ChildTree.Add(itemToAdd);
}
}
Try using a map (C# Dictionary) of ID (string, although I'm curious why that isn't int) to node (Tree object) to store your tree nodes.
This would allow you to get the node corresponding to an ID with expected O(1) complexity, rather than your current O(n) complexity.
Beyond that, I suggest you rethink your approach a bit - try to write code which involves you only going through the input data once, just use a single Dictionary - if the parent doesn't exist yet, you could just create a filler-item for the parent, which has its members populated only when you get to that item.
I would use a dictionary (hash table) to make this faster. Here is my algorithm in pseudocode:
- create a dictionary mapping ID to IList<Tree> // mapping a node to its children
- create Queue<string,string> of IDs //item (3,5) in the queue corresponds to a node with ID=3 that has a parent with ID=5
- initialize the queue with all the codes with no parent
- List<Tree> withoutParent = dictionary[null]
- for each item in withoutParent:
- add (item.Id, null) to the queue
- while queue is not empty:
- (id,pid) = delete an item from the queue
- make a new node t
- t.Id = id
- t.parentId = pid
- t.ChildItems = dictionary[id]
- for each child in t.ChildItems:
- add (child.Id, id) to the queue
is the column ID a unique identifier. If it is then you can try the following. Instead of using a List, use a Set or a hashmap. This is because if a parent has too many child, lookup in a list can slow down your operations. If you use a Set, you can do a quick lookup and you can also do a quick addition of your elements.
Also, can you check how much time an order by clause will take . This might really help you speed up your process. If ID is a clustered index, you will get a fast sort by(as the data is already sorted) , else your query will still use the same index
When a parent does not exist , you are creating a parent of a parent. I would try to avoid that. What you can do is in case a child's parent does not exist in the tree, add it to a separate list. After you have gone through the original list , make a second pass to find orphaned elements. The advantage is that you do not need to resize your tree every time you create a parent of parent and then find out that the parent was just at the end of the list

count based on lookup in LINQ

I have a table (or entity) named Cases. There is another table CaseStatus_Lookup and the primary key of this table is a foreign key in the Cases table.
What I want to do is: For every status type I want the number of count of cases. For e.g. if status = in progress , I want to know how many cases are in that status.
one other thing: I also want to filter the Cases based on UserID.
I tried several ways in LINQ but could not get vary far. I was wondering if someone could help.
try Linq .GroupBy
am assuming your entity structure
suppose your Case Entity is like
public class Case
{
public int Id{get;set;}
public int CaseStatusId{get;set;}
public int UserId{get;set;}
//navigational fields
public virtual CaseStatus CaseStatus {get;set;}
}
and suppose your CaseStatus entity is like:
public class CaseStatus
{
public int Id{get;set;}
public string Name{get;set;}
//navigational fields..
public virtual ICollection<Case> Cases{get;set;}
}
then you can do this:
using (myDbContext db = new myDbContext())
{
var query = db.Cases.GroupBy(case => case.CaseStatus.Name)
.Select(group =>
new {
Name = group.Key,
Cases= group.OrderBy(x => x.Id),
Count= group.Count()
}
).ToList();
//query will give you count of cases grouped by CaseStatus.
}
similarly you can further filter your result based on userId.
Start to explore about Linq .GroupBy
You need a function that returns the sum and takes the status as parameter :- something like below.
MyCaseStatusEnum caseStatus; //Pass your required status
int caseCount = myCases
.Where(r => r.Status == caseStatus)
.GroupBy(p => p.Status)
.Select(q => q.Count()).FirstOrDefault<int>();

Filter a List<T> based on another list that each instance of T contains

So I have a collection of objects in one list, but each object in that list contains another list.
Consider the following:
class Parent
{
public Parent(string parentName)
{
this.ParentName = parentName;
}
public string ParentName { get; set; }
public List<Child> Children { get; set; }
}
class Child
{
public Child(string name)
{
this.ChildName = name;
}
public string ChildName { get; set; }
}
By the nature of the application, all Parent objects in the list of parents are unique. Multiple parents can contain the same child, and I need to get the parents that contain child x.
So, say the child with ChildName of "child1" belongs to both parents with ParentName of "parent1" and "parent5". If there are 100 parents in the collection, I want to get only the ones that have the Child with ChildName of "child1"
I would prefer to do this with a lambda expression but I'm not sure where to start as I don't really have to much experience using them. Is it possible, and if so, what is the correct approach?
If the Child class has defined an equality operation by implementing IEquatable<Child>, you can do this easily by using a lambda, the Enumerable.Where method of LINQ and the List.Contains method:
var parents = new List<Parent> { ... }; // fully populated list of parents
var child = null; // the child you are looking for goes here
var filtered = parents.Where(p => p.Children.Contains(child));
You can now iterate over filtered and perform your business logic.
If the Child class does not have an equality operation explicitly defined (which means that it will use reference equality rules instead of checking for identical ChildName), then you would need to include the "what passes for equal" check into the lambda yourself:
var filtered = parents.Where(p => p.Children.Any(c => c.ChildName == "child1"));
Note: There are of course many other ways to do the above, including the possibly easier to read
parents.Where(p => p.Children.Count(c => c.ChildName == "child1") > 0);
However, this is not as efficient as the Any version even though it will produce the same results.
In both cases, the lambdas very much "read like" what they are intended to do:
I want those parents where the Children collection contains this item
I want those parents where at least one of the Children has ChildName == "child1"
You can do it like this:
var result = parents.Where(p => p.Children.Any(c => c.ChildName == "child1"));
This would do it
IEnumerable<Parent> parentsWithChild1 = parents.Where(p => p.Children.Any(c => c.ChildName == "child1"));

Using LINQ query on Dictionary and List

I have a Dictionary<int, int> idsAndTypes = new Dictionary<int, int>(); and i have a
List<Product> products = new List<Product>()
as list of products , the product class is as below
class Product
{
public int Id {get;set;}
public int Type{get;set;}
}
the dictionary idsAndTypes contains id's and types , now i want to use a linq query on the list to update the type of products based on id's in the dictionary....
i know the other way can be like following :
foreach (int item in idsAndTypes.Keys)
{
Product.Where(product => product.Id == item).
Select(product => product).ToList()[0].
Type = idsAndTypes[item];
}
but i want to do it with a linq query to avoid the foreach loop, is there a way to do it ?
Please suggest...
Well, LINQ is really for querying not for updating.
There may be a way of doing it without the loop, but I think the loop is the cleanest way. However, I wouldn't use that loop. You've got something which is very quick to look up, and you're just iterating through it... but then doing a lookup (effectively) on a slow data structure in terms of lookup. I'd do this:
foreach (Product p in products)
{
int type;
if (idsAndTypes.TryGetValue(product.Id, out type))
{
p.LinkedProductType = type;
}
}
One difference here - that will update all the products in the list with values in the dictionary; your current code will only do the first product in the list with the given ID. I hope that isn't a problem.
Your sample code is quite confusing. But I think what you want is:
products = products.Select(p =>
{
p.LinkedProductType = idAndTypes[p.ID];
return p;
}
);
While this should achieve the goal, I would considered it an abuse of LINQ.

linq and object initialisation

If I have something like:
var query = from children in _data.Children
where children.ChildId == childId
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
};
return query.FirstOrDefault();
Where I want the resultant object to be my custom model.
Can I handle the custom model instantiation in a different method, which could be reused if I had multiple linq queries that all generated a custom child model?
For example,
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild([param ??]);
return query.FirstOrDefault();
This may well be impossible, I don't know, but what would the method signature be like if it is possible?
I'm only thinking reuse for when multiple linq queries contain duplicate object initialisation code.
Thanks
It really depends on what version of LINQ you're using. If you're using LINQ to SQL, I don't believe you can call arbitrary methods in the query. The query translator wouldn't know what to do with the method call
If you're using LINQ to Objects, you're absolutely fine to do it, like this:
var query = from children in _data.Children
where children.ChildId == childId
select CreateMyCustomChild(children)
return query.FirstOrDefault();
// Elsewhere
public CustomModel.MyChild CreateMyCustomChild(OtherChild child)
{
return new CustomModel.MyChild
{
ChildId = child.ChildId,
Name = child.ChildName
};
}
(Side note: I'd call the range variable in the query "child" rather than "children" as at any one time it only represents a single child.)
If you wanted you could write "select 1" or in your case "CreateMyCustomChild(children)" since "children" is containing all your info. In your case you aren't adding a lot of info to "children", so why not "select children"?
In other words, just try it out. The return type of your value will determine over which type your LINQ enumerates.
Suppose you had a method that did the transform for you.
public static class Conversions
{
public static CustomModel.MyChild ToCustomModel(this DataModel.MyChild source)
{
return new CustomModel.MyChild()
{
ChildId = source.ChildId,
Name = source.ChildName
}
}
}
You can use such a method to do the conversion of a single item.
DataModel.MyChild myResult = getResult();
CustomModel.MyChild myConvertedResult = myResult.ToCustomModel()
Such a method can also be used in a Enumerable.Select method call.
IEnumerable<DataModel.MyChild> myQueriedResults = getResult();
IEnumerable<CustomModel.MyChild> myConvertedResults =
myQueryiedResults.Select(c => c.ToCustomModel());
While you can do with expressions, I don't think it is worth the hassle. Instead I suggest you define an extension method like:
IQueryable<CustomModel.MyChild> ToModel(this IQueryable<Child> childs)
{
return childs.Select( c=>
select new CustomModel.MyChild
{
ChildId = children.ChildId,
Name = children.ChildName
}
);
}
You can then call:
return _data.Children
.Where(c=>c.ChildId == childId)
.ToModel()
.FirstOrDefault();

Resources