public ResourceGraphNode( object obj, ApiResource resource, ResourceGraphPathSet includePaths, int graphDepth, string propertyName = null) { obj.ThrowIfNull(nameof(obj)); resource.ThrowIfNull(nameof(resource)); includePaths.ThrowIfNull(nameof(includePaths)); Key = new ResourceGraphNodeKey(obj, resource); SourceObject = obj; IncludePaths = includePaths; PropertyName = propertyName; Resource = resource; GraphDepth = graphDepth; Relationships = Resource.Relationships .Select(r => new { key = r.Name, value = new ResourceGraphRelationship(r, includePaths.MatchesProperty(r.PropertyName), SourceObject.GetValueOfProperty(r.PropertyName)) }).ToDictionary(a => a.key, a => a.value); }
private void Build( object obj, ApiResource resource, ResourceGraphPathSet includePaths, int depth, string propertyName = null) { if (obj == null) { // end of the line - obj will be a leaf node in our graph return; } if (obj.IsCollectionType()) { foreach (var o in (IEnumerable)obj) { Build(o, resource, includePaths, depth); } return; } // keys (type & id pair) uniquely identifier each resource in a compount document var key = new ResourceGraphNodeKey(obj, resource); var existingNode = _nodes.ContainsKey(key) ? _nodes[key] : null; if (existingNode == null) { // this is the first time we have seen this node existingNode = new ResourceGraphNode(obj, resource, includePaths, depth, propertyName); } else if (!existingNode.IncludePaths.Equals(includePaths)) { /* * We've seen this node before but this time it's include paths are different * to those we are currently applying.This is usually because a resource * is present at two or more distinct locations within an object graph. * * We must combine our current include paths with those of the already built node * and rebuild to capture any new relationships to include. */ includePaths = existingNode.IncludePaths.Union(includePaths); existingNode = new ResourceGraphNode(obj, resource, includePaths, depth, propertyName); } else if (existingNode.GraphDepth > depth) { /* * We've seen this node before but this time we're closer to the root node. * We must update it's depth to the new lower value so we can later on identify * nodes that should be serialized into the data section. */ existingNode.GraphDepth = depth; return; } else { return; } _nodes[key] = existingNode; foreach (var r in resource.Relationships .Where(r => includePaths.MatchesProperty(r.PropertyName))) { // Build a new set of include paths with the relationship property as the root var childIncludes = includePaths.PathSetForChildProperty(r.PropertyName); if (r.Kind == RelationshipKind.HasMany) { var collection = (IEnumerable)obj.GetValueOfProperty(r.PropertyName); if (collection != null) { foreach (var o in collection) { // Add the relationship member to the graph Build(o, r.RelatedResource, childIncludes, depth + 1, r.PropertyName); } } } else { var o = obj.GetValueOfProperty(r.PropertyName); if (o != null) { // Add the relationship member to the graph Build(o, r.RelatedResource, childIncludes, depth + 1, r.PropertyName); } } } }