/// <summary> /// Traverse the result object graph, flattening into a single list. Note that this /// flattening must maintain relative ordering for the top level elements, since the client might /// have passed an order expression. /// </summary> /// <typeparam name="TEntity">The root entity Type of the query.</typeparam> /// <param name="list">The list of entities to add to the results.</param> /// <param name="rootResults">The root entities. The value can be <value>null</value> if the list of root results is already known.</param> /// <param name="includedResults">The included entities.</param> /// <param name="visited">Map used for the lifetime of the flattening to ensure that each entity /// is added to the results only once.</param> /// <param name="domainServiceDescription">description for the DomainService.</param> private static void FlattenGraph <TEntity>(IEnumerable list, List <TEntity> rootResults, List <object> includedResults, HashSet <object> visited, DomainServiceDescription domainServiceDescription) { if (list == null) { return; } // Queue used for breadth-first scan Queue <IEnumerable> resultsQueue = new Queue <IEnumerable>(); resultsQueue.Enqueue(list); IList result = rootResults; while (resultsQueue.Count > 0) { foreach (object entity in resultsQueue.Dequeue()) { if (!visited.Add(entity)) { continue; } // If we already know the root results, then we don't need to copy them over to a new list. if (result != null) { result.Add(entity); } // make sure to use the correct entity Type, taking inheritance into account Type entityType = domainServiceDescription.GetSerializationType(entity.GetType()); PropertyDescriptorCollection properties = MetaType.GetMetaType(entityType).IncludedAssociations; foreach (PropertyDescriptor pd in properties) { IEnumerable value = null; if (typeof(IEnumerable).IsAssignableFrom(pd.PropertyType)) { value = (IEnumerable)pd.GetValue(entity); } else { // singleton association object singleton = pd.GetValue(entity); if (singleton != null) { value = new object[] { singleton }; } } if (value != null) { resultsQueue.Enqueue(value); } } } // From now on, add everything to includedResults. result = includedResults; } }