LINQ IQueryable Help - PullRequest
       17

LINQ IQueryable Help

1 голос
/ 24 июня 2009

У меня есть класс Menu, который имеет свойство IQueryable, называемое WebPages. В следующем утверждении я возвращаю элементы меню, основанные на совпадении, но мне нужно включить свойство Webpages. Вот что у меня есть на данный момент.

var allCategories = Menu.All().Where(x => x.CategoryID == 4 && x.Visible)

Мне нужно расширить его, чтобы проверить свойство в классе WebPage, что-то вроде этого ..

var allCategories = Menu.All().Where(x => x.CategoryID == 4 && x.Visible && x.WebPages.Roles.Contains(User.Identity.Name))

Это не скомпилируется, но я надеюсь, что вы понимаете, что я пытаюсь сделать.

ПРИМЕЧАНИЕ. Свойство Webpage заполняется PageID, а не CategoryID, но не уверен, что это имеет значение ??

Вот краткое описание моих занятий.

public partial class Menu: IActiveRecord
    {
       public int ID {get; set;}
       public int CategoryID {get;set;}
       public bool Visible {get;set;}
       public int PageID {get;set;}
       public IQueryable<WebPage> WebPages
        {
            get
            {

                  var repo=NorthCadburyWebsite.Models.WebPage.GetRepo();
                  return from items in repo.GetAll()
                       where items.ID == _PageID
                       select items;
            }
        }
}

public partial class WebPage: IActiveRecord
    {
       public int ID {get;set;}
       public string Roles {get;set;}
}

Ответы [ 4 ]

1 голос
/ 26 июня 2009

Если я правильно понимаю проблему, вы хотите что-то вроде этого:

var menuItems =
    from menuItem in Menu.All()
        where menuItem.Visible
            and (
                menuItem.WebPages.Contains(
                    webPage => webPage.Roles.Contains(
                        "role"
                    )
                )
                or menuItem.PageIsNull
            )
        select menuItem;

При этом следует выбирать только те пункты меню, которые присоединены к страницам с соответствующей ролью.

0 голосов
/ 24 июня 2009

Это должно сделать это для вас, приятель. Вам просто нужно сказать WebPages.Any, это вернет true, если какое-либо меню содержит веб-страницу с указанной вами ролью.

var allCategories = menus.Where(menu => menu.CategoryID == 1 && menu.Visible && menu.WebPages.Any(webPage => webPage.Roles.Contains(roleToSearchFor)));

Итак, ключевой бит, который вам нужно добавить, это.

menu.WebPages.Any(webPage => webPage.Roles.Contains(roleToSearchFor))

Использование функции Any () очень эффективно, так как перестает искать, как только найдет совпадение.

Если вы использовали Где () , а затем Count () , он будет перебирать все веб-страницы для поиска всех совпадений, а затем перебирать результаты для их подсчета, так что быть гораздо менее эффективным.

