使用递归过滤LINQ上的集合

使用递归过滤LINQ上的集合,linq,collections,recursion,filter,Linq,Collections,Recursion,Filter,我有一个名为MenuItemCollection的集合,这个派生形式是列表 有一个MenuItemCollection的单例实例,如果我简化MenuItem的字段: public class MenuItem { int Id {set;get;} string Title {set;get;} MenuItemCollection ChildMenus {set;get;} } 我需要在此集合上使用过滤器方法。例如,我想在集合中筛选一个菜单的Id。 以下是一个示例MenuI

我有一个名为MenuItemCollection的集合,这个派生形式是列表

有一个MenuItemCollection的单例实例,如果我简化MenuItem的字段:

public class MenuItem
{
   int Id {set;get;}
   string Title {set;get;}
   MenuItemCollection ChildMenus {set;get;}
}
我需要在此集合上使用过滤器方法。例如,我想在集合中筛选一个菜单的Id。 以下是一个示例MenuItemCollection:

1-Home
2-User Menu
  4-Update Info
  5-Delete Account
3-News
  6-Archived News
如您所见,有一些子菜单,如数字4或数字6

我通常使用以下方法进行过滤:

public List<MenuItem> Filter(MenuItemFilterArgs args)
{
     List<MenuItem> Result = new List<MenuItem>();
     IQueryable<MenuItem> QueryableTemp = this.AsQueryable();

     return (from item in QueryableTemp
                      orderby item.Ordering descending
                      select item).ToList<MenuItem>();
}
由于数字5位于数字2菜单项下的内部集合中,因此结果返回为0。找不到它

如何通过内部MenuItemCollections递归运行筛选器?你能写一个代码示例吗

PS:如果您必须知道我为什么使用单例实例;我的想法是 从数据库中检索菜单并将其作为 在运行时更容易、更快地使用

任何帮助都将不胜感激。
提前感谢

这里是一个工作示例。您需要的是ExtensionMethods.Map方法

