Linq extension to recursively retrieve collection of items - linq

public class MyClass
{
public MyClass();
public long Id { get; set; }
public intScore { get; set; }
public MyClass[] subclasses { get; set; }
public string title { get; set; }
.....
}
The results returned from extrenal source are recursive, I am trying to retrieve the results from the collection using linq or any extension methods using recursion, any help appreciated.
ex; The collection i have is
Myclass results=XXXmethod(xxxx)// which gives me results.
subclasses is a list of type Myclass, so this list will have again some collection, and inturn that can collection agian nested levels
say the service returned 10 results or Myclass[10] Myclass[0] is having again Myclass[4] which might have 2 or 4, need to build a collection allitems
I am trying to do like this but some are missing
results.class.subclusters.subclasses (o => o.subclasses )
.SelectMany(x => x.subclasses ).ToList()
but which is not giving correct results.

LINQ itself does not support recursive method, but you can have the recursive method like below:
public class MyClass
{
public int Id { get; set; }
public MyClass[] SubClasses { get; set; }
//More properties
public IEnumerable<MyClass> GetRecursive()
{
yield return this;
if (SubClasses != null)
{
foreach (var item in SubClasses
.Where(s => s != null)
.SelectMany(x => x.GetRecursive()))
{
yield return item;
}
}
}
}
Then call:
myClass.GetRecursive()

Related

LINQ how to order on a referenced List

