views:

655

answers:

3

I have a schema like so. Menu->Pages->PageRoles->ASPNetRoles

Menu has a CategoryID.

I want to return all Menu items with a CategoryID of 6.

Some Menu items have a foreigh key of PageID. Pages can have 1 or more roles against them. I can check the currently logged in users roles and make sure that they are in the results by joining the tables.

I want to return all Menu items with a CategoryID of 6 and for those that have PageID's the users role must be in those roles assigned to the page.

The only way I can think is to do a union but when I do this in Subsonic it fails. The following works.

    var dd = (from menu in Menu.All().Where(x => x.PageID == null && x.CategoryID == 6) select menu);
    var ss =  from menu2 in Menu.All()
              join pages in WebPage.All() on menu2.PageID equals pages.ID
              join pagesRoles in PageRole.All() on pages.ID equals pagesRoles.PageID
              join roles in aspnet_Role.All() on pagesRoles.RoleId equals roles.RoleId
              where Roles.GetRolesForUser().Contains(roles.RoleName) &&
              menu2.CategoryID == 6
              select menu2;

How do I combine the results?

Doing this it fails:

var dd = (from menu in Menu.All().Where(x => x.PageID == null) select menu).Union(
              from menu2 in Menu.All()
              join pages in WebPage.All() on menu2.PageID equals pages.ID
              join pagesRoles in PageRole.All() on pages.ID equals pagesRoles.PageID
              join roles in aspnet_Role.All() on pagesRoles.RoleId equals roles.RoleId
              where Roles.GetRolesForUser().Contains(roles.RoleName)
              select menu2);

EDIT:

I can get the results in SQL via LEFT OUTER JOINS (See Below) but again translating this into LINQ/Subsonic fails.

SELECT * FROM MENU M

LEFT OUTER JOIN WEBPAGE P
ON P.ID = M.PAGEID

LEFT OUTER JOIN PAGEROLES R
ON R.PAGEID = P.ID

LEFT OUTER JOIN ASPNET_ROLES A
ON A.ROLEID = R.ROLEID

WHERE ((CATEGORYID = 1) OR ( CategoryID = 1 AND A.ROLENAME IN ('ADMINISTRATOR','USER')))

Even something simple like this fails

var resu = from p in db.Menus 
join pages in db.WebPages on p.PageID equals pages.ID 
into temp from pages in temp.DefaultIfEmpty()
select p;
A: 

It looks like you're bumping into a bug in SubSonic's implementation of Union/Concat, you should report it to the google code site. You should just be able to do the following, which I'm pretty sure you'd already worked out:

var unionList = dd.Concat(ss).ToList<Menu>();

In the meantime the following should be pretty close to the outer join you're after:

var ss =  from menu in Menu.All()
    group join pages in WebPage.All() on menu2.PageID equals pages.ID
      into pagesMenu from pm in pagesMenu.DefaultIfEmpty()
    group join pagesRoles in PageRole.All() on pages.ID equals pagesRoles.PageID
      into pagesRolesPages from prp in pagesRolesPages.DefaultIfEmpty()
    group join roles in aspnet_Role.All() on pagesRoles.RoleId equals roles.RoleId
      into pagesRolesRoles from prr in pagesRolesRoles.DefaultIfEmpty()
  where menu.PageID == null || 
    (Roles.GetRolesForUser().Contains(roles.RoleName) && menu2.CategoryID == 6)
  select menu;
Adam
That doesn't compile I'm afraid
Jon
Well I did say pretty close ;) What's the error?
Adam
It doesn't recognise the group keyword. Just had an forum post from Rob Conery and have noted the Union issue as a bug as well as the LEFT OUTER JOIN issue. Will go with the ConCat approach for now. Thanks
Jon
Sounds like the concat is the simplest workaround. Hopefully the bugs'll get squished soon
Adam
Rob has decided its not going to be supported in SS3.http://code.google.com/p/subsonicthree/issues/detail?id=68
Jon
A: 

Yeh, I'm the one that reports the LEFT OUTER JOIN problem. rob told me that the problem is by the Linq parser (Iqueryable provider impl). i think this is major issue because if u can't do more than just basic querys - you can't work with it at all...

so push rob to fix it ASAP. :-).

zahi.

Rob's suggestion is to use the Subsonic Query object instead of LINQ for that kind of thing
Jon
A: 

If the SQL queries generated to fill your anonymous types doesn't match, no way: you must do something like this:

dd.ToList().AddRange(ss.ToList());

And of course, this implies double request to your database.

Apocatastasis