Фильтрация коллекции в LINQ с помощью рекурсии - PullRequest
2 голосов
/ 16 ноября 2011

У меня есть коллекция с именем MenuItemCollection и эта производная форма Список

Существует одиночный экземпляр MenuItemCollection , и если я упросту поля MenuItem :

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

Мне нужно использовать метод filter для этой коллекции. Например, я хотел бы отфильтровать коллекцию по идентификатору одного меню. Вот пример 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>();
}

И вызов этого метода как:

var FilteredMenus = MenuItemCollection.GetInstance.Filter(new MenuItemFilterArgs { Id = 5 });

Поскольку число 5 находится во внутренней коллекции под число 2 пункт меню, результат возвращается как 0. Он не может быть найден.

Как можно рекурсивно запустить фильтр через внутренние MenuItemCollections? Не могли бы вы написать пример кода?

PS: Если вы должны знать, почему я использую одноэлементный экземпляр; моя идея была извлечь меню из базы данных и сохранить его как объект для более простое и быстрое использование во время выполнения.

Любая помощь будет принята с благодарностью. Заранее спасибо

1 Ответ

3 голосов
/ 16 ноября 2011

Вот рабочий пример.Вам нужен метод 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);
        }
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...