DirectoryContext.Query reflecting different behavior if result set is enumerated before filtering

Mar 23, 2012 at 3:18 PM

Working on displaying users based on their membership status for a specified group.


// Note: ldapContext is a DirectoryContext object
// Returns expected result set [note .ToArray() before .Where()]
User[] nonMembersFromArray = ldapContext.Query<User>().ToArray()
.Where(u => !_group.Members.Contains(u.DistinguishedName)).ToArray(); // Returns all users [note that we do not enumerate before .Where()] User[] nonMembersFromIQueryable = ldapContext.Query<User>()
.Where(u => !_group.Members.Contains(u.DistinguishedName)).ToArray();


I could stick with the first example except I want to page my result set. IEnumerable does not contain a definition for ToPage(). I have also tried converting the Enumerable to a Queryable using .AsQueryable() but receive the follow run-time exception:
"There is no method 'ToPage' on type 'LinqToLdap.QueryableExtensions' that matches the specified arguments" (The argument being an integer for page size.)

Is there some functionality I have overlooked? I don't understand why ldapContext.Query<User>() would yield different results based on enumeration as long as the filter code is the same.

Mar 30, 2012 at 3:48 AM
Edited Mar 30, 2012 at 3:49 AM

ToPage is an extension method that exists only in LinqToLdap.  Unfortunately the Skip / Take convention doesn't really work for LDAP so that's why I added it.  I considered allowing the conversion of the cookie to an int, but that kind of leaves you dead in the water for subsequent pages since you can't calculate it yourself like with Skip and Take once you know the total record count.

As for your Contains issue, you're running into a boundary issue.  Before the ToArray, you're still building the filter to send to the directory which is much more forgiving of casing.  After the ToArray the query has been issued and the results are now in .Net where when you compare strings you need to specify StringComparer or StringComparison if you want to ignore casing.

Mar 30, 2012 at 7:07 PM

On the Contains issue, are you talking about the first or second example? It sounds like you're talking about the first issue which works fine as is--just not page friendly.

After further head banging, I've come to realize that this issue is a result of executing .Contains against an empty collection. So when _group.Members is empty, instead of getting none of the users, I get all of the users possible. (bah!) So until a better design comes to mind, I'll just check the collection size before writing a pagable LINQ.

Thanks mate!