ContentManager Get Single Page without List.FirstOrDefault

Topics: General, Troubleshooting, Writing modules
Nov 8, 2011 at 8:44 AM

How can i retrieve a single page of a specific type without using the following code:

_orchardServices.ContentManager.Query("ContentTypeName").List().FirstOrDefault();

I find my self very often using this code but i don't think it's very efficient. Is there a better alternative?

thanks!

Nov 8, 2011 at 8:47 AM

Ok i found out a alternative but i don't know if it's faster of better:

_orchardServices.ContentManager.Query(VersionOptions.Published,"ContentTypeName").Slice(0,1).FirstOrDefault()

Developer
Nov 8, 2011 at 8:50 AM

I too use the first approach. It's a bit confusing that sometimes if the content item specified in the query is not found it gets created too (I haven't isolated the issue yet.)

Nov 8, 2011 at 10:21 AM

Slice is the equivalent of Skip/Take in ordinary Linq queryables; as in, it properly pages the query and therefore limits the number of records queried instead of returning everything. So yes, Slice is hugely more efficient than List, especially if you have a lot of content.

Piedone: I've never been aware of Query methods actually creating records, are you sure about this? 

Nov 8, 2011 at 10:48 AM

Ok cool to know that Slice is int this case faster then List.FirstOrDefault(). The strange thing is that in the core of orchard i see very often .List().FirstOrDefault();.

So are you really sure that it's faster then Slice(0,1).FirstOrDefault() for retrieving only 1 contentItem?

Nov 8, 2011 at 11:20 AM

Yes; it's in the documentation, for one thing.

Can you give an example of where List().FirstOrDefault() happens in core? I suspect it might be somewhere that only a single record will be returned anyway; if not, it's something you could raise workitems for since it's a clear and easy optimisation!

Nov 8, 2011 at 2:15 PM

o i think i didn't make myself clear. I also want to return just a single contentItem in my result. But i wondered if there's a better way to return just a single contentitem based on a criteria instead of using List().FirstOrDefault()

Nov 8, 2011 at 2:28 PM

You definitely made yourself clear :) Can you give an example of where you've seen the inoptimal version in Orchard source code?

Developer
Nov 8, 2011 at 6:19 PM

Randompete: although the effect seems to be this, after today's investigation I found out that this was caused by the part whose records appeared being attached to the User type. Now this caused part records to appear without creating them explicitly. However I think this is by design.

Nov 8, 2011 at 6:41 PM

yes that's by design and i experienced this to be very handy.

Well for example the method SendLostPasswordEmail in Orchard.Users.Services.UserService has the line:

var user = _contentManager.Query<UserPart, UserPartRecord>().Where(u => u.NormalizedUserName == lowerName || u.Email == lowerName).List().FirstOrDefault();

i'm just interested but have not tested it myself if the following edited line creates a more efficient query?:

var user = _contentManager.Query<UserPart, UserPartRecord>().Where(u => u.NormalizedUserName == lowerName || u.Email == lowerName).Slice(0,1).FirstOrDefault();

Nov 8, 2011 at 10:19 PM

In the case of that User query there will only ever be one record returned because there's only one user with any given name. So it won't make any difference using Slice, in fact it could be less efficient because the paging SQL will be executed unnecessarily. Can you find any examples that are not guaranteed to be single record queries anyway?

Nov 9, 2011 at 6:07 AM

yes but the query .List().FirstOrDefault(); seems a little bit strange because you already know it's gonna be one result. But i tested this and the query is very efficient so it doesn't mather. Slice(0,1).FirstOrDefault() is indeed slower then just List().FirstOrDefault();

thanks!

Nov 9, 2011 at 12:07 PM

List() returns an IEnumerable, and what the code is after is an object ... you know the query is returning either a single object or nothing, so FirstOrDefault() makes perfect sense to get an object from the list (how else would you do it?)

Just remember that for any query with an unknown number of results, Slice will be faster.