It is naturally to use Expand
in a Linq
query to get children collection under an entity, as showed in the following example.
using System.Collections.Generic; using System.Linq; public IEnumerable<User> GetMembersByGroup(string groupIdentifier) { var team = this.dataContext // each Team has a MemberUsers collection .Teams.Expand(t => t.MemberUsers) .Where(t => t.Id == groupIdentifier) .Single(); return team.MemberUsers.ToList(); }
The above code would work as long as the size of the collection is small and within server paging size of the data service (- see this blog). In order to get result from all paged collection, the following example is using
DataServiceCollection<T>.Load
method.Note: The service reference needs have
UseDataServiceCollection
enabled in .datasvcmap
configuration which should be supported by .NET 3.5 SP1 and 4. In other case, rather than System.Data.Services.Client.DataServiceCollection
, the System.Collections.ObjectModel.Collection
won't have Continuation
.using System.Collections.Generic; using System.Data.Services.Client; using System.Linq; public IEnumerable<User> GetMembersByGroup(string groupIdentifier) { var team = this.dataContext // expand both MemberUsers and Children teams .Teams.Expand("MemberUsers,Children/MemberUsers") .Where(t => t.Id == groupIdentifier) .AsEnumerable() .FirstOrDefault(); if (team == null) { return new List<User>(); } DataServiceCollection<User> users = team.MemberUsers; while (users.Continuation!= null) { users.Load(this.dataContext.Execute(users.Continuation)); } return users; }
The
DataServiceCollection<T>
requires a type T
. For Query Projction
with anonymous (or Tuple
) type, the query can load data but may not support Continuation
.Another similar solution is sending data query to data service with
GetContinuation
.using System.Collections.Generic; using System.Linq; public IEnumerable<User> GetMembersByGroup(string groupIdentifier) { var dataQuery = from t in this.dataContext.Teams where t.Id == groupIdentifier from u in t.MemberUsers select u; var users = this.dataContext.GetAll(dataQuery); return users; }
Since only navigation query supports
join
operation, the query must be on the primary key (as the Id
in above code); otherwise, use another query with SingleOrDefault
or FirstOrDefault
in prior to get the identifier. Also, GetAll
method should support Query Projection
, so that GetAll(dataQuery)
can be used, instead of GetAll((DataServiceQuery)dataQuery)
.In order to query all users by a name (which is not a navigation query), the following data query projects the result to a collection of an anonymous typed objects, so that we can get identifiers for navigation queries later.
using System.Collections.Generic; using System.Linq; public IEnumerable<string> GetUsersByName(string userName) { var dataQuery = from u in this.Users where u.Name == userName select new { { Id = u.Id, Alias = u.Alias } var result = this.dataContext.GetAll(dataQuery); var users = result.Select(a => a.Id); return users; }
Here is the source of
GetAll
extension (with support of Query Projection
):using System.Collections.Generic; using System.Data.Services.Client; using System.Linq; public static IEnumerable<T> GetAll<T>(this DataServiceContext dataContext, IQueryable<T> dataServiceQuery) { QueryOperationResponse<T> response = (QueryOperationResponse<T>) ((DataServiceQuery)dataServiceQuery).Execute(); DataServiceQueryContinuation<T> continuation = null; do { if (continuation != null) { response = this.dataContext.Execute(continuation); } foreach (var result in response) { yield return result; } continuation = response.GetContinuation(); } while (continuation != null); }