In the last post in this series, I explored using WCF RIA Services to load a set of entities from a self-referencing, hierarchical table in the database, and display the items in a Silverlight TreeView.
In this post, we’ll introduce a lazy loading scheme so that child nodes are fetched dynamically from the server to the client whenever the user expands a parent node in the tree. This technique results in more round-trips to the database overall, but can potentially result in much less data being loaded for a very large tree.
To run this sample, you’ll need to download and install RIA Services, if you haven’t already. We’ll use Visual Studio Beta 2 and the Silverlight 4 Beta for this example. You’ll also need to install WCF RIA Services.
You may want to read and grab the code from the previous post in the series, since we’ll use that code as our starting point.
You can also grab a copy of the final source code for this sample here.
To get started, we’ll open up the solution we created in the previous post, and bring up the TeamDomainService.cs file from the server project. Previously, we sent all of the Team entities from the server to the client in one go, so we only had to offer a very tiny query method to do this:
public IQueryable<Team> GetTeamHierarchy()
{
return this.ObjectContext.Teams;
}
We’ll need richer business logic on the server to handle lazy loading. The scheme we’re going to use is to automatically load the first two levels in the tree when the Silverlight client UI launches. This will allow the TreeView control to display expansion arrows next to the root nodes if they have children. Whenever one of the tree nodes is expanded by the user, we’ll asynchronously call back to the server and load the grandchildren of that node. This works because we’ve already loaded the item’s children, and now we want the TreeView to display expansion arrows next to those children which have children.
To accomplish this, we’ll write two new query methods within our DomainService, one to fetch the top two levels in the tree, and another to fetch the grandchildren of any item.
The first query method finds all of the root-level teams in the hierarchy (those without a parent), and instructs EF to also include the contents of their “ChildTeams” collection. This effectively returns the top-two levels in the hierarchy.
public IQueryable<Team> GetTopLevelTeams()
{
return
from team in this.ObjectContext.Teams.Include("ChildTeams")
where team.ParentTeam == null
select team;
}
The second query method finds all Teams where the parent and grandparent exist, and the ID of the grandparent is equal to the given team’s ID.
public IQueryable<Team> GetGrandchildTeams(int teamID)
{
return
from team in this.ObjectContext.Teams
where team.ParentTeam != null &&
team.ParentTeam.ParentTeam != null &&
team.ParentTeam.ParentTeam.TeamID == teamID
select team;
}
Now we’ll need to alter our Silverlight client code to consume these new query methods, and set up the proper event handling in the TreeView to handle lazy expansion.
Let’s take a look at the code we previously wrote in the MainPage constructor, located in MainPage.xaml.cs:
var query = this.context.GetTeamHierarchyQuery();
var loadOperation = this.context.Load(query);
loadOperation.Completed += (sender, e) =>
{
this.treeView1.ItemsSource =
loadOperation.Entities.Where(t => t.ParentTeamID == null);
};
Because we were previously loading all of the Team entities to the client at the same time, this load logic is fairly simple. We called the GetTeamHierarchy query method on the server, which returned all of the Teams in the database as a flat list. When our asynchronous callback was called, we stripped out only the top-level Team entities, and passed that list as the binding to the TreeView control.
With the introduction of lazy loading, we only want to load the top two levels of the tree to start with, and then we want to start listening for node expansion events in the TreeView, so we can fetch more data from the server as nodes are expanded. Our new load logic becomes:
var query = this.context.GetTopLevelTeamsQuery();
var loadOperation = this.context.Load(query);
loadOperation.Completed += (sender, e) =>
{
this.treeView1.ItemsSource = loadOperation.Entities;
// Register for the expanded event on all of the nodes.
this.AddExpandedHandlers(this.treeView1);
};
This new load logic calls the GetTopLevelTeams query method, which returns the root-level Teams, and also includes their ChildTeams collections. We can now bind the TreeView’s ItemsSource property directly to the LoadOperation’s Entities property, since this collection will only contain the top-level nodes.
Finally, we introduce a new method called AddExpandedHandlers, which walks through all the nodes in the tree and registers for the OnItemExpanded event for each TreeNode. This will allow us to take action whenever the user expands one of the nodes. I won’t include that code here since it isn’t directly related to RIA Services, here, but you can find it in the linked sample code.
So far, we’ve only loaded the top two levels of the tree, so we’ll still need to hook up the code which lazily loads the rest of the nodes. Before we do that, however, let’s run the code and see how the initial load works. When we run the solution, we see:
The top-level nodes are showing up, but we don’t see the drop-down expansion arrows, indicating that the children of these nodes have not been loaded. Something isn’t quite right.
Let’s put a breakpoint on the following line and then debug:
this.treeView1.ItemsSource = loadOperation.Entities;
When this breakpoint is hit, we can look inside the loadOperation and see what’s been returned.
Sure enough, both the Entities and AllEntities collections on the LoadOperations instance only contain the tree top-level nodes, but none of their children. Let’s look at our DomainService query again:
public IQueryable<Team> GetTopLevelTeams()
{
return
from team in this.ObjectContext.Teams.Include("ChildTeams")
where team.ParentTeam == null
select team;
} The query is set up to include the contents of the ChildTeams properties, so we should be seeing the child entities as well on the client. The reason we’re not is because we haven’t told RIA Services that we want it to include the ChildTeams entities when serializing a Team across to the client. To do this, we need to author some metadata on the server-side Team entity. We’ll add a new file in our server project called TeamDomainService.Metadata.cs, and place the following code in the file:
[MetadataTypeAttribute(typeof(Team.TeamMetadata))]
public partial class Team
{
internal sealed class TeamMetadata
{
// Metadata classes are not meant to be instantiated.
private TeamMetadata() { }
[Include]
public EntityCollection<Team> ChildTeams;
}
}
As you can see, we’ve added the [Include] attribute to the ChildTeams collection. This tells RIA Services to include any entities in this collection whenever serializing the parent Team to the client.
Now if we run the solution again and hit our breakpoint, we see:
The Entities collection still only contains three entities, but this time the AllEntities collection contains 6 entities. This is because the Entities collection only contains the entities at the top level of the query, while the AllEntities collection contains all entities that were returned, regardless of whether they are top level entities or child entities. This is why we can bind the TreeView directly to the Entities collection, since it only contains the top-level entities.
If we continue past the breakpoint we’ll see:
This time, the expansion arrows are showing up as expected, because we’ve successfully loaded both the top-level Teams and their direct children.
Now we need to hook up the rest of our lazing loading scheme, to load the grandchildren of any expanded node. To do this, we’ll introduce an OnItemExpanded event handler:
private void OnItemExpanded(object sender, RoutedEventArgs e)
{
TreeViewItem item = sender as TreeViewItem;
if (item != null)
{
item.Expanded -= this.OnItemExpanded;
Team expandedTeam = item.DataContext as Team;
if (expandedTeam != null)
{
var query = this.context.GetGrandchildTeamsQuery(
expandedTeam.TeamID);
var loadOperation = this.context.Load(query);
loadOperation.Completed += (s, args) =>
{
this.AddExpandedHandlers(item);
};
}
}
}
Here, we author another RIA Services load operation, similar to the initial load operation we performed in the constructor. In this case, we want to call the GetGrandchildTeams query method, and pass in the ID of the Team that was expanded. When the asynchronous data load completes, all we have to do is wire up to the OnItemExpanded event on the new nodes. RIA Service will automatically insert the new grandchildren nodes into the correct ChildTeams collections on the client-side Team entities, and the TreeView will automatically detect these new entities and bind to them.
Now we can run our solution again and start expanding nodes. You’ll notice that each time you expand a node, the expansion arrows for its children are not present initially, but they show up a split-second later after their children have been lazily loaded from the server. You can see this more clearly by inserting a sleep within the load callback we authored above:
loadOperation.Completed += (s, args) =>
{
System.Threading.Thread.Sleep(1000);
this.AddExpandedHandlers(item);
};
You’ve now got a lazy-loaded entity TreeView, using WCF RIA Services.
In my previous post, I explored how to display a set of self-referencing, hierarchical entities in a Silverlight TreeView, with the entities loaded from server to client using WCF RIA Services. This example used a static set of data on the server, which was already formatted into a hierarchical class tree.
In this post, we’ll refactor this example to use a live database with an Entity Framework model providing access to the data on the server. We’ll see how this slightly complicates the way that we organize and use the self-referencing entity type, both on the server and on the client.
To get started, you’ll need to download and install RIA Services, if you haven’t already. We’ll use Visual Studio Beta 2 and the Silverlight 4 Beta for this example. You’ll also need to install WCF RIA Services.
You may want to read and grab the code from the previous post in the series, since we’ll use that code as our starting point.
You can also grab a copy of the final source code for this sample here.
Since we’ll be storing our data in in a database, we can delete the classes we previously created in the server project which contain the static in memory data, Node.cs and NodeData.cs. Instead, we’ll create a new Team table in our database, and use Entity Framework to create a Team entity on the server for us.
To start, we’ll need to create our database. In this example, we’ll use the SQL Express features built into Visual Studio. You can also do this using a more advanced version of SQL.
Start by invoking the Add New Item dialog on the web server project, and selecting SQL Server Database under the Data folder. Name the database TreeSample.mdf, and choose to store it under the App_Data folder.
Double-click on the new DB to open it, right-click on the Tables node in Server Explorer, and select Add New Table. We’ll set up a simple table representing a team:
The table has three columns. The TeamID column is set up as an Identity column, and is also the primary key for the table. The ParentTeamID column will contain the ID of the parent Team, if there is one. Since top-level teams have no parent, this column is allowed by null. Finally, the Name column contains the name of the team.
Save the table and name it Team. Lastly, we’ll need to set up a foreign key relationship between Team.TeamID and Team.ParentTeamID. To do this, right-click on the table, and select Relationships. Add a new relationship, then click the elipses on the Tables And Identity Specification field. Set up the relationshp as shown below:
This indicates that the ParentTeamID field on a Team must map to the TeamID of another Team, which sets up the Team table as a hierarchy.
We’ll also add some sample data to the database, so we have something to display in the UI:
The next step is to add an Entity Framework model to the project. Bring up the Add New Item dialog for the server project, select ADO.Net Entity Data Model, and name it TreeModel.edmx. Select to generate the model from a database, then browse to the TreeSample.mdf database file you just created, hit next to page through the wizard, making sure to select the Team table on the Choose Your Database Objects page:
After finishing the wizard, the new Entity Data Model is displayed:
We can see that two Navigation properties have been created, named Team1 and Team2. If we open up the TreeModel.Designer.cs file and scroll down to the Team entity, we can see how these properties are defined:
public EntityCollection<Team> Team1
…
public Team Team2
…
Entity Framework has created these properties for us because of the foreign key relationship we created from ParentTeamID and TeamID, but unfortunately the default property names are not very descriptive. The “Team1” property will actually contain the collection of child teams (teams where the ParentTeamID field is equal to the ID of this team), and the “Team2” property actually refers to the parent of this team. To fix this, rename these fields on the model designer, as shown below:
Now that our model is set up correctly, we can edit our DomainService class to load the team hierarchy using Entity Framework. Find the NodeDomainService.cs file, and rename it to TeamDomainService.cs. Also open up the file and rename the NodeDomainService class to TeamDomainService.
Since we’re using Entity Framework, we’ll make our DomainService class inherit from the LinqToEntitiesDomainService class, passing in the TreeSampleEntities class Entity Framework created for us. Note that you may need to add a reference to the System.Web.DomainService.EntityFramework assembly.
class TeamDomainService : LinqToEntitiesDomainService<TreeSampleEntities>
We’ll also need to change the GetNodeHierarchy method we previously created to return Teams instead of Nodes, using the Entity Framework’s object context to retrieve the data:
public IQueryable<Team> GetTeamHierarchy()
{
return this.ObjectContext.Teams;
}
Note that we’re actually doing something quite different here than what we did in the previous in-memory data example. Previously, our server-side data was already structured into a nice set of hierarchical classes. To send the data to the client, we set up our GetNodeHierarchy method to only return the top-level entities. We also added an [Include] attribute to the ChildNodes collection on our server-side Node class, to tell RIA Services to send child entities along to the client as well.
In fact, we could try to set up our new example the same way, though this would come with some disadvantages. To do this, we’d need to adjust our new DomainService query method to only return the top-level teams, like we were doing in the Node example. This can be done by querying for teams where ParentTeamID field is null:
public IQueryable<Team> GetTeamHierarchy()
{
return this.ObjectContext.Teams
.Where(t => t.ParentTeamID == null);
}
Then, we’d need to tell RIA Services to send the child Teams to the client as well by adding the [Include] attribute to the ChildTeams property on the Team entity, which we could accomplish using a metadata buddy class.
By default, this would not work properly, since Entity Framework delays loading the ChildTeam collection until the first time the property is accessed. Thus, our query would only return the top-level Teams, and all of their ChildTeam collections would be empty. We’d have to force Entity Framework to recursively load all of the child teams on the server before sending all of the data to the client, which would probably result in EF doing multiple round-trips to the database and loading up the entire data set in memory on the server. The downside of this approach is that the client might choose to extend the query it’s sending to the server, for example by filtering down to only the teams which have “Bob” in the name. We’d prefer to allow EF to only fetch those matching teams from the DB and not return any of the other teams at all. This isn’t possible if we force EF to load all Teams on the server so it can set up the object class hierarchy.
A simpler way to architect this is to allow EF to simply return a flat list of Teams, and then use RIA Services to construct this list back into the expected parent-child Team hierarchy on the client. RIA Services will do this for you automatically, by analyzing the Association attribute on the client-side Team entity which associates the ParentTeamID of an entity with the TeamID of another entity:
[Association("Team_Team", "TeamID", "ParentTeamID")]
public EntityCollection<Team> ChildTeams
Moving forward with this plan, our server-side DomainService looks like:
public IQueryable<Team> GetTeamHierarchy()
{
return this.ObjectContext.Teams;
}
This returns a query which will select all of the Teams in a flat list. If, for example, the client extends the query it sends to the server with a “where team.Name contains Bob” clause, EF will only load nodes matching this query.
Before we can run this, we’ll need to fix up our Silverlight project since we’re now using Teams rather than Nodes. First, update the TreeView’s HierarchicalDataTemplate in MainPage.xaml to bind the hierarchy to ChildTeams rather than ChildNodes:
<winControls:HierarchicalDataTemplate ItemsSource="{Binding ChildTeams}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name, Mode=OneWay}" Margin="5,0" />
</StackPanel>
</winControls:HierarchicalDataTemplate>
We also need to edit the client-side code we wrote which loads the query and passes the data to the TreeView, to change from Node to Team:
public partial class MainPage : UserControl
{
private TeamDomainContext context = new TeamDomainContext();
public MainPage()
{
this.InitializeComponent();
var query = this.context.GetTeamHierarchyQuery();
var loadOperation = this.context.Load(query);
loadOperation.Completed += (sender, e) =>
{
this.treeView1.ItemsSource = loadOperation.Entities;
};
}
}
This code creates a query which calls the GetTeamHierarchy method on the server, which will result in all Teams being returned to the client. When the entities are loaded, the entities collection is set as the TreeView’s ItemsSource.
Now when we run the project, we see:
We can see that something isn’t quite right. The child Teams are showing up correctly beneath their parent Teams, but all of the child Teams are also showing up at the root level of the tree. Let’s dig in and see why this is happening, by putting a breakpoint on the line where the Team entities are returned to the client:
this.treeView1.ItemsSource = loadOperation.Entities;
When we run the solution again and break here, we can see that the Entities collection contains all 21 entities, including the top-level Teams and all the child Teams. This makes sense, since the DomainService query method we wrote on the server returns the entire list of Teams as a flat list.
But, if we look at one of the top-level teams that were returned, for example the “Uncle Munsters” team, we see that the entity has a ChildTeams property which will be correctly populated with this Team’s child Teams when the property is accessed the first time. This is why the child Teams are showing up twice in the tree. They are showing up at the root level because we are binding the tree to the entire list of entities returned from the server, which includes all of the Teams, but we’ve also set up the tree’s HierarchicalDataTemplate to navigate through the ChildTeams properties.
To fix this, we need to adjust the collection of Teams that we are binding to the tree. The simple fix is to only bind the tree to the top-level Teams. To do this, we’ll add a where clause on the client:
this.treeView1.ItemsSource =
loadOperation.Entities.Where(t => t.ParentTeamID == null);
Now when we run our solution again, we’ll see the correct tree:

There are many samples floating around showing how to use WCF RIA Services to load data into a GridView, either directly or through the Silverlight DomainDataSource control. Sometimes, however, the entity type you’re working with is self-referencing and hierarchical in nature, for example a Team which contains sub-Teams and has a parent Team. This type of data is usually better off displayed in a tree rather than a grid. This article shows a simple example illustrating how to load self-referencing hierarchical data into a Silverlight TreeView using WCF RIA Services.
In this sample, we’ll keep things simple by defining a small in-memory data store on the server, and defining a POCO style entity to represent the data. In a real-world application, you would most likely have your data in a database, and use something like Entity Framework to create the strongly-typed entities on the server.
We’ll also keep things simple by sending the entire data set to the client in one transaction, so we can focus on what the data looks like on the client, and how to load it into a tree. A future blog post will show how to implement lazy loading, so that child nodes are fetched from the server only when the user expands expands a parent node.
First, let’s look at the finished product. This sample will end up producing a very simple TreeView, like the one shown below. The tree nodes are named based on their position in the hierarchy, to make it easy to determine whether everything came out correctly.
To get started, you’ll need to download and install RIA Services, if you haven’t already. We’ll use Visual Studio Beta 2 and the Silverlight 4 Beta for this example, though this should also work with the VS 2008 and SL3 bits. You’ll also need to install WCF RIA Services.
You can also grab a copy of the full source code for this sample here.
Next, we’ll create a new Silverlight Application project in Visual Studio, called TreeSample. Be sure to check the Enable .NET RIA Services box:
Next, we’ll need to set up our data source on the server. We’ll define a simple entity type called Node.
The node class contains a Name property, which will also be our primary key:
[Key]
public string Name { get; set; } We’ll also add a ParentNode property, which references the name of the parent node.
public string ParentNode { get; set; }
Finally, we’ll set up the collection of child nodes beneath this node:
[Include]
[Association(“ChildNodes”, “Name”, “ParentNode”)]
public List<Node> ChildNodes { get; set; }
Notice that the ChildNodes property contains a couple of attributes. The Association attribute tells RIA Services how a child node is associated with its parent node. In this case, The ParentNode property of a child node must be equal to the Name property of its parent node.
The Include attribute tells RIA Services to include all entities found within this collection when sending the parent entity across to the client. RIA Services will not automatically send the child nodes to the client without this attribute.
Now that the properties are set up, we’ll add a constructor which will help us set up our static server-side data set. The constructor saves the name of the Node as well as its collection of child nodes, and it sets the ParentNode property on each of the children. Here is the entire Node class:
public class Node
{
public Node()
{
}
public Node(string name, IEnumerable<Node> children)
{
this.Name = name;
this.ChildNodes = new List<Node>(children);
if (children != null)
{
foreach (Node child in children)
{
child.ParentNode = this.Name;
}
}
}
[Key]
public string Name { get; set; }
public string ParentNode { get; set; }
[Include]
[Association(“ChildNodes”, “Name”, “ParentNode”)]
public List<Node> ChildNodes { get; set; }
}
Now that the server-side entity is set up, we’ll create the static in-memory data set on the server. In a real application this data would most likely be stored in a database.
internal static class NodeData
{
public static Node[] Nodes = new Node[]
{
new Node("1.1", new Node[]
{
new Node("1.1.1", new Node[]
{
new Node("1.1.1.1", new Node[]
{
})
}),
new Node("1.1.2", new Node[]
{
})
}),
new Node("1.2", new Node[]
{
new Node("1.2.1", new Node[]
{
new Node("1.2.1.1", new Node[]
{
}),
new Node("1.2.1.2", new Node[]
{
}),
}),
}),
new Node("1.3", new Node[]
{
new Node("1.3.1", new Node[]
{
}),
new Node("1.3.2", new Node[]
{
}),
new Node("1.3.2", new Node[]
{
})
}),
};
}
Now our self-referencing hierarchical data is set up on the server, and it’s time to send it to the client. To do this, we’ll add a RIA Services DomainService class on the server.
[EnableClientAccess]
public class NodeDomainService : DomainService
{
public IQueryable<Node> GetNodeHierarchy()
{
return NodeData.Nodes.AsQueryable();
}
}
This simple DomainService contains a GetNodeHierarchy method which returns the root-level nodes as an IQueryable. The class has the EnableClientAccess attribute, which tells RIA Services to create client-side proxies for this DomainService.
Although this method only returns the root-level nodes, all of the child nodes will be sent to the client as well, since we have set up the association from child to parent nodes, and added the Include attribute to the ChildNodes collection, to tell RIA Services that we want to include the child nodes in the data set.
Once we build the application, RIA Services creates a client-side version of the NodeDomainService class, called NodeDomainContext, and a client-side version of the Node entity, still called Node. As one would expect, the client-side Node entity contains the same properties as the server-side entity, including the collection of child nodes.
[Association("ChildNodes", "Name", "ParentNode")]
public EntityCollection<Node> ChildNodes
{
get { … }
}
[DataMember()]
[Key()]
public string Name
{
get { … }
set { … }
}
[DataMember()]
public string ParentNode
{
get { … }
set { … }
}
The ChildNodes property is present on the client-side entity because we specified the [Include] attribute on the server-side version of the property.
Now we can begin building up the UI to display the TreeView. To do this, we’ll open up the MainPage.xaml file in the client project, and drag a TreeView control from the ToolBox onto the designer. If TreeView doesn’t show up in your ToolBox, you’ll need to right-click on the ToolBox, select Choose Items, and find TreeView in the list of available controls.
Once you’ve dragged the TreeView onto the designer, right-click and select Reset Layout/All. This will cause the TreeView to fill the entire space of its parent control.
Now we’ll flip over to code-behind and wire up the data. Open the MainPage.xaml.cs file, and add a NodeDomainContext field to the class:
private NodeDomainContext context = new NodeDomainContext();
Now add the following to the constructor, just after the call to InitializeComponent:
var query = this.context.GetNodeHierarchyQuery();
var loadOperation = this.context.Load(query);
loadOperation.Completed += (sender, e) =>
{
this.treeView1.ItemsSource = loadOperation.Entities;
};
This code first calls the GetNodeHierarchyQuery method to set up a query object which we can use to construct the query we want to send to the server. In our case, we’ll use the default query, but we could optionally extend this query on the client before passing it to the server, to filter to result set.
The GetNodeHierarchyQuery method corresponds to the GetNodeHierarchy method we wrote in our server-side NodeDomainService class, which returns a queryable object containing the root-level nodes.
Next, we’ll call context.Load, and pass in the query object. This initiates an asynchronous load of the data from the server.
Finally, we set up an event handler which will be called when the load operation has completed. The body of the event handler assigns the collection of loaded entities to the ItemsSource collection on the TreeView.
Now we’ll hit F5 to run the solution, and we see this:
Notice that only the top-level nodes are appearing, with no way to expand these nodes and see the child nodes. Something is missing. To determine what the problem is, let’s run the solution again, but this time put a breakpoint on the following line:
this.treeView1.ItemsSource = loadOperation.Entities;
If we poke around in the debugger, we can see that the Entities collection contains three nodes, which is what we expect. Furthermore, if you look at the loadOperation.AllEntities collection, you’ll see that it actually contains 12 nodes. This collection includes all of the nodes loaded from the server, including all of the child nodes, as a flat list. Finally, if you go back to the Entities collection and expand down through one of the Node entities, you’ll see that the ChildNodes collection is empty! This is because child nodes collection will be lazily created on the client first time this property is accessed. To be clear, all of the entities throughout the hierarchy have already been loaded from the server to the client, but the child entity collections within the parent entities will not be set up until the collection property is accessed.
So, this isn’t the reason the child nodes aren’t showing up in the tree. In fact, the child nodes are missing because we haven’t told the TreeView how to bind to them. To do this, we’ll need to add a HierarchicalDataTemplate. Switch back to MainPage.xaml, and add the following code beneath the TreeView control:
<controls:TreeView Name="treeView1">
<controls:TreeView.ItemTemplate>
<winControls:HierarchicalDataTemplate ItemsSource="{Binding ChildNodes}">
<StackPanel Orientation="Horizontal">
<TextBlock Text="{Binding Name, Mode=OneWay}" Margin="5,0" />
</StackPanel>
</winControls:HierarchicalDataTemplate>
</controls:TreeView.ItemTemplate>
</controls:TreeView>
This tells the TreeView that the data set is hierarchical in nature. Furthermode, it tells the tree to look for child nodes within the ChildNodes property on the entity type, and to use the Name property as the display text for the tree nodes.
Note: You’ll need to add a reference to the System.Windows.Controls namespace at the top of your xaml:
xmlns:winControls="clr-namespace:System.Windows;assembly=System.Windows.Controls"
Now when we run the solution again, we’ll see all the child nodes in the tree:

Working on .NET RIA Services has given me a new appreciation for LINQ. In fact, I even got up the nerve to try writing my own LINQ provider. Ouch, not easy. Authoring a LINQ provider is a bit like writing a compiler, requiring you to parse a myriad of complex LINQ expressions, each of which can contain a never-ending array of sub-expressions and clauses.
It’s well worth the investment to create a fully functional LINQ provider when your back-end data source is a relational database or a rich data abstraction layer like Entity Framework or nHibernate. When you’re working with a limited data provider which can’t support most complex query operations anyway, like a simple WCF service for example, the overhead in authoring the LINQ provider might be much more than it’s worth.
Enter LinqLite. LinqLite is a small library I put together which takes care of parsing the nasty LINQ expressions for you. In return, it hands back a much more simplistic set of clauses describing the original query. These clauses maintain the original query expression order, and give you the data you need to know in an easy-to-absorb format. You still need to write code to handle the query clauses for your specific data provider, but doing so is dramatically simpler and more contained than handling the full set of LINQ expressions.
The catch is that LinqLite does not support the full set of LINQ syntax. It does not support joins, for example. However, it does support the core set of LINQ expressions that you’re likely to find yourself using 90% of the time for simple queries over a simple data provider. And again, using LinqLite is a much less expensive way to expose an IQueryable over your back-end data provider.
In this blog post, I’ll describe the subset of syntax supported by LinqLite, and walk through an example implementation.
Supported Syntax
By default, LinqLite supports a limited set of LINQ syntax:
1. LinqLite supports the following LINQ expression types: Where, OrderBy, ThenBy, GroupBy, Skip, Take, Count
2. A LinqLite query may only operate over a single entity type. For instance the following query can be handled by LinqLite since it only includes a single entity type, Customer:
var result = this.customers.Where(c => c.Gender == "M");
3. The OrderBy, ThenBy, and GroupBy expressions are limited to accepting the name of a property on the entity and cannot take an IComparable parameter. For example:
var result = this.customers.OrderBy(c => c.Name).ThenBy(c => c.Gender);
4. The Where expression is limited to the following operator types: ==, !=, >, >=, <, <=, StartsWith, EndsWith, Contains
In addition, a Where expression must compare an entity property with another property on the same entity, an entity property with a constant value, or a constant value with an entity property. For example, the following queries can all be handled by LinqLite:
var result = this.customers.Where(c => c.Issues > 2);
var result = this.customers.Where(c => c.FirstName != c.LastName);
var result = this.customers.Where(c => “M” == c.Gender);
var result = this.customers.Where(c => c.Name.StartsWith(“Fr”));
If the query expression passed to the LinqLite query provider does not fall within the above constraints, LinqLite will throw an InvalidQueryException.
If you need support for additional LINQ syntax, it may be simpler for you to modify the LinqLite library to add this support, rather than writing your own LINQ provider from scratch.
Example Implementation
The following sample shows an implementation of LinqLite for use within a .NET RIA Services project (naturally). To keep the example very simple, the back-end data source is a simple hardcoded array of data. The example code shows how to parse each of the query clauses returned from LinqLite and then return the correct data from the original data set, based upon the parameters provided in the original query.
The full sample code can be downloaded here.
This sample code requires:
- VS2008 SP1 (Which includes SQL Express 2008)
- Silverlight 3 RTM
- .NET RIA Services July '09 Preview
As with most RIA Services projects, our sample project contains an ASP.Net Web Server project and a Silverlight client project, as well as the LinqLite library project. RIA Services allows the Silverlight client to pass LINQ queries back to the server over a WCF channel, which then grabs and returns the requested data. Since our back-end data store on the server is simply a hard-coded array of data, we need a way to break apart the query received from the client, and then filter and sort the data accordingly. Of course, we could use LINQ to Objects to query the data array directly, but for demo purposes we’re ignoring that and executing the query manually.
To start with, we need to create our strongly typed entities on the server. In our case we’re using two entities, SuperEmployee and SuperEmployeeOrigin, which are simple POCO classes.
Next, we create the back-end data set, which is just an array. This is stored in the static SuperEmployeeData class.
private static SuperEmployee[] superEmployees = new SuperEmployee[]
{
new SuperEmployee(0, "Foo Basdasdar", "Male", "Human", 123, "DC", "123"),
new SuperEmployee(1, "Cool Name", "Male", "Human", 982, "DC", "first appears in Batman #16"),
new SuperEmployee(2, "Alfred E. Neuman", "Male", "Human", 518, "Ec", "first appears in MAD #21"),
new SuperEmployee(3, "Angel", "Male", "Mutant", 1061, "Marvel", "first appears in X-Men #1"),
...
The SuperEmployeeData class returns two status properties, a SuperEmployees property which returns the entire array of SuperEmployees, and a SuperEmployeeOrigins property which returns an array of all the unique SuperEmployee origins.
Now that our entities and data are in place, it’s time to author our RIA DomainService class. In our simple example, the DomainService class will return a set of read-only query methods, each of which returns an IQueryable<SuperEmployee> or IQueryable<SuperEmployeeOrigin>. RIA Services will expose these query methods to the client, allowing the client to write LINQ queries against our data. The outline of our DomainService looks like:
[EnableClientAccess]
public class SuperEmployeeDomainService : DomainService
{
/// <summary>
/// Gets a query to match all super employees.
/// </summary>
public IQueryable<SuperEmployee> GetSuperEmployees()
/// <summary>
/// Gets query to match a single super employee with the given ID.
/// </summary>
public IQueryable<SuperEmployee> GetSuperEmployee(int employeeID);
/// <summary>
/// Gets a query to return all origins.
/// </summary>
public IQueryable<SuperEmployeeOrigin> GetOrigins();
}
Each of these methods needs to return an instance of a LINQ Query Provider which implements IQueryable<T>. The query provider will be called automatically when the query is composed and executed. The query provider will then need to parse the query, gather and return the requested data.
To go any farther we need to create our custom LINQ Query Provider, based on LinqLite. In our case we will actually create two Query Providers, one which knows how to query against SuperEmployees, and another to query for SuperEmployeeOrigins.
Because we’re creating two query provider classes, it makes sense to put as much of the code as possible into a base class. So, we’ll create a class in our server project called QueryableEntityBase, which inherits from the LinqLiteQueryable<T> class. LinqLiteQueryable<T> implements the IOrderedQueryable<T> interface, and does all the hard work of parsing the original query expression and translating it into a simple set of query clauses for your code to consume.
Then, we’ll create a couple of specific implementations of QueryableEntityBase for the two types of entities we want to be able to query against, QueryableSuperEmployee and QueryableSuperEmplolyeeOrigin.
The next step on our journey is to override the ExecuteQuery method within our QueryableEntityBase class. This method gets called after LinqLite has parsed the query, and it’s where you will inspect the query clauses returned from LinqLite and do whatever work needs to be done to return the requested data. Our implementation looks like:
protected override object ExecuteQuery(QueryExpression simplifiedQuery)
{
// Start off with the entire data set.
T[] resultSet = this.GetInitialResultSet();
// Iterate through the clauses.
foreach (QueryClause clause in simplifiedQuery.Children)
{
if (clause.ClauseType == QueryClauseType.Skip)
{
resultSet = this.HandleSkip((SkipClause)clause, resultSet);
}
else if (clause.ClauseType == QueryClauseType.Take)
{
resultSet = this.HandleTake((TakeClause)clause, resultSet);
}
else if (clause.ClauseType == QueryClauseType.OrderBy ||
clause.ClauseType == QueryClauseType.ThenBy)
{
resultSet = this.HandleOrderBy((OrderByClause)clause, resultSet);
}
else if (clause.ClauseType == QueryClauseType.Where)
{
resultSet = this.HandleWhere((WhereClause)clause, resultSet);
}
else if (clause.ClauseType == QueryClauseType.Count)
{
return resultSet.Length;
}
else if (clause.ClauseType == QueryClauseType.GroupBy)
{
throw new ArgumentException("Not supported.");
}
}
return resultSet;
}
Notice that this method first calls GetInitialResultSet, which is an abstract method which needs to return the full dataset. The QueryableSuperEmployee class implements this by simply returning the full collection of SuperEmployees:
protected override SuperEmployee[] GetInitialResultSet()
{
return SuperEmployeeData.SuperEmployees;
}
After obtaining the original data set, the ExecuteQuery method begins looping through each of the simple QueryClauses returned from LinqLite, which can be of type WhereClause, OrderByClause, ThenByClause, GroupByClause, SkipClause, TakeClause, or CountClause. In each case, it calls a special handler method which will return a new subset of the data matching the parameters in the query clause.
For example, the SkipClause class looks like:
public class SkipClause : QueryClause
{
public int Size
{
get;
}
}
This simple class just returns the requested number of items to skip.
And the skip clause handler looks like:
protected virtual T[] HandleSkip(SkipClause skip, T[] dataSet)
{
if (skip.Size <= 0)
{
return dataSet;
}
else if (skip.Size >= dataSet.Length)
{
return new T[] { };
}
else
{
T[] resultSet = new T[dataSet.Length - skip.Size];
for (int i = 0; i < resultSet.Length; ++i)
{
resultSet[i] = dataSet[i + skip.Size];
}
return resultSet;
}
}
Since our back-end data source is a simple array, this method simply skips past the requested count and returns the rest of the items in the array, if any.
Perhaps it’s a bit more interesting to look at how we handle an OrderBy expression. For instance, consider the case where the original query looks like:
var x = this.superEmployees.OrderBy(s => s.Gender);
For this query, LinqLite will hand you an OrderByClause object with the PropertyName property set to “Gender”, and the Direction property set to OrderByDirection.Ascending. Since the value of the PropertyName property is a simple string, we need to map this back to the actual property on the SuperEmployee entity which we’re supposed to order by.
Our sample code does this by calling to an abstract method called GetOrderByComparisonDelegate, which needs to return a Comparison<T> that, when called, will perform the comparison against the appropriate entity property. The rest of the code then performs a simple bubble sort to do the actual sorting.
protected virtual T[] HandleOrderBy(OrderByClause orderBy, T[] dataSet)
{
Comparison<T> comparer = GetOrderByComparisonDelegate(orderBy);
if (comparer != null)
{
// Perform a simple, inefficient bubble sort for demo purposes.
for (int i = 0; i < dataSet.Length; ++i)
{
for (int j = i + 1; j < dataSet.Length; ++j)
{
int comparison = comparer(dataSet[i], dataSet[j]);
… (ommitted)
}
}
}
return dataSet;
}
The QueryableSuperEmployee class implements GetOrderByComparisonDelegate as:
protected override Comparison<SuperEmployee> GetOrderByComparisonDelegate(OrderByClause orderBy)
{
Comparison<SuperEmployee> comparer = null;
switch (orderBy.PropertyName)
{
case "EmployeeID":
comparer = delegate(SuperEmployee x, SuperEmployee y) {
return x.EmployeeID.CompareTo(y.EmployeeID); };
break;
case "Gender":
comparer = delegate(SuperEmployee x, SuperEmployee y) {
return string.Compare(x.Gender, y.Gender, StringComparison.OrdinalIgnoreCase); };
break;
case "Issues":
comparer = delegate(SuperEmployee x, SuperEmployee y) {
return Nullable.Compare<int>(x.Issues, y.Issues); };
break;
case "Name":
comparer = delegate(SuperEmployee x, SuperEmployee y) {
return string.Compare(x.Name, y.Name, StringComparison.OrdinalIgnoreCase); };
break;
case "Origin":
comparer = delegate(SuperEmployee x, SuperEmployee y) {
return string.Compare(x.Origin, y.Origin, StringComparison.OrdinalIgnoreCase); };
break;
case "Publisher":
comparer = delegate(SuperEmployee x, SuperEmployee y) {
return string.Compare(x.Publishers, y.Publishers, StringComparison.OrdinalIgnoreCase); };
break;
case "Sites":
comparer = delegate(SuperEmployee x, SuperEmployee y) {
return string.Compare(x.Sites, y.Sites, StringComparison.OrdinalIgnoreCase); };
break;
default:
break;
}
return comparer;
}
The Where clause is handled in much the same way, by providing specific implementations for SuperEmployee and SuperEmployeeOrigin that know how to filter against specific properties on those entities, with two caveats. First, a LinqLite Where-clause is allowed to compare two properties on the entity, or it can compare an entity property against a constant. The QueryableEntityBase class handles this by first checking what type of value we find on the right and left-hand sides of the Where operator, and then calling either FilterOnProperties or FilterOnConstant, as required:
private T[] HandleWhere(WhereClause where, T[] dataSet)
{
WhereFilterPropertyMember p1 = where.LeftHandSide as WhereFilterPropertyMember;
if (p1 != null)
{
WhereFilterPropertyMember p2 = where.RightHandSide as WhereFilterPropertyMember;
if (p2 != null)
{
// Both sides are entity properties.
return this.FilterOnProperties(p1, p2, where.Operator, dataSet);
}
else
{
WhereFilterConstantMember c2 = where.RightHandSide as WhereFilterConstantMember;
if (c2 != null)
{
// Left side is a property, right side is a constant.
return this.FilterOnConstant(p1, c2, where.Operator, dataSet);
}
}
}
else
{
WhereFilterConstantMember c1 = where.LeftHandSide as WhereFilterConstantMember;
if (c1 != null)
{
WhereFilterPropertyMember p2 = where.RightHandSide as WhereFilterPropertyMember;
if (p2 != null)
{
// Left side is a constant, right side is a property.
return this.FilterOnConstant(c1, p2, where.Operator, dataSet);
}
}
}
// A comparison with two constants or something unsupported.
throw new InvalidQueryException("Invalid where query parameters");
}
The FilterOnProperties and FilterOnConstant methods set up a delegate which knows how to extract the value from the appropriate entity property at the appropriate time, much like we did with the OrderBy handler described above.
The second caveat is that a LinqLite Where-clause can support multiple types of comparison operators, such as equals, not-equals, greater than, StartsWith, EndsWith, etc.
The QueryableEntityBase class takes care of handling these operators based on the type of the entity property being filtered against. For instance, for a string property, the comparison looks like:
private static bool StringCompare(string s1, string s2, WhereFilterOperator @operator)
{
// Handle nulls.
if (s1 == null && s2 == null)
{
return @operator == WhereFilterOperator.Equals ||
@operator == WhereFilterOperator.LessThanOrEqualTo ||
@operator == WhereFilterOperator.GreaterThanOrEqualTo;
}
if (s1 == null || s2 == null)
{
return @operator == WhereFilterOperator.NotEquals;
}
switch (@operator)
{
case WhereFilterOperator.Equals:
return s1 == s2;
case WhereFilterOperator.NotEquals:
return s1 != s2;
case WhereFilterOperator.GreaterThan:
return string.Compare(s1, s2) > 0;
case WhereFilterOperator.GreaterThanOrEqualTo:
return string.Compare(s1, s2) >= 0;
case WhereFilterOperator.LessThan:
return string.Compare(s1, s2) < 0;
case WhereFilterOperator.LessThanOrEqualTo:
return string.Compare(s1, s2) <= 0;
case WhereFilterOperator.StartsWith:
return s1.StartsWith(s2);
case WhereFilterOperator.EndsWith:
return s1.EndsWith(s2);
case WhereFilterOperator.Contains:
return s1.Contains(s2);
default:
return false;
}
}
Though it’s not shown here, the WhereFilterPropertyMember class also exposes a CaseSensitivity property, allowing you to handle case-sensitive queries such as:
var x = this.superEmployees.Where(s => s.Name.ToLower().StartsWith(“s”));
Putting all of this together, we’ve now got a set of classes which allow us to author LINQ queries against the SuperEmployee data type or the SuperEmployeeOrigin data type, provided that the expressions used in the query are constrained to the set of clauses and operations supported by LinqLite. Whenever a query is executed, LinqLite will parse the original LINQ expression, throw an exception if it finds something it cannot support, and otherwise hand back the query as a set of QueryClauses. These lightweight classes are relatively easy to parse and apply to your data-set.
Of course, LinqLite supports all of the query expression types that RIA Services queries are allowed to use. We can now complete our RIA Servcies DomainService, which exposes the queryable objects from the server to the client:
[EnableClientAccess]
public class SuperEmployeeDomainService : DomainService
{
// Reference to the linq provider for our super employee service.
private QueryableSuperEmployee superEmployeeContext = new QueryableSuperEmployee();
// Reference to the linq provider for our super employee origins service.
private QueryableSuperEmployeeOrigin superEmployeeOriginContext = new QueryableSuperEmployeeOrigin();
public IQueryable<SuperEmployee> GetSuperEmployees()
{
return this.superEmployeeContext;
}
public IQueryable<SuperEmployee> GetSuperEmployee(int employeeID)
{
return this.superEmployeeContext.Where(emp => emp.EmployeeID == employeeID);
}
public IQueryable<SuperEmployeeOrigin> GetOrigins()
{
return this.superEmployeeOriginContext;
}
}
}
This means that I can now run my sample app, and my Silverlight UI can query against my server data to its heart’s content. The UI below contains a TextBox which allows me to enter an Origin value. When I type in “Human”, the UI sends a new query back to the server which asks for all SuperEmployees where the Origin property starts with “Human”, and displays only the matching rows in the grid:
References
The SuperEmployee placement service example used in this sample is a modification of the code written by Brad Abrams in his extremely prolific series on RIA Services.
The LinqLite query provider is a heavily modified version of the LINQ To TerraServer example on MSDN.
Disclaimers
Much of the code in the MyApp and MyApp.Web projects was written by Brad Abrams originally, so don’t flame me for the millions of StyleCop violations!
On the eve of 2010, I’m pretty sure the best thing for everybody at this point is for me to introduce a new blog to the world. There, is that better? I tried to wait out this whole “blogging” fad, but I am weak and a little vain, so here you are.
A little about me. First of all, I really don’t like fragmented sentences. I don’t.
But seriously… I’m currently a Development Manager within the Microsoft Developer Division, managing the dev teams for .NET RIA Services, and the Managed Extensibility Framework. I’m in my 12th year as a dev at Microsoft, and previously worked on Dynamics NAV, Systems Center, Visio, and even Windows 2000. I’m also the creator of StyleCop, and thus am much loathed throughout the C# world. In my spare time I still occasionally dabble in StyleCop and release updates every so often.
I love to travel, and my family and I recently completed a three-year stint in Copenhagen. I’ve got the best kids in the world, and my favorite color is blue.
I hope you enjoy the blog! If not, here are some other blogs you’ll probably like a lot better:
| Brad Abrams | My boss and co-author of the Framework Design Guideline bibles |
| Nikhil Kothari | RIA Services architect extraordinaire, and one of the early ASP.Net devs |
| Dinesh Kulkarni | RIA Services lead PM, and resident Linq to SQL expert |
| Scott Guthrie | Needs no introduction |