Ниже приведен пример с полным исходным кодом, который вы можете попробовать.

    namespace DoctaJonez.TestingBrace
    {
        public partial class Menu //: IActiveRecord 
        { 
            public int ID { get; set; } 
            public int CategoryID { get; set; } 
            public bool Visible { get; set; } 
            public int PageID { get; set; } 
            public IQueryable<WebPage> WebPages { get; set; } 
        } 

        public partial class WebPage //: IActiveRecord 
        { public int ID { get; set; } public string Roles { get; set; } }

        public static class Launcher
        {
            /// <summary>
            /// The Main entry point of the program.
            /// </summary>
            static void Main(string[] args)
            {
                Menu myMenu1 = new Menu
                {
                    ID = 1,
                    CategoryID = 1,
                    PageID = 1,
                    Visible = true,
                    WebPages = new List<WebPage>()
                    {
                        new WebPage { ID = 1, Roles = "Role1" },
                        new WebPage { ID = 1, Roles = "Role2" },
                        new WebPage { ID = 1, Roles = "Role3" },
                    }.AsQueryable()
                };

                Menu myMenu2 = new Menu
                {
                    ID = 1,
                    CategoryID = 1,
                    PageID = 1,
                    Visible = true,
                    WebPages = new List<WebPage>()
                    {
                        new WebPage { ID = 1, Roles = "Role3" },
                        new WebPage { ID = 1, Roles = "Role4" },
                        new WebPage { ID = 1, Roles = "Role5" },
                    }.AsQueryable()
                };

                Menu myMenu3 = new Menu
                {
                    ID = 1,
                    CategoryID = 1,
                    PageID = 1,
                    Visible = true,
                    WebPages = new List<WebPage>()
                    {
                        new WebPage { ID = 1, Roles = "Role5" },
                        new WebPage { ID = 1, Roles = "Role6" },
                        new WebPage { ID = 1, Roles = "Role7" },
                    }.AsQueryable()
                };

                List<Menu> menus = new List<Menu>() { myMenu1, myMenu2, myMenu3 };

                string roleToSearchFor = "Role3";

                var allCategories = menus.Where(menu => menu.CategoryID == 1 && menu.Visible && menu.WebPages.Any(webPage => webPage.Roles.Contains(roleToSearchFor))).ToList();

                return;
            }
        }
0 голосов
/ 24 июня 2009

DoctorJonez спасибо !!!

Я помещаю это здесь, поскольку у меня есть больше места. У меня есть 11 записей в таблице меню, только 1 с установленным PageID, однако, если я использую

var test = Menu.All().Where(x => x.WebPages.Any(pages => pages.Roles.Contains(Roles.GetRolesForUser()[0])

Я получаю 11 записей, так как SQL-запрос это

SELECT [t0].[CategoryID], [t0].[CreatedBy], [t0].[CreatedOn], [t0].[ID], [t0].[ImageID], [t0].[ImageIDHover], [t0].[Locale], [t0].[ModifiedBy], [t0].[ModifiedOn], [t0].[OrderID], [t0].[PageID], [t0].[ParentID], [t0].[Title], [t0].[URL], [t0].[Visible]
FROM [dbo].[Menu] AS t0
WHERE EXISTS(
  SELECT NULL 
  FROM [dbo].[WebPage] AS t1
  WHERE ([t1].[Roles] LIKE '%' + 'User' + '%')
  )

Если я запускаю это, я получаю 1 запись

var test = Menu.All().Where(x => x.WebPages.Any(pages => pages.Roles.Contains(Roles.GetRolesForUser()[0]) && pages.ID == x.PageID));

SQL для этого

SELECT [t0].[CategoryID], [t0].[CreatedBy], [t0].[CreatedOn], [t0].[ID], [t0].[ImageID], [t0].[ImageIDHover], [t0].[Locale], [t0].[ModifiedBy], [t0].[ModifiedOn], [t0].[OrderID], [t0].[PageID], [t0].[ParentID], [t0].[Title], [t0].[URL], [t0].[Visible]
FROM [dbo].[Menu] AS t0
WHERE EXISTS(
  SELECT NULL 
  FROM [dbo].[WebPage] AS t1
  WHERE (([t1].[Roles] LIKE '%' + 'User' + '%') AND ([t1].[ID] = [t0].[PageID]))
  )

Это ошибка в Subsonic или я не правильно ее понимаю?

Проблема с Any () состоит в том, что в SQL, пока одна запись завершается, не имеет значения, какая запись вернет данные.

Я думаю, что эффективно хочу UNION SQL, как показано ниже, но я не знаю, как я могу преобразовать это в C # / Subsonic

select m.* from menu m where pageid is null

union

select m.* from menu m

join webpage p
on p.id = m.pageid

where p.roles like '%User%'

Я хочу вернуть все записи меню, и для тех с установленным PageID, что соответствующая веб-страница имеет роль пользователя в этом. Если роль пользователя отсутствует на веб-странице, я не хочу видеть ее в своих результатах.

0 голосов
/ 24 июня 2009

Попробуйте изменить

public IQueryable<WebPage> WebPages

до

public IEnumerable<WebPage> WebPages

Я думаю, что LINQ-запросы возвращают IEnumerable ...

...