Thursday, August 7, 2008

Using ListView and LINQ to display multi-level relationships



This is cool! Assume we have a database that has a three-level relationship. We have a Product, Install, and Document table. Products have installations and installations have related documents for them. This means our Document table has a InstallId and our Install table has a ProductId. This is all standard relationship stuff so hopefully you are following this.

Now assume we want to use LINQ and the ListView web control to display hierarchical data and we want full control over the HTML generated (that's why we use the ListView control). The display will look like:

Product P1
  Installation I1
    Document D1
    Document D2
  Installation I2
    Document D3
Product P2
...

First, use a standard LINQ-to-SQL class in Visual Studio to create your data context object. Next, create a three-level set of ListView objects. Here's the cool part...are you ready for this?

Binding your data: Each ListView needs to be bound to a LINQ IQueryable data source. The outer most ListView, Product, can be just linked to the Products (remember the generated data context adds the plural name to the table name) like:

MultiLevelDataContext db = new MultiLevelDataContext();
IEnumerable<Product> products = db.Products;
lvProduct.DataSource = products;
lvProduct.DataBind();



If you do this in code-behind, that's all you are going to do there. The other two bindings are done in the ListView declaration itself.

The Install and Document nested ListView components just need to have their DataSource property set to the property of its outer ListView object. Remember that when the LINQ-to-SQL code was generated, it automatically added properties for relationship data. For example, the Product class has a property called Installs. The Install class has a property called Documents. These properties basically end up being IQueryable data sources. We simply use a standard Eval() binding statement in the DataSource to connect things up. This makes it incredibly easy to bind the related data into a web control like a three-level ListView structure.

Below is what is all ends up looking like. Two things to mention. Most of the aspx is table formatting. Secondly, notice the DataSource statements in the two nested ListView controls.

Code Behind (cs)

protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
MultiLevelDataContext db = new MultiLevelDataContext();
IEnumerable<Product> products = db.Products;
lvProduct.DataSource = products;
lvProduct.DataBind();
}
}



Web Page (aspx)

<asp:ListView ID="lvProduct" runat="server">
<LayoutTemplate>
<table cellpadding="3" cellspacing="0" border="1" style="width: 100%; background-color: Silver;">
<tr runat="server" id="itemPlaceholder" />
</table>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>Product:
<%# Eval("Name") %>
</td>
</tr>
<asp:ListView ID="lvInstall" runat="server" DataSource='<%# Eval("Installs") %>'>
<LayoutTemplate>
<tr>
<td>
<table cellpadding="3" cellspacing="0" border="1" style="width: 100%; background-color: Aqua;">
<tr runat="server" id="itemPlaceholder" />
</table>
</td>
</tr>
</LayoutTemplate>
<ItemTemplate>
<tr>
<td>Version:
<%# Eval("Version") %>
</td>
<td>Release Date:
<%# Eval("ReleaseDate") %>
</td>
</tr>
<asp:ListView ID="lvDocuments" runat="server" DataSource='<%# Eval("Documents") %>'>
<LayoutTemplate>
<tr>
<td colspan="2">
<table cellpadding="3" cellspacing="0" border="1" style="width: 100%; background-color: Lime;">
<tr runat="server" id="itemPlaceholder" />
</table>
</td>
</tr>
</LayoutTemplate>
<ItemTemplate>
<tr valign="top">
<td>
<%# Eval("Name") %>
</td>
<td>
<%# Eval("Description") %>
</td>
</tr>
</ItemTemplate>
</asp:ListView>
</ItemTemplate>
</asp:ListView>
</ItemTemplate>
</asp:ListView>

No comments:

Can't RDP? How to enable / disable virtual machine firewall for Azure VM

Oh no!  I accidentally blocked the RDP port on an Azure virtual machine which resulted in not being able to log into the VM anymore.  I did ...