using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace WindowsFormsApplication1
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var menuItems1 = MenuItemCollection.Instance.Filter(null);
            var menuItems2 = MenuItemCollection.Instance.Filter(new MenuItemCriteria { Id = 5 });
            var menuItems3 = MenuItemCollection.Instance.Filter(new MenuItemCriteria { Title = "News" });
            var menuItems4 = MenuItemCollection.Instance.Filter(new MenuItemCriteria { Title = "News", IsTrash = true });
        }
    }

    public class MenuItemCollection : List<MenuItem>
    {
        public static readonly MenuItemCollection Instance;

        static MenuItemCollection()
        {
            Instance = GetMenuList();
        }

        static MenuItemCollection GetMenuList()
        {
            return new MenuItemCollection {
                new MenuItem {Id = 1, Title = "Home"},
                new MenuItem {Id = 2, Title = "User Menu", ChildMenus = new MenuItemCollection { 
                        new MenuItem { Id = 4, Title = "Update Info"},
                        new MenuItem { Id = 5, Title = "Delete"}
                    }},
                new MenuItem {Id = 3, Title = "News", ChildMenus = new MenuItemCollection { 
                        new MenuItem { Id = 6, Title = "Archived News"},
                        new MenuItem { Id = 6, Title = "Trashy News", IsTrash = true}
                    }},
            };
        }

        public List<MenuItem> Filter(MenuItemCriteria criteria)
        {
            var expression = PredicateBuilder.True<MenuItem>();

            if(criteria != null)
            {
                if (criteria.Id.HasValue)
                {
                    expression = expression.And(menuItem => menuItem.Id == criteria.Id);
                }

                if (!string.IsNullOrEmpty(criteria.Title))
                {
                    expression = expression.And(menuItem => menuItem.Title.Contains(criteria.Title));
                }

                if (criteria.IsTrash.HasValue)
                {
                    expression = expression.And(menuItem => menuItem.IsTrash == criteria.IsTrash);
                }
            }

            Func<MenuItem, bool> searchCriteria = expression.Compile();
            Func<MenuItem, IEnumerable<MenuItem>> childrenSelector = x => x.ChildMenus;

            return this.Map(searchCriteria, childrenSelector).ToList();
        }
    }

    public class MenuItemCriteria
    {
        public int? Id { set; get; }
        public string Title { set; get; }
        public bool? IsTrash { set; get; }
    }

    public class MenuItem
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public bool IsTrash { set; get; }
        public MenuItemCollection ChildMenus { set; get; }
    }

    public static class ExtensionMethods
    {
        public static IEnumerable<T> Map<T>(this IEnumerable<T> source,
                                            Func<T, bool> selector = null,
                                            Func<T, IEnumerable<T>> childrenSelector = null)
        {
            if (source == null) return new List<T>();

            if (selector == null)
            {
                // create a default selector that selects all items
                selector = x => true;
            }

            var list = source.Where(selector);

            if (childrenSelector != null)
            {
                foreach (var item in source)
                {
                    list = list.Concat(childrenSelector(item).Map(selector, childrenSelector));
                }
            }

            return list;
        }
    }

    public static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                            Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                             Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }
    }
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用System.Linq.Expressions;
命名空间Windows窗体应用程序1
{
静态类程序
{
[状态线程]
静态void Main()
{
var menuItems1=MenuItemCollection.Instance.Filter(null);
var menuItems2=MenuItemCollection.Instance.Filter(新的MenuItemCriteria{Id=5});
var menuItems3=MenuItemCollection.Instance.Filter(新的MenuItemCriteria{Title=“News”});
var menuItems4=MenuItemCollection.Instance.Filter(新的MenuItemCriteria{Title=“News”,IsTrash=true});
}
}
公共类MenuItemCollection:列表
{
公共静态只读MenuItemCollection实例;
静态MenuItemCollection()
{
Instance=GetMenuList();
}
静态MenuItemCollection GetMenuList()
{
返回新的MenuItemCollection{
新菜单项{Id=1,Title=“Home”},
新菜单项{Id=2,Title=“用户菜单”,子菜单项=新菜单项集合{
新菜单项{Id=4,Title=“Update Info”},
新菜单项{Id=5,Title=“Delete”}
}},
新菜单项{Id=3,Title=“News”,子菜单项=新菜单项集合{
新菜单项{Id=6,Title=“存档新闻”},
新菜单项{Id=6,Title=“垃圾新闻”,IsTrash=true}
}},
};
}
公共列表筛选器(MenuItemCriteria标准)
{
var表达式=PredicateBuilder.True();
如果(条件!=null)
{
if(criteria.Id.HasValue)
{
expression=expression.And(menuItem=>menuItem.Id==criteria.Id);
}
如果(!string.IsNullOrEmpty(criteria.Title))
{
expression=expression.And(menuItem=>menuItem.Title.Contains(criteria.Title));
}
if(criteria.IsTrash.HasValue)
{
expression=expression.And(menuItem=>menuItem.IsTrash==criteria.IsTrash);
}
}
Func searchCriteria=expression.Compile();
Func childrenSelector=x=>x.childmenu;
返回此.Map(searchCriteria,childrenSelector).ToList();
}
}
公共类菜单项标准
{
公共int?Id{set;get;}
公共字符串标题{set;get;}
公共bool?IsTrash{set;get;}
}
公共类菜单项
{
公共int Id{set;get;}
公共字符串标题{set;get;}
公共bool IsTrash{set;get;}
公共菜单项集合子菜单{set;get;}
}
公共静态类扩展方法
{
公共静态IEnumerable映射(此IEnumerable源,
Func选择器=null,
Func childrenSelector=null)
{
if(source==null)返回新列表();
if(选择器==null)
{
//创建选择所有项目的默认选择器
选择器=x=>true;
}
var list=来源。其中(选择器);
if(childrenSelector!=null)
{
foreach(源中的var项)
{
list=list.Concat(childrenSelector(item).Map(selector,childrenSelector));
}
}
退货清单;
}
}
公共静态类谓词生成器
{
公共静态表达式True(){return f=>True;}
公共静态表达式False(){return f=>False;}
公共静态表达式或(此表达式expr1,
表达式expr2)
{
var invokedExpr=Expression.Invoke(expr2,expr1.Parameters.Cast());
返回表达式.Lambda
(Expression.OrElse(expr1.Body,invokedExpr),expr1.Parameters);
}
公共静态表达式和(此表达式expr1,
表达式expr2)
{
var invokedExpr=Expression.Invoke(expr2,expr1.Parameters.Cast());
返回表达式.Lambda
(Expression.AndAlso(expr1.Body,invokedExpr),expr1.Parameters);
}
}
}

