Copied to clipboard

Flag this post as spam?

This post will be reported to the moderators as potential spam to be looked at


  • Tim 225 posts 690 karma points
    Jul 19, 2010 @ 20:23
    Tim
    0

    Linq2Umbraco query has me stumped

    Hi,

    I've got a problem with Linq2Umbraco at the moment,which is probably due to my poor understanding of LInq. I'm a bit old school for all this fancy new technology ;-)

    Here's the Scenario:

    I have a collection of content like this, this is the node structure (the doc types are in square brackets)

    ->Content Folder [contentFolder]

      ->en-gb [culture]

        ->packages [packageFolder]

          ->test package [package]

    I want to be able to get "test package" given that I know what a property on test package is and I know the correct culture (there will be duplicate "test package" items in different cultures)

    So I need to pass my linq the culture (in this case en-gb) and a property of a package

    My first thought was to look in the collection of packages and pick the packages that matched the property then filter down the packages by their grandparent culture.

    In XSLT I'd use the ancestor-or-self axis to do this. I tried using the Linq to SQL AncestorOrDefault method but couldn't get it to work.

    Any clues on how I could approach this in Linq would be gratefully received.

    Thanks

    Tim

     

     

  • Peter Gregory 408 posts 1614 karma points MVP 3x admin c-trib
    Jul 19, 2010 @ 22:36
    Peter Gregory
    0

    Hi Tim, 

    Linq2Umbraco is missing the Path property which which would have contained the list of parent nodes.  I have put in a request to Aaron (@slace) to have this added in the next version.   At the moment the Entities in L2U does have the Parent property that returns you the parent object as far as I am aware but you would have to keep walking up through the parents to find the node you wanted this way.

    Im not a massive Linq expert but I am sure that there is probably some fancy query that would do that for you in linq.

    Peter

  • Morten Christensen 596 posts 2773 karma points admin hq c-trib
    Jul 19, 2010 @ 22:40
    Morten Christensen
    0

    With Peters info I guess you could do something like this (of the top of my head):

    var items = ctx.packages.Where(p => p.packageProperty == "something" && p.Parent().Parent().Name == "en-GB")

    where packages is your list of packages from the UmbracoDataContext, your package property = something and then walk up the parents until you can limit your result by your culture - assuming Name has the "en-GB" culturecode.

    - Morten

  • Tim 225 posts 690 karma points
    Jul 19, 2010 @ 22:49
    Tim
    0

    Hmmm OK

    Sounds like the thing to do might be to combine the normal NodeFactory stuff with Linq (so I can grab the path) a bit like this (very much example code!)

    This looks in the packages collection for a package with a PackageId of 123

    Then it gets the node from the package.Id and splits the path into a collection. It then looks in he path collection again using link to get the cultures collection which is filtered by name.

    That's just proof of concept code, but might be an approach. I wonder if it would be easier to extend the base classes to do the path side of things...

    var pakage = (from packages in ctx.Packages

    where packages.PackageID == 123

    && (new Node(packages.Id)).Path.Split(',').ToList<string>().Contains

    (

    (

    from cultureFolders in ctx.cultures

    where cultureFolders.Name == cultureString

    select cultureFolders

    ).First().Id.ToString()

    )

    select packages).First();

  • Morten Christensen 596 posts 2773 karma points admin hq c-trib
    Jul 19, 2010 @ 23:09
    Morten Christensen
    0

    Isn't new Node(123) an "expensive" call to the umbraco db? If it still is I wouldn't want to use it in a linq statement..

    You could go from top to bottom if you prefer. Simply do multiple select statements untill you have narrowed your results to your package property. Either way, i think moving up or down serves you better then including nodefactory, but i haven't tried such a combo, so i dont know for sure.

  • Tim 225 posts 690 karma points
    Jul 19, 2010 @ 23:13
    Tim
    0

    No,

    Node(123) is querying the in memory XML node cache so no DB hit at all. Linq to Umbraco is effectively calling the XML Cache anyway so should be no different than calling a Linq query (except for he internal caching Linq to Umbraco does)

    T

  • Morten Christensen 596 posts 2773 karma points admin hq c-trib
    Jul 19, 2010 @ 23:19
    Morten Christensen
    0

    Sorry, think i confused new Node with new Document.

    I will try and digg up some samples tomorrow which will hopefully give you some inspiration for a solution (if Aaron doesnt beat me to it).

    • Morten
  • Aaron Powell 1708 posts 3046 karma points c-trib
    Jul 20, 2010 @ 05:35
    Aaron Powell
    3

    You can use the AncestorOrDefault method to do this:

    var items = ctx.packages.Where(x => x.AncestorOrDefault<culture>(c => c.Name == "en-gb") != default(culture));

    This recurses back up from the current instance and looks for a parent which matches the type (culture) and the selector c.Name == "en-gb"


  • Lee 1130 posts 3088 karma points
    Jul 20, 2010 @ 07:44
    Lee
    0

    Awesome - This has just helped me too :)

  • Tim 225 posts 690 karma points
    Jul 20, 2010 @ 11:00
    Tim
    0

    Yes - this is what I tried first however I can't get it to work. I've used your code and it still doesn't find the ancestor??

    I can't work out what the problem is, everything appears to be in order but it's just not finding the ancestor.

    It returns the following error when trying to enumerate the results:

     

    Value cannot be null. Parameter name: attribute

     

    Here's the stack trace:

       at System.Xml.Linq.XAttribute.op_Explicit(XAttribute attribute)
       at umbraco.Linq.Core.Node.NodeDataProvider.<>c__DisplayClass4.<LoadAncestors>b__3(XElement x)
       at System.Linq.Enumerable.Single[TSource](IEnumerable`1 source, Func`2 predicate)
       at umbraco.Linq.Core.Node.NodeDataProvider.LoadAncestors(Int32 startNodeId)
       at umbraco.Linq.Core.DocTypeBase.AncestorOrDefault[TDocType](Func`2 func)
       at umbraco.Linq.Core.DocTypeBase.AncestorOrDefault[TDocType]()
       at Website.Extensions.UserControls.Controls.SelectPackageTest.<GetPackageContentItem>b__0(PackagesFolder packages)
       at System.Linq.Enumerable.WhereEnumerableIterator`1.MoveNext()
       at System.Linq.SystemCore_EnumerableDebugView`1.get_Items()

    Any clues??

    T

     

  • Tim 225 posts 690 karma points
    Jul 20, 2010 @ 15:19
    Tim
    0

    Update:

    I've had to give up with the AncestorOrDefault method as it just doesn't seem to work for me. I'm wondering if this is because I have quote nested document types?

    Anyway I've worked around it for now until I can work out why it's not working or if it's a bug in the implementation of linq2Umbraco.

    For reference I'm now doing this in two separate queries, one which finds the culture folder and then one which uses a combination of linq2Umbraco and node factory to check for the culture folder id in the path of the node.

    Which is similar to my post above.

    T

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Aug 25, 2010 @ 10:56
    Jeroen Breuer
    0

    AncestorOrDefault is working for me.

    Now I'd like to know which approach is faster.

    I've got the followin structure:

    -homeNL

    --chamber

    ---employee1

    ---employee2

    --chamber

    ---content1

    -homeEn

    --etc.

    I've got the following queries: 

    Query 1

    from employeeNode in DigibizDataContext.Employees
    where employeeNode.AncestorOrDefault<Home>().Name == language

    Query 2

    from homeNode in DigibizDataContext.Homes
    where homeNode.Name == language
    from chamberNode in homeNode.Chambers
    from employeeNode in chamberNode.Employees

    In Query 1 I look at the parent of each employee. In Query 2 I look at the home node and go down in tree.

    There will be about 23 employees. Which would be faster?

    Jeroen

  • Jeroen Breuer 4908 posts 12265 karma points MVP 4x admin c-trib
    Aug 25, 2010 @ 11:22
    Jeroen Breuer
    0

    Since Linq2Umbraco has an AncestorOrDefault method is there also a Descendant method which can go down in the nodes?

  • Aaron Powell 1708 posts 3046 karma points c-trib
    Aug 25, 2010 @ 12:24
    Aaron Powell
    0

    No there isn't a infinite depth child option, that'd put a hell of a load of overhead to implement.

    You can get type-agnostic children with the Children property, so you could just use a SelectMany to join them all

Please Sign in or register to post replies

Write your reply to:

Draft