Question

I have a wcf data service that has a service "Nodes" that return the following entity.

[DataServiceKey("NodeId")]
public class Node
{
    public int NodeId { get; set; }

    public IQueryable<Node> SubNodes
    {
        get { return new NodeRepository().GetNodes(this.NodeId); }
    }

}

which gives me a fully recursive OData service. I get all root nodes with http://www.test.com/api/Nodes, a single node with http://www.test.com/api/Nodes(123), it's subnodes with http://www.test.com/api/Nodes(123)/SubNodes, a single subnode with http://www.test.com/api/Nodes(123)/SubNodes(234), the subnode's subnodes with http://www.test.com/api/Nodes(123)/SubNodes(234)/SubNodes and so on.

The question is how to consume this data in code.

I can use

var context = new MyServiceV1.TheServiceContext(new Uri(dataUrl));
var nodes = context.Nodes;

to get the first level of nodes. Then if I pass the context and my nodes to this method I can get the subnodes of the first level nodes.

private void RecurseNodes(TheServiceContext context, IEnumerable<Node> nodes)
{
    foreach (var node in nodes)
    {
        var subNodes = dataContext.LoadProperty(node, "SubNodes") as QueryOperationResponse<Node>;
        RecurseNodes(dataContext, subNodes);
    }
}

The above LoadProperty call will generate the correct url (/api/Nodes(123)/SubNodes) but when it tries to load the subnodes of the next level (i.e. the node with id 234) it uses the url /api/Nodes(234)/SubNodes. What it should use is /api/Nodes(123)/SubNodes(234)/SubNodes.

Do I have to start generating the urls myself or can I instruct the datacontext to accomplish this?

SOLUTION: The problem is in the OData-feed, since /api/Nodes does not return all Node's, only the root nodes. This is how I solved it without touching the OData-feed:

private void RecurseNodes(IEnumerable<Node> nodes, NCContentServiceContext dataContext, string baseUrl)
{
    foreach (var node in nodes)
    {
        var url = baseUrl + "(" + node.NodeId + ")/SubNodes";

        var subNodes = dataContext.Execute<Node>(new Uri(url, UriKind.Relative)).ToList();

        RecurseNodes(subNodes, dataContext, url);
    }
}

and the caller is

var context = new MyServiceV1.TheServiceContext(new Uri(dataUrl));
RecurseNodes(context.Nodes, context, "/Nodes");
Was it helpful?

Solution

I'm not sure why you need the recursive URI, but the URL here generated by LoadProperty() is correct from an OData perspective.

In OData world, every entity has a unique entity id. For instance, /api/Nodes(123) and /api/Nodes(234) are the entity ids of the two entities. So the query /api/Nodes(123)/SubNodes(234) is really reaching for the entity /api/Nodes(234). And when you ask for /api/Nodes(123)/SubNodes(234)/SubNodes, it's the same as /api/Nodes(234)/SubNodes. So LoadProperty is doing the right thing here.

If you still need to build the recursive URI, you'll have to build it yourself.

OTHER TIPS

According to the OData specification /api/Nodes return all entities of type Node. So, /api/Nodes(234) will return node #234. You need to develope a custom method to return the root nodes.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top