在菜单项用于构建树之前,通过查询菜单项的原始源代码,您可能可以更轻松地完成这项工作。但问题是,我不想一直回到数据库。。这是我使用罪恶的主要原因
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;

namespace WindowsFormsApplication1
{
    static class Program
    {
        [STAThread]
        static void Main()
        {
            var menuItems1 = MenuItemCollection.Instance.Filter(null);
            var menuItems2 = MenuItemCollection.Instance.Filter(new MenuItemCriteria { Id = 5 });
            var menuItems3 = MenuItemCollection.Instance.Filter(new MenuItemCriteria { Title = "News" });
            var menuItems4 = MenuItemCollection.Instance.Filter(new MenuItemCriteria { Title = "News", IsTrash = true });
        }
    }

    public class MenuItemCollection : List<MenuItem>
    {
        public static readonly MenuItemCollection Instance;

        static MenuItemCollection()
        {
            Instance = GetMenuList();
        }

        static MenuItemCollection GetMenuList()
        {
            return new MenuItemCollection {
                new MenuItem {Id = 1, Title = "Home"},
                new MenuItem {Id = 2, Title = "User Menu", ChildMenus = new MenuItemCollection { 
                        new MenuItem { Id = 4, Title = "Update Info"},
                        new MenuItem { Id = 5, Title = "Delete"}
                    }},
                new MenuItem {Id = 3, Title = "News", ChildMenus = new MenuItemCollection { 
                        new MenuItem { Id = 6, Title = "Archived News"},
                        new MenuItem { Id = 6, Title = "Trashy News", IsTrash = true}
                    }},
            };
        }

        public List<MenuItem> Filter(MenuItemCriteria criteria)
        {
            var expression = PredicateBuilder.True<MenuItem>();

            if(criteria != null)
            {
                if (criteria.Id.HasValue)
                {
                    expression = expression.And(menuItem => menuItem.Id == criteria.Id);
                }

                if (!string.IsNullOrEmpty(criteria.Title))
                {
                    expression = expression.And(menuItem => menuItem.Title.Contains(criteria.Title));
                }

                if (criteria.IsTrash.HasValue)
                {
                    expression = expression.And(menuItem => menuItem.IsTrash == criteria.IsTrash);
                }
            }

            Func<MenuItem, bool> searchCriteria = expression.Compile();
            Func<MenuItem, IEnumerable<MenuItem>> childrenSelector = x => x.ChildMenus;

            return this.Map(searchCriteria, childrenSelector).ToList();
        }
    }

    public class MenuItemCriteria
    {
        public int? Id { set; get; }
        public string Title { set; get; }
        public bool? IsTrash { set; get; }
    }

    public class MenuItem
    {
        public int Id { set; get; }
        public string Title { set; get; }
        public bool IsTrash { set; get; }
        public MenuItemCollection ChildMenus { set; get; }
    }

    public static class ExtensionMethods
    {
        public static IEnumerable<T> Map<T>(this IEnumerable<T> source,
                                            Func<T, bool> selector = null,
                                            Func<T, IEnumerable<T>> childrenSelector = null)
        {
            if (source == null) return new List<T>();

            if (selector == null)
            {
                // create a default selector that selects all items
                selector = x => true;
            }

            var list = source.Where(selector);

            if (childrenSelector != null)
            {
                foreach (var item in source)
                {
                    list = list.Concat(childrenSelector(item).Map(selector, childrenSelector));
                }
            }

            return list;
        }
    }

    public static class PredicateBuilder
    {
        public static Expression<Func<T, bool>> True<T>() { return f => true; }
        public static Expression<Func<T, bool>> False<T>() { return f => false; }

        public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1,
                                                            Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
        }

        public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
                                                             Expression<Func<T, bool>> expr2)
        {
            var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
            return Expression.Lambda<Func<T, bool>>
                  (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
        }
    }
}