I am trying to get all menus and children that satisfy those conditions using linq:
Menus should have either a link or children count > 0 to be displayed at the
website
For the menus that has children : they should have at least one child menu that has a link
This is the Menu class:
public class Menu
{
public string Name { get; set; }
public string Link { get; set; }
public List<Menu> Children { get; set; }
public Menu()
{
Children = new List<Menu>();
}
}
Suppose we have this data structure:
List<Menu> root = new List<Menu>();
Menu parent_1 = new Menu() { Name = "Parent 1", Link = null };
Menu parent_2 = new Menu() { Name = "Parent 2", Link = null };
//children for parent 1
Menu p1_child_1 = new Menu() { Name = "p1_child_1", Link = null };
Menu p1_child_2 = new Menu() { Name = "p1_child_2", Link = null };
//sub children of p1_child_2
Menu p1_child_1_1 = new Menu() { Name = "p1_child_1_1", Link = "l1-1" };
Menu p1_child_1_2 = new Menu() { Name = "p1_child_1_2", Link = null };
p1_child_1.Children.AddRange(new List<Menu> { p1_child_1_1 , p1_child_1_2 });
parent_1.Children.AddRange(new List<Menu> { p1_child_1, p1_child_2 });
Menu p2_child_1 = new Menu() { Name = "p2_child_1", Link = null };
Menu p2_child_2 = new Menu() { Name = "p2_child_2", Link = "l2-2" };
Menu p2_child_1_1 = new Menu() { Name = "p2_child_1_1", Link = null };
Menu p2_child_1_2 = new Menu() { Name = "p2_child_1_2", Link = null };
p2_child_1.Children.AddRange(new List<Menu> { p2_child_1_1, p2_child_1_2 });
parent_2.Children.AddRange(new List<Menu> { p2_child_1, p2_child_2 });
root.Add(parent_1);
root.Add(parent_2);
Result: The filtered list returned based on the conditions requested will be:
parent_1
p1_child_1
p1_child_1_1
parent_2
p2_child_2
How to achieve that using Linq or alternative approach taking into consideration the menu could have up to many levels?
Trying the solution as proposed in the comments, i added the extension method
public static IEnumerable<TResult> SelectHierarchy<TResult>(this IEnumerable<TResult> source, Func<TResult, IEnumerable<TResult>> collectionSelector, Func<TResult, bool> predicate)
{
if (source == null)
{
yield break;
}
foreach (var item in source)
{
if (predicate(item))
{
yield return item;
}
var childResults = SelectHierarchy(collectionSelector(item), collectionSelector, predicate);
foreach (var childItem in childResults)
{
yield return childItem;
}
}
Then called the method:
var result = root.SelectHierarchy(n => n.Children, n => n.Children.Count > 0 || n.Link != null).ToList();
However this is not what i want, I expect two menus which carry the subMenus that satisfy my condition, but i am getting 6 menus which i guess are flattened.
Although, p2_child_1 was returned since children count > 0, however it shouldn't cause its menus has no links. ( I placed the predicate as above, since i don't have other option.
This works for me:
public static class Ex
{
public static List<Menu> CloneWhere(this List<Menu> source, Func<Menu, bool> predicate)
{
return
source
.Where(predicate)
.Select(x => new Menu()
{
Name = x.Name,
Link = x.Link,
Children = x.Children.CloneWhere(predicate)
})
.Where(predicate)
.ToList();
}
}
The sample data looks like this:
...then I can apply this:
var result = root.CloneWhere(m => m.Children.Any() || m.Link != null);
...and I get this result:
Assuming the depth is not so big to cause stack overflow, you can use a simple recursive method or recursive lambda as follows:
Func<List<Menu>, List<Menu>> filter = null;
filter = items =>
(from item in items
let children = filter(item.Children)
where item.Link != null || children.Any()
select new Menu { Name = item.Name, Link = item.Link, Children = children }
).ToList();
var filtered = filter(root);
The essential part is to process the children first (post order traversal) before filtering the parent.
Related
I have a razor view in ASP.NET MVC looping over an array of objects from my model and generating corresponding html controls.
My html elements are properly bound, except my drop down lists who can't seem to select the value provided to them by the model.
My view: (in the hereby case, I'm simply displaying a list of countries)
#for (var i = 0; i < Model.answers.Count(); i++)
{
<div class="form-group">
...
#switch (Model.answers[i].Statement.QuestionType)
{
...
case ExternalEnums.QuestionTypeEnum.country:
#Html.DropDownListFor(Model => Model.answers[i].Value,
new SelectList(Model.Pays, "Value", "Text"))
break;
}
...
</div>
}
My view controller, generating the country list items and retrieving the existing model entries:
public class HomeIndexViewModel
{
private QuestionsModelContainer dbContext;
private AdmcommonEntities admCommonContext;
...
public List<Answer> answers { get; private set; }
private IEnumerable<SelectListItem> _countries;
public IEnumerable<SelectListItem> Pays
{
get
{
if (_countries == null)
SetCountries();
return _countries;
}
}
public HomeIndexViewModel()
{
Init(-1, null);
}
public HomeIndexViewModel(int page, string _pageWideError = null)
{
Init(page, _pageWideError);
}
private void Init(int page, string _pageWideError = null)
{
dbContext = new QuestionsModelContainer();
PageNum = page;
pageWideError = _pageWideError;
answers = GetAnswers();
...
}
private void SetCountries()
{
using (admCommonContext = new AdmcommonEntities())
{
var localEntities = admCommonContext.Pays.ToList();
var localList = new List<SelectListItem>();
localList.Add(new SelectListItem());
foreach (var item in localEntities)
{
var newItemList = new SelectListItem();
newItemList.Text = item.Libelle;
newItemList.Value = item.Libelle;
localList.Add(newItemList);
}
_countries = localList;
}
}
public List<Statement> GetStatements()
{
var statements = dbContext.StatementSet.Where(w => w.Page == PageNum).OrderBy(w => w.Order).ToList();
return statements;
}
public List<Answer> GetAnswers()
{
var statements = GetStatements();
var ExistingAnswers = new List<Answer>();
if (AdminPermissionManager.IsUserAuthenticated()) //Loading existing entries.
ExistingAnswers = Answer.GetExistingAnswers(statements, dbContext);
var answers = new List<Answer>();
foreach (var item in statements)
{
var answer = ExistingAnswers.Where(w => w.StatementId == item.Id).FirstOrDefault();
if (answer == null)
{
answer = new Answer();
answer.StatementId = item.Id;
answer.Statement = item;
}
answers.Add(answer);
}
return answers;
}
}
My model class, simply containing the value I'm trying to display:
[MetadataType(typeof(AnswerMetaData))]
public partial class Answer
{
...
public static List<Answer> GetExistingAnswers(List<int> statementIds, QuestionsModelContainer dbContext)
{
List<Answer> ExistingAnswers;
var usercode = AdminPermissionManager.GetUserCode();
ExistingAnswers = dbContext.AnswerSet.Where(w => statementIds.Contains(w.StatementId) && w.ChildCode == usercode).ToList();
return ExistingAnswers;
}
public static List<Answer> GetExistingAnswers(List<Statement> statements, QuestionsModelContainer dbContext)
{
var statementIds = statements.Select(w => w.Id).ToList();
return GetExistingAnswers(statementIds, dbContext);
}
}
public class AnswerMetaData
{
[InternalValidation]
public string Value { get; set; }
private class InternalValidationAttribute : ValidationAttribute
{
...
}
}
I'm sure there's something very obvious that I'm missing, but can't figure out what exactly :/...
You're nearly there actually, this part in the View:
#Html.DropDownListFor(
Model => Model.answers[i].Value,
new SelectList(Model.Pays, "Value", "Text")
)
You create a new selectlist - each time, but you already have a IEnumerable<SelectListItem> created, so you don't have to recreate that list. The only thing you might be missing (most likely) is the "Selected" item option.
If you already have a value selected (and it isn't the first one) it will not be selected dropdown option - also because you pass the value of the selected option as the "ID" of the field (not the actual value) - DropDownListFor is kinda weird in that regard.
So you want to change your #Html.DropDownListFor to something like this:
#Html.DropDownListFor(
Model => Model.answers[i].Name,
Pays(Model.answers[i].Value)
)
When that being done you should change your property "Pays" in the ViewModel to a method that accepts a value (idk what you're using, but let's assume it's string) - to something along the lines of this:
public IEnumerable<SelectListItem> Pays(string selectedValue)
{
if (_countries == null) SetCountries();
var value = new List<SelectListItem>();
foreach(var item in _countries)
{
item.Selected = (item.Value == selectedValue);
value.Add(item);
}
return value;
}
This above is a bit pseudocoded since I'm typing this from memory, but it should get you into the correct direction. Also remember to check with the inspect element in the browser if the dropdown HTML element really has the correct name attribute.
I have to create an indented navigation menu using below data from a .csv file:
ID;MenuName;ParentID;isHidden;LinkURL1;Company;NULL;False;/company2;About Us;1;False;/company/aboutus3;Mission;1;False;/company/mission4;Team;2;False;/company/aboutus/team5;Client 2;10;False;/references/client26;Client 1;10;False;/references/client17;Client 4;10;True;/references/client48;Client 5;10;True;/references/client510;References;NULL;False;/references
Using this data I have to develop an application that will parse the file and present the content in a console as the example below:
. Company.... About Us....... Team.... Mission. References.... Client 1.... Client 2
Menu items should be indented (depending on the parent), hidden items (isHidden==true) shouldn't be presented and items should be ordered alphabetically. So far I tried:
using (StreamReader sr = new StreamReader(#"file.csv"))
{
// Read the stream to a string, and write the string to the console.
string [] lines = sr.ReadToEnd().Split(/*';', */'\n');
for (int i = 1; i < lines.Length; i++)
{
Console.WriteLine($"String no {i} is : {lines[i-1]}");
}
}
With this i'm getting the lines but I'm stuck after that. I'm new in coding so any help will be appreciated :)
heres some code that should help you get off.
Working sample:
https://dotnetfiddle.net/L37Gjr
It first parses the data to a seperate object. This then gets used to build a m-ary tree, or a hierachical structure of connected nodes. (a node has a reference to 0 or more children).
https://en.wikipedia.org/wiki/M-ary_tree
Then tree traversal (use google if you need to know more) is used to insert and print the output, There is still something wrong however. it now uses level order traversal to print, this however comes up with an error:
Found root:1 - Company
Found root:10 - References
-------------------
1 - Company
2 - About Us
3 - Mission
4 - Team
10 - References
6 - Client 1
5 - Client 2
As you can see, it prints 4 - Team on the wrong level. I'll leave it to you to fix it (because i ran out of time), and if not i hope i gave you plenty ideas to go off and research on your own.
// sample for https://stackoverflow.com/questions/61395486/read-csv-file-and-return-indented-menu-c-sharp by sommmen
using System;
using System.Collections;
using System.Linq;
using System.Collections.Generic;
public class Program
{
public class Node<T>
{
public T Data {get;set;}
public List<Node<T>> Children { get; set;}
public Node()
{
Children = new List<Node<T>>();
}
// Tree traversal in level order
public List<Node<T>> LevelOrder()
{
List<Node<T>> list = new List<Node<T>>();
Queue<Node<T>> queue = new Queue<Node<T>>();
queue.Enqueue(this);
while(queue.Count != 0)
{
Node<T> temp = queue.Dequeue();
foreach (Node<T> child in temp.Children)
queue.Enqueue(child);
list.Add(temp);
}
return list;
}
public List<Node<T>> PreOrder()
{
List<Node<T>> list = new List<Node<T>>();
list.Add(this);
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
return list;
}
public List<Node<T>> PostOrder()
{
List<Node<T>> list = new List<Node<T>>();
foreach (Node<T> child in Children)
list.AddRange(child.PreOrder());
list.Add(this);
return list;
}
}
public class Entity
{
public int id {get;set;}
public string menuName {get;set;}
public int? parentID {get;set;}
public bool isHidden {get;set;}
public string linkURL {get;set;}
}
public static void Main()
{
var data = #"ID;MenuName;ParentID;isHidden;LinkURL
1;Company;NULL;False;/company
2;About Us;1;False;/company/aboutus
3;Mission;1;False;/company/mission
4;Team;2;False;/company/aboutus/team
5;Client 2;10;False;/references/client2
6;Client 1;10;False;/references/client1
7;Client 4;10;True;/references/client4
8;Client 5;10;True;/references/client5
10;References;NULL;False;/references";
var lines = data.Split('\n');
var rootNodes = new List<Node<Entity>>();
var childItems = new List<Entity>();
// Parse the data to entities
// Items without a parent are used as rootnodes to build a tree
foreach(var row in lines.Skip(1))
{
var columns = row.Split(';');
var id = Convert.ToInt32(columns[0]);
var menuName = columns[1];
var parentID = ToNullableInt(columns[2]);
var isHidden = Convert.ToBoolean(columns[3]);
var linkURL = columns[4];
var entity = new Entity()
{
id = id,
menuName = menuName,
parentID = parentID,
isHidden = isHidden,
linkURL = linkURL
};
if(parentID == null)
{
Console.WriteLine("Found root:" + entity.id + " - " + entity.menuName);
rootNodes.Add(new Node<Entity>()
{
Data = entity
});
}
else
{
childItems.Add(entity);
}
}
// Add the childElements to their appropriate rootnode
foreach(var rootNode in rootNodes)
{
foreach(var childItem in childItems.OrderBy(a=>a.parentID).ThenBy(b=>b.menuName))
{
var newNode = new Node<Entity>()
{
Data = childItem
};
Insert(rootNode, newNode);
}
}
Console.WriteLine("-------------------");
foreach(var rootNode in rootNodes)
{
var indent = 0;
var previous = rootNode;
foreach(var node in rootNode.LevelOrder())
{
if(node.Data.isHidden) continue;
if(previous.Data.parentID != node.Data.parentID)
indent++;
for(var i = 0; i < indent; i++)
Console.Write("\t");
Console.WriteLine(node.Data.id + " - " + node.Data.menuName);
previous = node;
}
}
}
public static void Insert(Node<Entity> rootNode, Node<Entity> targetNode)
{
foreach(var current in rootNode.LevelOrder())
{
if(current.Data.id == targetNode.Data.parentID)
{
current.Children.Add(targetNode);
return;
}
}
}
public static int? ToNullableInt(string s)
{
int i;
if (int.TryParse(s, out i)) return i;
return null;
}
}
I need to determine the hierarchy level to display a tree, I don't need to link relationships at the moment, I have a list of objects as follows:
public class ObjectData
{
public string ID;
public string hierarchyParent;
public int hierarchyLevel;
}
I need to set the hierarchyLevel integer based on its row level. The hierarchyParent var contains the ID of its parent. I don't know how wide each column would be nor how many rows, so it needs to be dynamic with the hierarchy level integer either ascending or descending. So far, I have been able to determine the top row but am unsure how to continue, any help would be appreciated! So far:
List<ObjectData> Sort(List<ObjectData> objectToBeSorted){
List<ObjectData> returnlist = new List<ObjectData>();
string topObject = null;
foreach(ObjectData obj in objectToBeSorted)
{
if(obj.hierarchyParent == null){
topObject = obj.ID;
obj.hierarchyLevel = 1;
}
}
foreach(ObjectData obj in objectToBeSorted)
{
if(obj.hierarchyParent == topObject){
}
}
return returnlist;
}
Here's a quick try with sample data and recursive calls :
The useful part is is in AssignChild method.
public class ObjectData
{
public string ID;
public string hierarchyParent;
public int hierarchyLevel;
}
void Main()
{
var objects = new List<ObjectData>() {
new ObjectData() { ID = "Obj12", hierarchyParent = null },
new ObjectData() { ID = "Obj5", hierarchyParent = "Obj12" },
new ObjectData() { ID = "Obj9", hierarchyParent = "Obj12" },
new ObjectData() { ID = "Obj7", hierarchyParent = "Obj5" },
new ObjectData() { ID = "Obj99", hierarchyParent = "Obj58" },
new ObjectData() { ID = "Obj58", hierarchyParent = "Obj5" } };
ObjectData top = objects.Find(p => p.hierarchyParent == null);
top.hierarchyLevel = 1;
AssignChild(objects, top);
objects.Dump();
}
void AssignChild(List<ObjectData> all, ObjectData parent)
{
var child = all.FindAll(o => o.hierarchyParent == parent.ID);
child.ForEach(c => { c.hierarchyLevel = parent.hierarchyLevel +1; AssignChild(all, c); });
}
It can probably be optimized but it should work.
I suggest doing something like this:
public int GetHierarchyLevel(ObjectData obj, IEnumerable<ObjectData> allObjects)
{
if(obj.hierarchyParent == null)
return 1;
else
return 1 + GetHierarchyLevel(allObjects.First(o=>o.ID == obj.hierarchyParent));
}
Of course, you should integrate this into your classes so that you can possibly replace the arguments by class members. Also, please notice that some error checking may be required. It is just meant to give you an idea of an algorithm.
For performance, I suggest a caching mechanism. Like initializing hierarchyLevel to -1 and using the following modification:
public int GetHierarchyLevel(ObjectData obj, IEnumerable<ObjectData> allObjects)
{
if (obj.hierarchyLevel != -1)
return obj.hierarchyLevel;
if(obj.hierarchyParent == null)
return 1;
else
return 1 + GetHierarchyLevel(allObjects.First(o=>o.ID == obj.hierarchyParent));
}
Of course, this would require invalidating all cached results when you want to recalculate after a change in the structure of your hierarchy.
I have a list of a class, the class also has a class which is used to display map in the tree view.
public class Option
{
public Guid Id;
public string Title;
public string Description;
public List<GotoOption> GotoOptions;
public bool IsEnd;
public string GotoValueParent;
public Option()
{
this.IsEnd = false;
this.GotoOptions = new List<GotoOption>();
}
}
public class GotoOption
{
public Guid GotoId;
public string Value;
}
So an Option can have many GotoOptions and these are mapped by the Guid, so if my tree view looked like:
Tree
1.1. Branch
1.2. Branch
1.3. Branch
There will be 4 options but the tree view will have 3 GotoOptions which link to the branches.
So my goal is to basically create a recursive loop so I don't have to manually create a loop, but I got no idea how to start it off.
Currently I have -
private void PopulateTreeView(Option option)
{
if (option != null)
{
TreeNode node = new TreeNode();
node.Text = option.Title;
node.Tag = option;
pages.Nodes.Add(node);
foreach (GotoOption op in option.GotoOptions)
{
Option ops = Options.FirstOrDefault(i => i.Id == op.GotoId);
TreeNode inner = new TreeNode();
inner.Text = ops.Title;
inner.Tag = ops;
node.Nodes.Add(inner);
foreach (GotoOption op2 in ops.GotoOptions)
{
Option opps = Options.FirstOrDefault(i => i.Id == op2.GotoId);
TreeNode inner2 = new TreeNode();
inner2.Text = opps.Title;
inner2.Tag = opps;
inner.Nodes.Add(inner2);
}
}
}
}
Which is looping for 3 layers only, but we could have 10-25 odd layers and that's a lot of manual code. I have been looking at how it works with files and folders http://www.dotnetperls.com/recursive-file-list but I can't seem to convert it from how it works there to getting it to work with my code. Any help would be great.
Managed to solve, I created an optional parameter passing in the created node, if the parameter is not passed in, it creates a new one.
private void PopulateTreeView(Option option, TreeNode existingNode = null)
{
if (option != null)
{
TreeNode newNode = new TreeNode();
newNode.Text = option.Title;
newNode.Tag = option;
if (existingNode == null)
{
pages.Nodes.Add(newNode);
}
else
{
existingNode.Nodes.Add(newNode);
}
foreach (GotoOption gotoOption in option.GotoOptions)
{
Option newOption = Options.FirstOrDefault(i => i.Id == gotoOption.GotoId);
PopulateTreeView(newOption, newNode);
}
}
}
private void CreateTreeView()
{
var roots = Options.Select(z => z.Id)
.Except(Options.SelectMany(z => z.GotoOptions.Select(x => x.GotoId)))
.Select(z => Options.Single(x => x.Id == z));
var treeNodes = roots.Select(GetNode);
foreach (var treeNode in treeNodes)
{
pages.Nodes.Add(treeNode);
}
}
private TreeNode GetNode(Option option)
{
var node = new TreeNode
{
Text = option.Title,
Tag = option
};
foreach (var child in option.GotoOptions.Select(z => Options.Single(x => x.Id == z.GotoId)))
{
node.Nodes.Add(GetNode(child));
}
return node;
}
I try do implement a user dynamic filter, where used selects some properties, selects some operators and selects also the values.
As I didn't find yet an answer to this question, I tried to use LINQ expressions.
Mainly I need to identify all houses which main rooms are kitchens(any sens, I know).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
//using System.Linq.Dynamic;
namespace ConsoleApplication2
{
class Program
{
static void Main(string[] args)
{
Room aRoom = new Room() { Name = "a Room" };
Room bRoom = new Room() { Name = "b Room" };
Room cRoom = new Room() { Name = "c Room" };
House myHouse = new House
{
Rooms = new List<Room>(new Room[] { aRoom }),
MainRoom = aRoom
};
House yourHouse = new House()
{
Rooms = new List<Room>(new Room[] { bRoom, cRoom }),
MainRoom = bRoom
};
House donaldsHouse = new House()
{
Rooms = new List<Room>(new Room[] { aRoom, bRoom, cRoom }),
MainRoom = aRoom
};
var houses = new List<House>(new House[] { myHouse, yourHouse, donaldsHouse });
//var kitchens = houses.AsQueryable<House>().Where("MainRoom.Type = RoomType.Kitchen");
//Console.WriteLine("kitchens count = {0}", kitchens.Count());
var houseParam = Expression.Parameter(typeof(House), "house");
var houseMainRoomParam = Expression.Property(houseParam, "MainRoom");
var houseMainRoomTypeParam = Expression.Property(houseMainRoomParam, "Type");
var roomTypeParam = Expression.Parameter(typeof(RoomType), "roomType");
var comparison = Expression.Lambda(
Expression.Equal(houseMainRoomTypeParam,
Expression.Constant("Kitchen", typeof(RoomType)))
);
// ???????????????????????? DOES NOT WORK
var kitchens = houses.AsQueryable().Where(comparison);
Console.WriteLine("kitchens count = {0}", kitchens.Count());
Console.ReadKey();
}
}
public class House
{
public string Address { get; set; }
public double Area { get; set; }
public Room MainRoom { get; set; }
public List<Room> Rooms { get; set; }
}
public class Room
{
public double Area { get; set; }
public string Name { get; set; }
public RoomType Type { get; set; }
}
public enum RoomType
{
Kitchen,
Bedroom,
Library,
Office
}
}
var kitchens = from h in houses
where h.MainRoom.Type == RoomType.Kitchen
select h;
But you must set the RoomType property on the rooms before.
Ok, edit:
so you must redefine:
var comparison = Expression.Lambda<Func<House, bool>>(...
Then, when you use it:
var kitchens = houses.AsQueryable().Where(comparison.Compile());
Edit #2:
Ok, here you go:
var roomTypeParam = Expression.Parameter(typeof(RoomType), "roomType");
// ???????????????????????? DOES NOT WORK
var comparison = Expression.Lambda<Func<House, bool>>(
Expression.Equal(houseMainRoomTypeParam,
Expression.Constant(Enum.Parse(typeof(RoomType), "Kitchen"), typeof(RoomType))), houseParam);
// ???????????????????????? DOES NOT WORK
var kitchens = houses.AsQueryable().Where(comparison);
Edit #3: Of, for your needs, I am out of ideas for now. I give you one last one:
Declare an extension method on the String type:
internal static object Prepare(this string value, Type type)
{
if (type.IsEnum)
return Enum.Parse(type, value);
return value;
}
Then use it in that expression like:
Expression.Constant("Kitchen".Prepare(typeof(RoomType)), typeof(RoomType))
That's because apparently enums are treated differently. That extension will leave the string unaltered for other types. Drawback: you have to add another typeof() there.
// ???????????????????????? DOES NOT WORK
var kitchens = houses.AsQueryable().Where(comparison);
The Where method takes a Func<House, bool> or a Expression<Func<House, bool>> as the parameter, but the variable comparison is of type LambdaExpression, which doesn't match. You need to use another overload of the method:
var comparison = Expression.Lambda<Func<House, bool>>(
Expression.Equal(houseMainRoomTypeParam,
Expression.Constant("Kitchen", typeof(RoomType))));
//now the type of comparison is Expression<Func<House, bool>>
//the overload in Expression.cs
public static Expression<TDelegate> Lambda<TDelegate>(Expression body, params ParameterExpression[] parameters);
I wouldn't build the where clause in that way - I think it's more complex than it needs to be for your needs. Instead, you can combine where clauses like this:
var houses = new List<House>(new House[] { myHouse, yourHouse, donaldsHouse });
// A basic predicate which always returns true:
Func<House, bool> housePredicate = h => 1 == 1;
// A room name which you got from user input:
string userEnteredName = "a Room";
// Add the room name predicate if appropriate:
if (!string.IsNullOrWhiteSpace(userEnteredName))
{
housePredicate += h => h.MainRoom.Name == userEnteredName;
}
// A room type which you got from user input:
RoomType? userSelectedRoomType = RoomType.Kitchen;
// Add the room type predicate if appropriate:
if (userSelectedRoomType.HasValue)
{
housePredicate += h => h.MainRoom.Type == userSelectedRoomType.Value;
}
// MainRoom.Name = \"a Room\" and Rooms.Count = 3 or
// ?????????????????????????
var aRoomsHouses = houses.AsQueryable<House>().Where(housePredicate);
I tested this one, honest :)
what about this
var kitchens = houses
.SelectMany(h => h.Rooms, (h, r) => new {House = h, Room = r})
.Where(hr => hr.Room.Type == RoomType.Kitchen)
.Select(hr => hr.House);
To add a new Enum type to dynamic Linq, you must add the following code :
typeof(Enum),
typeof(T)
T : Enum type
in predefined types of dynamic. That works for me.