I have an object as defined like this
public class Person {
public Person() {
this.NewsItems = new List<NewsItem>();
}
[Key, DatabaseGenerated(DatabaseGeneratedOption.Identity)]
public int Id { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public int Age {get;set;}
public virtual IList<NewsItem> NewsItems { get; set; }
}
The NewsItem object has a property called DisplayOrder and it is of type int. I then have LINQ as following:
return _repo.GetAll<Person>().Where(p => p.age >60).ToList();
My objective is have a sorted List<Person> based on DisplayOrder.
I am going to use this List<Person> in controller. In this controller I do have access to Person.NewsItems. I need to have List<Person> to be sorted in order or DisplayOrder which is inside the referenced list NewsItems.
Basically I have 1:m relationship between Person and NewsItem.
You can implement a custom comparer that you can use with .OrderBy. This could be against the NewsItems property. So you can do something like:
var sortedPersons = persons
.Where(p => p.Age > 60)
.OrderBy(x => x.NewsItems, new NewsItemsComparer());
Where your NewsItemComparer could take the form of:
public class NewsItemsComparer : IComparer<IList<NewsItem>>
{
public int Compare(IList<NewsItem> x, IList<NewsItem> y)
{
if (<logic where xDisplayOrders takes precedence over yDisplay Orders>)
{
return 1;
}
if (<logic where yDisplayOrders takes precedence over xDisplay Orders>)
{
return -1;
}
else
return 0;
}
}
Here's a sample implementation in which Persons are sorted according to which NewsItems has the lowest DisplayOrder number: https://dotnetfiddle.net/MTtwHU

Can we use Linq to sort a class based on its ID without implementing IEnumerable

I am new to Linq and tried to learn its intricacies.
I have a normal class (not derived from anything) with a unique ID and I would like to use Linq to sort a list of that element of the class above.
class item
{
public int itemID { get; set; }
public string Description { get; set; }
}
class items
{
List<item> myItems = new List<item>();
public List<item> test()
{
List<item> t = myItems.Sort((x,y) => (x.itemID.CompareTo(y.itemID))).ToList<item>();
return t;
}
}
The error I got was "Operator '.' cannot be applied to operand of type 'void'". itemID is an int and I should be able to compare it with another int... What am I missing?
I think what you are looking for is OrderBy:
return myItems.OrderBy(x => x.itemID);
Sorting lists explicitly is rarely needed these days. Prefer a functional programming style.
The Sort() method you are using is not a LINQ extension method, it is a native List<T> method, which sorts the list in place. So there is no return value, and that is causing your problem. Try like this:
class item
{
public int itemID { get; set; }
public string Description { get; set; }
}
class items
{
List<item> myItems = new List<item>();
public List<item> test()
{
myItems.Sort((x, y) => (x.itemID.CompareTo(y.itemID)));
//return myItems.ToList<item>(); // unneeded transformation
return myItems;
}
}

Query that Groups Parent, then Child, when fetching from IQueryable<GrandChild>

Lets imagine that below is our data model. I want to query the Toys but have the results returned such that I can do the following psuedo code.
foreach (var parent in parents)
{
Console.WriteLine(parent.Name);
foreach (var child in parent.Children)
{
Console.WriteLine(child.Name);
foreach (var toy in child.Toys)
{
Console.WriteLine(toy.Name);
}
}
}
The data model:
public class Parent
{
public Guid ParentId { get; set; }
public String Name { get; set; }
public ICollection<Child> Children { get; set; }
}
public class Child
{
public Guid ChildId { get; set; }
public Guid ParentId { get; set; }
public Parent Parent { get; set; }
public String Name { get; set; }
public ICollection<Toy> Toys { get; set; }
}
public class Toy
{
public Guid ToyId { get; set; }
public Guid ChildId { get; set; }
public Child Child { get; set; }
public String Name { get; set; }
public bool IsCool { get; set; }
}
I've tried doing this with grouping but when I try to iterate a grouping all that I see is Key which doesn't have any properties on it so I can't get the Parent.Name or the Child.Name.
Thanks in advance.
You can do:
var query = from p in parents
from c in p.Children
from t in c.Toys
select new { Parent = p.Name, Child = c.Name, Toy = t.Name }
foreach (var a in query)
{
// write values.
}
In effect, this uses SelectMany.
Edit
After your comment, I think what you're looking for is not a grouping. If you've got a Parent object in fact its Children are already grouped inside of it, because each parent has its own collection. But maybe you're looking for a way to populate the collections? That would be done by
var query = from p in parents.Include(x => x.Children.Select(c => c.Toys));
With LazyLoadingEnabled set to be false, you will be able to do this by using one linq query to load all related objects.
Please note, this may cause some performance issue if you have great amount of related data in Children or Toys table.
context.ContextOptions.LazyLoadingEnabled = false;
var parents = context.Parents.Select(x=>x).ToList();
foreach(var parent in parents) {
Console.WriteLine(parent.Name);
foreach(var child in parent.Children) {
Console.WriteLine(child.Name);
foreach(var toy in child.Toys) {
Console.WriteLine(toy.Name);
}
}
}

Linq to entities: cast result to multiple listed complex types

I'm trying to create a single linq query which populates the following models in the CompanyViewModel constructor:
public class CompanyViewModel
{
public IList<CompanyUserViewModel> CompanyUsers { get; set; }
...
}
public class CompanyUserViewModel
{
public User User { get; set; }
public IList<UserOperationViewModel> UsersOperations { get; set; }
}
public class UserOperationViewModel
{
public Operation Operation { get; set; }
public int Permission { get; set; }
}
Currently I've got the following query:
return db.Users.Where(u => u.CompanyId == companyId)
.Select(u => new CompanyUserViewModel {
User = u,
UsersOperations = db.UsersInOperations
.Where(uo => uo.UserId == uo.UserId)
.Select(uo => new UserOperationViewModel{
Operation = uo.Operation,
Permission = uo.Permission
}).ToList()
}).ToList();
Which builds, but when the page runs I get
LINQ to Entities does not recognize the method 'System.Collections.Generic.List`1[WoodCo.Models.BusinessObject.UserOperationViewModel] ToList[UserOperationViewModel](System.Collections.Generic.IEnumerable`1[WoodCo.Models.BusinessObject.UserOperationViewModel])' method, and this method cannot be translated into a store expression.
What does one do?
Change your view model properties to use IEnumerable<T> instead of IList<T and remove the .ToList() calls.

Concrete implementation of generic base class and extension method

The end goal for this post is to override the ToString() method of a concrete implementation of a generic base class while still being able to search the implementation using Linq flattening technique. So if you read this and see a better way let me know. I'm using Telerik controls for Silverlight and they won't change their api to allow some of their control properties to be data-bound and instead rely on the ToString() method of whatever object they are bound to. yea, stupid.. Anyway here is what I've got.
RadTreeView control on my page. The FullPath property of each node in the treeview uses the ToString() method of each item its bound to (so this is what I need to override).
I had to create an "intermediary" class to enhance my base model class so it can be bound as a heirarchy in the tree view and then a concrete implementation of that generic class to override ToString(). Now the problem is I have a Linq extension that explodes because it cannot convert the concrete implementation back to the base generic class. I love generics but this is too much for me. Need help on solving the extension method issue.
Intermediary generic class:
public class HeirarchicalItem<T> : NotifyPropertyChangedBase, INotifyCollectionChanged where T : class
{
public event NotifyCollectionChangedEventHandler CollectionChanged;
public virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs ea)
{
if (CollectionChanged != null)
CollectionChanged(this, ea);
}
public HeirarchicalItem() { }
public HeirarchicalItem(T item)
{
Item = item;
}
public HeirarchicalItem(IEnumerable<T> collection)
{
CopyFrom(collection);
}
private T _item;
public T Item
{
get
{
return _item;
}
set
{
_item = value;
RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Item);
}
}
private ObservableCollection<HeirarchicalItem<T>> _children = new ObservableCollection<HeirarchicalItem<T>>();
public virtual ObservableCollection<HeirarchicalItem<T>> Children
{
get { return _children; }
set
{
_children = value;
RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
private void CopyFrom(IEnumerable<T> collection)
{
if ((collection != null))
{
using (IEnumerator<T> enumerator = collection.GetEnumerator())
{
while (enumerator.MoveNext())
{
HeirarchicalItem<T> newHeirarchicalItem = new HeirarchicalItem<T>(enumerator.Current);
Children.Add(newHeirarchicalItem);
RaisePropertyChanged<HeirarchicalItem<T>>(a => a.Children);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add));
}
}
}
}
}
Base model class: (data is shuttled to and from WCF Ria service using this class)
public class tbl_Path : EntityBase, IFullPath, IEquatable<tbl_Path>, IEqualityComparer<tbl_Path>
{
public tbl_Path();
public int GetHashCode(tbl_Path obj);
public override string ToString();
public DateTime CreateDate { get; set; }
public short Depth { get; set; }
public string FullPath { get; set; }
public bool IsAuthorized { get; set; }
public bool IsSelected { get; set; }
public string Name { get; set; }
public override IEnumerable<Operation> Operations { get; }
public int? ParentPathID { get; set; }
public int PathID { get; set; }
public Guid SecurityKey { get; set; }
public EntityCollection<tbl_Configuration> tbl_Configuration { get; set; }
public EntityCollection<tbl_Key> tbl_Key { get; set; }
public EntityCollection<tbl_SecurityACL> tbl_SecurityACL { get; set; }
public EntityCollection<tbl_SecurityInheriting> tbl_SecurityInheriting { get; set; }
public EntityCollection<tbl_Variable> tbl_Variable { get; set; }
}
Concrete Implementation so that I can override ToString():
public class HeirarchicalPath : HeirarchicalItem<tbl_Path>
{
public HeirarchicalPath()
{
}
public HeirarchicalPath(tbl_Path item)
: base(item)
{
}
public HeirarchicalPath(IEnumerable<tbl_Path> collection)
: base(collection)
{
}
public override string ToString()
{
return Item.Name; **// we override here so Telerik is happy**
}
}
And finally here is the Linq extension method that explodes during compile time because I introduced a concrete implementation of my generic base class.
public static IEnumerable<T> Traverse<T>(this IEnumerable<T> source, Func<T, IEnumerable<T>> fnRecurse)
{
foreach (T item in source)
{
yield return item;
IEnumerable<T> seqRecurse = fnRecurse(item);
if (seqRecurse != null)
{
foreach (T itemRecurse in Traverse(seqRecurse, fnRecurse))
{
yield return itemRecurse;
}
}
}
}
Actual code that is breaking: (x.Children is highlighted with the error)
Cannot implicitly convert type
'System.Collections.ObjectModel.ObservableCollection<HeirarchicalItem<tbl_Path>>' to
'System.Collections.Generic.IEnumerable<HeirarchicalPath>'. An explicit conversion
exists (are you missing a cast?)
HeirarchicalPath currentItem = this.Paths.Traverse(x => x.Children).Where(x => x.Item.FullPath == "$/MyFolder/Hello").FirstOrDefault();
Figured it out. Been working on this all day and minutes after posting the question I resolve it as always.
Just needed to add this bit to my concrete implementation and no more compiler errors.
private ObservableCollection<HeirarchicalPath> _children = new ObservableCollection<HeirarchicalPath>();
public new ObservableCollection<HeirarchicalPath> Children
{
get
{
return _children;
}
set
{
if (value == null)
return;
_children = value;
RaisePropertyChanged<HeirarchicalPath>(a => a.Children);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}

Resources