This is a migrated thread and some comments may be shown as answers.

[Solved] 3 level hierarchy via client api performs poorly - any fixes or tips

1 Answer 114 Views
Grid
This is a migrated thread and some comments may be shown as answers.
Scott
Top achievements
Rank 1
Scott asked on 16 Jul 2009, 07:01 PM
Hi, I followed the guidance provided by Telerik Support in this blog, for a 2 level deep client api driven hierarchical grid, and I modified the code to implement a 3rd level hierarchy following the same pattern.

The blog entry has a zip file you can download for the basic pattern.  I created a page called 3levels.aspx which extends the concept to add a 3rd level, and I modified the web service and the lSQL entities to map out a 3rd level of data.

If you load default.aspx from the project you will see the page come up and render very quickly, performing as expected

If you load my 3levels.aspx (see below code) you will see a 20-30 second delay while rendering churns, before the browser responds and renders the markup.

These pages request the data after the page has rendered, so the rendering churn is what's killing the 3rd level grid approach.

Okay you grid masters!  Please review and let me know what I can do to work past this issue!

I appreciate any insights!

Thanks and regards!
Scott Burkhalter
SR. SDE Microsoft OEM IT
Dev Lead, Grid Replacement Project

3Levels.aspx - new file add to website
<%@ Page EnableViewState="false" Language="C#" %> 
<%@ Register Assembly="AjaxControlToolkit" Namespace="AjaxControlToolkit" 
    TagPrefix="act" %> 
<%@ Register TagPrefix="telerik" Namespace="Telerik.Web.UI" Assembly="Telerik.Web.UI" %> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml">  
<head runat="server">  
    <title>Three level hierarchy</title> 
    <telerik:RadCodeBlock ID="RadCodeBlock1" runat="server">  
 
        <script type="text/javascript" src="Script1.js"></script> 
 
        <script type="text/javascript">  
            function pageLoad(sender, args) {  
                tableView = $find("<%= RadGrid1.ClientID %>").get_masterTableView();  
 
                commandName = "Load" 
 
                WebService.GetCustomers(0, tableView.get_pageSize(),  
                    tableView.get_sortExpressions().toString(), tableView.get_filterExpressions().toDynamicLinq(),  
                        updateGrid);  
            }  
        </script> 
 
    </telerik:RadCodeBlock> 
</head> 
<body> 
    <form id="form1" runat="server">  
    <div> 
        <telerik:RadScriptManager ID="RadScriptManager1" runat="server">  
            <Services> 
                <asp:ServiceReference Path="~/WebService.asmx" /> 
            </Services> 
        </telerik:RadScriptManager> 
        <telerik:RadStyleSheetManager ID="RadStyleSheetManager1" runat="server" /> 
        <telerik:RadGrid ID="RadGrid1" AllowFilteringByColumn="true" Skin="Telerik" runat="server" 
            GridLines="None" AllowPaging="true" AllowSorting="true" PageSize="10">  
            <ClientSettings> 
                <ClientEvents OnCommand="RadGrid1_Command" OnHierarchyExpanding="RadGrid1_HierarchyExpanding" /> 
            </ClientSettings> 
            <MasterTableView HierarchyLoadMode="Client" ClientDataKeyNames="CustomerID">  
                <Columns> 
                    <telerik:GridBoundColumn SortExpression="CustomerID" DataField="CustomerID" HeaderText="CustomerID" /> 
                    <telerik:GridBoundColumn SortExpression="CompanyName" DataField="CompanyName" HeaderText="CompanyName" /> 
                    <telerik:GridBoundColumn SortExpression="ContactName" DataField="ContactName" HeaderText="ContactName" /> 
                    <telerik:GridBoundColumn SortExpression="ContactTitle" DataField="ContactTitle" HeaderText="ContactTitle" /> 
                    <telerik:GridBoundColumn SortExpression="Country" DataField="Country" HeaderText="Country" /> 
                    <telerik:GridBoundColumn SortExpression="City" DataField="City" HeaderText="City" /> 
                </Columns> 
                <NestedViewTemplate> 
                    <telerik:RadGrid ID="RadGrid2" AllowFilteringByColumn="true" Skin="Telerik" runat="server" 
                        GridLines="None" AllowPaging="true" AllowSorting="true" PageSize="5">  
                        <ClientSettings> 
                            <ClientEvents OnCommand="RadGrid2_Command" OnHierarchyExpanding="RadGrid2_HierarchyExpanding" /> 
                        </ClientSettings> 
                        <MasterTableView HierarchyLoadMode="Client" ClientDataKeyNames="OrderID">  
                            <Columns> 
                                <telerik:GridBoundColumn SortExpression="OrderID" DataField="OrderID" HeaderText="OrderID" 
                                    DataType="System.Int32" /> 
                                <telerik:GridBoundColumn SortExpression="OrderDate" DataField="OrderDate" HeaderText="OrderDate" 
                                    DataType="System.DateTime" DataFormatString="{0:MM/dd/yyyy}" /> 
                                <telerik:GridBoundColumn SortExpression="ShipName" DataField="ShipName" HeaderText="ShipName" /> 
                                <telerik:GridBoundColumn SortExpression="ShipCity" DataField="ShipCity" HeaderText="ShipCity" /> 
                                <telerik:GridBoundColumn SortExpression="ShipCountry" DataField="ShipCountry" HeaderText="ShipCountry" /> 
                            </Columns> 
                            <NestedViewTemplate> 
                                <telerik:RadGrid ID="RadGrid3" AllowFilteringByColumn="true" Skin="Telerik" runat="server" 
                                    GridLines="None" AllowPaging="true" AllowSorting="true" PageSize="3">  
                                    <ClientSettings> 
                                        <ClientEvents OnCommand="RadGrid3_Command" /> 
                                    </ClientSettings> 
                                    <MasterTableView HierarchyLoadMode="Client" ClientDataKeyNames="ProductID">  
                                        <Columns> 
                                            <telerik:GridBoundColumn SortExpression="ProductID" DataField="ProductID" HeaderText="ProductID" 
                                                DataType="System.Int32" /> 
                                            <telerik:GridBoundColumn SortExpression="ProductName" DataField="ProductName" HeaderText="ProductName" /> 
                                            <telerik:GridBoundColumn SortExpression="QuantityPerUnit" DataField="QuantityPerUnit" HeaderText="Quantity PerUnit" DataType="System.String" /> 
                                            <telerik:GridBoundColumn SortExpression="UnitsOnOrder" DataField="UnitsOnOrder" HeaderText="Units On Order" DataType="System.Int16" /> 
                                        </Columns> 
                                    </MasterTableView> 
                                    <PagerStyle AlwaysVisible="true" /> 
                                </telerik:RadGrid> 
                            </NestedViewTemplate> 
                        </MasterTableView> 
                        <PagerStyle AlwaysVisible="true" /> 
                    </telerik:RadGrid> 
                </NestedViewTemplate> 
            </MasterTableView> 
            <PagerStyle AlwaysVisible="true" /> 
        </telerik:RadGrid> 
    </div> 
    </form> 
</body> 
</html> 
 

Referenced js file Script1.js - modified to handle 3rd level hierarchy data requests - replaces file in sample site
var commandName = "";  
var tableView = null;  
var ordersView = null;  
var productsView = null;  
var ordersCommandName = "";  
var productsCommandName = "";  
 
function RadGrid1_Command(sender, args) {  
    args.set_cancel(true);  
 
    commandName = args.get_commandName();  
 
    WebService.GetCustomers(tableView.get_currentPageIndex() * tableView.get_pageSize(), tableView.get_pageSize(),  
                    tableView.get_sortExpressions().toString(), tableView.get_filterExpressions().toDynamicLinq(),  
                        updateGrid);  
}  
 
function updateGrid(result) {  
    tableView.set_dataSource(result);  
    tableView.dataBind();  
 
    var dataItems = tableView.get_dataItems();  
 
    if (commandName == "Filter" || commandName == "Load") {  
        WebService.GetCustomersCount(updateVirtualItemCount);  
    }  
}  
 
function updateVirtualItemCount(result) {  
    tableView.set_virtualItemCount(result);  
}  
 
 
function RadGrid1_HierarchyExpanding(sender, args) {  
    var customerID = args.getDataKeyValue("CustomerID");  
    var nestedViewItem = args.get_nestedViewItem();  
 
    ordersView = $telerik.findControl(nestedViewItem, "RadGrid2").get_masterTableView();  
 
    ordersCommandName = "Load";  
 
    WebService.GetOrdersByCustomerID(customerID, ordersView.get_currentPageIndex() * ordersView.get_pageSize(), ordersView.get_pageSize(),  
                    ordersView.get_sortExpressions().toString(), ordersView.get_filterExpressions().toDynamicLinq(),  
                        updateOrdersGrid, updateFailed);  
}  
 
function updateOrdersGrid(result) {  
    ordersView.set_dataSource(result);  
    ordersView.dataBind();  
 
    if (ordersCommandName == "Filter" || ordersCommandName == "Load") {  
        WebService.GetOrdersByCustomerIDCount(updateOrdersVirtualItemCount);  
    }  
}  
 
function updateFailed(ex) {  
    alert(ex.get_message());  
}  
 
function updateOrdersVirtualItemCount(result) {  
    ordersView.set_virtualItemCount(result);  
}  
 
function RadGrid2_Command(sender, args) {  
    ordersView = sender.get_masterTableView();  
    var nestedViewItem = sender.get_element().parentNode.parentNode;  
    var parentGridDataItem = $find(Telerik.Web.UI.Grid.GetNodePreviousSiblingByTagName(nestedViewItem, "tr").id);  
    var customerID = parentGridDataItem.getDataKeyValue("CustomerID");  
 
    args.set_cancel(true);  
 
    ordersCommandName = args.get_commandName();  
 
    WebService.GetOrdersByCustomerID(customerID, ordersView.get_currentPageIndex() * ordersView.get_pageSize(), ordersView.get_pageSize(),  
                    ordersView.get_sortExpressions().toString(), ordersView.get_filterExpressions().toDynamicLinq(),  
                        updateOrdersGrid, updateFailed);  
}  
 
 
 
function RadGrid2_HierarchyExpanding(sender, args) {  
    var orderID = args.getDataKeyValue("OrderID");  
    var nestedViewItem = args.get_nestedViewItem();  
 
    productsView = $telerik.findControl(nestedViewItem, "RadGrid3").get_masterTableView();  
 
    productsCommandName = "Load";  
 
    WebService.GetProductsByOrderID(orderID, productsView.get_currentPageIndex() * productsView.get_pageSize(), productsView.get_pageSize(),  
                    productsView.get_sortExpressions().toString(), productsView.get_filterExpressions().toDynamicLinq(),  
                        updateEmployeesGrid, updateFailed);  
}  
 
function updateEmployeesGrid(result) {  
    productsView.set_dataSource(result);  
    productsView.dataBind();  
 
    if (productsCommandName == "Filter" || productsCommandName == "Load") {  
        WebService.GetEmployeesByEmployeeIDCount(updateOrdersVirtualItemCount, updateFailed);  
    }  
}  
 
function updateOrdersVirtualItemCount(result) {  
    ordersView.set_virtualItemCount(result);  
}  
 
function RadGrid3_Command(sender, args) {  
    productsView = sender.get_masterTableView();  
    var nestedViewItem = sender.get_element().parentNode.parentNode;  
    var parentGridDataItem = $find(Telerik.Web.UI.Grid.GetNodePreviousSiblingByTagName(nestedViewItem, "tr").id);  
    var orderID = parentGridDataItem.getDataKeyValue("OrderID");  
 
    args.set_cancel(true);  
 
    productsCommandName = args.get_commandName();  
 
    WebService.GetProductsByOrderID(orderID, productsView.get_currentPageIndex() * productsView.get_pageSize(), productsView.get_pageSize(),  
                    productsView.get_sortExpressions().toString(), productsView.get_filterExpressions().toDynamicLinq(),  
                        updateEmployeesGrid, updateFailed);  

Modified webservice.asmx including code to obtain 3rd level data:
<%@ WebService Language="C#" Class="WebService" %>  
 
using System;  
using System.Web;  
using System.Web.Services;  
using System.Web.Services.Protocols;  
using System.Linq;  
using System.Linq.Dynamic;  
using System.Collections.Generic;  
 
 
 
[WebService(Namespace = "http://tempuri.org/")]  
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]  
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.   
[System.Web.Script.Services.ScriptService]  
public class WebService : System.Web.Services.WebService  
{  
    IQueryable<Customer> customers = new DataClassesDataContext().Customers.AsQueryable<Customer>();  
    IQueryable<Order> orders = new DataClassesDataContext().Orders.AsQueryable<Order>();  
    IQueryable<Order_Detail> order_details = new DataClassesDataContext().Order_Details.AsQueryable<Order_Detail>();  
    IQueryable<Product> products = new DataClassesDataContext().Products.AsQueryable<Product>();  
 
      
    [WebMethod(EnableSession = true)]  
    public List<Customer> GetCustomers(int startIndex, int maximumRows, string sortExpression, string filterExpression)  
    {  
        if (!String.IsNullOrEmpty(filterExpression))  
        {  
            customers = customers.Where(filterExpression);  
        }  
 
        if (!String.IsNullOrEmpty(sortExpression))  
        {  
            customers = customers.OrderBy(sortExpression);  
        }  
 
        HttpContext.Current.Session["CustomersCount"] = customers.Count();  
 
        return customers.Skip(startIndex).Take(maximumRows).ToList();  
    }  
 
    [WebMethod(EnableSession = true)]  
    public int GetCustomersCount()  
    {  
        return (int)HttpContext.Current.Session["CustomersCount"];  
    }  
 
    [WebMethod(EnableSession = true)]  
    public List<Order> GetOrdersByCustomerID(string customerID, int startIndex, int maximumRows, string sortExpression, string filterExpression)  
    {  
        if (!String.IsNullOrEmpty(filterExpression))  
        {  
            orders = orders.Where(String.Format(@"CustomerID == ""{0}"" AND {1}", customerID, filterExpression));  
        }  
        else 
        {  
            orders = orders.Where(c => c.CustomerID == customerID);  
        }  
 
        if (!String.IsNullOrEmpty(sortExpression))  
        {  
            orders = orders.OrderBy(sortExpression);  
        }  
 
        HttpContext.Current.Session["OrdersByCustomerCount"] = orders.Count();  
 
        foreach (var o1 in orders)  
            o1.Order_Details = null;  
 
        return orders.Skip(startIndex).Take(maximumRows).ToList();  
    }  
 
    [WebMethod(EnableSession = true)]  
    public int GetOrdersByCustomerIDCount()  
    {  
        return (int)HttpContext.Current.Session["OrdersByCustomerCount"];  
    }  
 
     
    [WebMethod(EnableSession = true)]  
    public List<ProductPOCO> GetProductsByOrderID(string orderID, int startIndex, int maximumRows, string sortExpression, string filterExpression)  
    {  
        if (!String.IsNullOrEmpty(filterExpression))  
        {  
            orders = orders.Where(String.Format(@"OrderID == ""{0}"" AND {1}", orderID, filterExpression));  
        }  
        else 
        {  
            orders = orders.Where(o => o.OrderID == Int32.Parse(orderID));  
        }  
 
        if (!String.IsNullOrEmpty(sortExpression))  
        {  
            orders = orders.OrderBy(sortExpression);  
        }  
 
        HttpContext.Current.Session["ProductsByOrderIDCount"] = orders.FirstOrDefault().Products.Count();  
 
        var q = from p in orders.FirstOrDefault().Products  
                select new ProductPOCO  
                    {  
                        ProductID = p.ProductID,  
                        ProductName = p.ProductName,  
                        QuantityPerUnit = p.QuantityPerUnit,  
                        UnitsOnOrder = p.UnitsOnOrder  
                    };  
 
        return q.Skip(startIndex).Take(maximumRows).ToList();  
    }  
 
    [WebMethod(EnableSession = true)]  
    public int GetEmployeesByEmployeeIDCount()  
    {  
        return (int)HttpContext.Current.Session["ProductsByOrderIDCount"];  
    }  
 
    public class ProductPOCO  
    {  
        public int ProductID { getset; }  
        public string ProductName { getset; }  
        public string QuantityPerUnit { getset; }  
        public short? UnitsOnOrder { getset; }  
    }  

ListSelector needed for many to many relationship between Orders and Products (put in App_Code) directory as ListSelector.cs
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Text;  
using System.Collections;  
using System.ComponentModel;  
 
namespace ListSelector  
{  
    public class BindingListSelector<TSource, T> : ListSelector<TSource, T>, IBindingList  
    {  
        public BindingListSelector(IBindingList source, Func<TSource, T> selector):base(source as IList<TSource>, selector)  
        {  
            sourceAsBindingList = source;  
        }  
 
        protected IBindingList sourceAsBindingList;
        #region IBindingList Members  
 
        public void AddIndex(PropertyDescriptor property)  
        {  
            sourceAsBindingList.AddIndex(property);  
        }  
 
        public object AddNew()  
        {  
            return sourceAsBindingList.AddNew();  
        }  
 
        public bool AllowEdit  
        {  
            get { return sourceAsBindingList.AllowEdit; }  
        }  
 
        public bool AllowNew  
        {  
            get { return sourceAsBindingList.AllowNew; }  
        }  
 
        public bool AllowRemove  
        {  
            get { return sourceAsBindingList.AllowRemove; }  
        }  
 
        public void ApplySort(PropertyDescriptor property, ListSortDirection direction)  
        {  
            sourceAsBindingList.ApplySort(property, direction);  
        }  
 
        public int Find(PropertyDescriptor property, object key)  
        {  
            return sourceAsBindingList.Find(property, key);  
        }  
 
        public bool IsSorted  
        {  
            get { return sourceAsBindingList.IsSorted; }  
        }  
 
        public event ListChangedEventHandler ListChanged  
        {  
            add  
            {  
                sourceAsBindingList.ListChanged += value;  
            }  
            remove  
            {  
                sourceAsBindingList.ListChanged -= value;  
            }  
        }  
 
        public void RemoveIndex(PropertyDescriptor property)  
        {  
            sourceAsBindingList.RemoveIndex(property);  
        }  
 
        public void RemoveSort()  
        {  
            sourceAsBindingList.RemoveSort();  
        }  
 
        public ListSortDirection SortDirection  
        {  
            get { return sourceAsBindingList.SortDirection; }  
        }  
 
        public PropertyDescriptor SortProperty  
        {  
            get { return sourceAsBindingList.SortProperty; }  
        }  
 
        public bool SupportsChangeNotification  
        {  
            get { return sourceAsBindingList.SupportsChangeNotification; }  
        }  
 
        public bool SupportsSearching  
        {  
            get { return sourceAsBindingList.SupportsSearching; }  
        }  
 
        public bool SupportsSorting  
        {  
            get { return sourceAsBindingList.SupportsSorting; }  
        }
        #endregion  
    }  
 
    public static class ListSelectorExtensions  
    {  
        public static ListSelector<TSource, T> AsListSelector<TSource, T>(this IList<TSource> source, Func<TSource, T> selector)  
        {  
            return new ListSelector<TSource, T>(source, selector);  
        }  
        public static BindingListSelector<TSource, T> AsListSelector<TSource, T>(this IBindingList source, Func<TSource, T> selector)  
        {  
            return new BindingListSelector<TSource, T>(source, selector);  
        }  
    }  
 
    public class ListSelector<TSource, T> : IList<T>, IList  
    {  
        public ListSelector(IList<TSource> source, Func<TSource, T> selector)  
        {  
            this.source = source;  
            this.selector = selector;  
            projection = source.Select(selector);  
        }  
 
        protected IList<TSource> source;  
        protected Func<TSource, T> selector;  
        protected IEnumerable<T> projection;
        #region IList<T> Members  
 
        public int IndexOf(T item)  
        {  
            int i = 0;  
            foreach (T t in projection)  
            {  
                if (t.Equals(item))  
                    return i;  
                i++;  
            }  
            return -1;  
        }  
 
        public void Insert(int index, T item)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        public void RemoveAt(int index)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        public T this[int index]  
        {  
            get 
            {  
                return selector(source[index]);  
            }  
            set 
            {  
                throw new Exception("The method or operation is not implemented.");  
            }  
        }
        #endregion  
 
        #region ICollection<T> Members  
 
        public void Add(T item)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        public void Clear()  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        public bool Contains(T item)  
        {  
            return projection.Contains(item);  
        }  
 
        public void CopyTo(T[] array, int arrayIndex)  
        {  
            //source.CopyTo(projection.ToArray(), arrayIndex);  
        }  
 
        public int Count  
        {  
            get { return source.Count; }  
        }  
 
        public bool IsReadOnly  
        {  
            get { return true; }  
        }  
 
        public bool Remove(T item)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }
        #endregion  
 
        #region IEnumerable<T> Members  
 
        public IEnumerator<T> GetEnumerator()  
        {  
            return projection.GetEnumerator();  
        }
        #endregion  
 
        #region IEnumerable Members  
 
        System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()  
        {  
            return projection.GetEnumerator();  
        }
        #endregion  
 
        #region IList Members  
 
        int IList.Add(object value)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        void IList.Clear()  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        bool IList.Contains(object value)  
        {  
            return Contains((T) value);  
        }  
 
        int IList.IndexOf(object value)  
        {  
            return IndexOf((T) value);  
        }  
 
        void IList.Insert(int index, object value)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        bool IList.IsFixedSize  
        {  
            get { return true; }  
        }  
 
        bool IList.IsReadOnly  
        {  
            get { return true; }  
        }  
 
        void IList.Remove(object value)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        void IList.RemoveAt(int index)  
        {  
            throw new Exception("The method or operation is not implemented.");  
        }  
 
        object IList.this[int index]  
        {  
            get 
            {  
                return this[index];  
            }  
            set 
            {  
                throw new Exception("The method or operation is not implemented.");  
            }  
        }
        #endregion  
 
        #region ICollection Members  
 
        void ICollection.CopyTo(Array array, int index)  
        {  
            //CopyTo(array, index);  
        }  
 
        int ICollection.Count  
        {  
            get { return this.Count; }  
        }  
 
        bool ICollection.IsSynchronized  
        {  
            get { return false; }  
        }  
 
        object ICollection.SyncRoot  
        {  
            get { throw new Exception("The method or operation is not implemented."); }  
        }
        #endregion  
    }  
}  
 

OrderPartial.cs class that addes accessor for Products list hanging off of the Order entity - put in App_Code dir
using System;  
using System.Collections.Generic;  
using System.Linq;  
using System.Web;  
using ListSelector;  
 
public partial class Order  
{  
    private ListSelector<Order_Detail, Product> products = null;  
    public ListSelector<Order_Detail, Product> Products  
    {  
        get 
        {  
            if (products == null)  
                products = this.Order_Details.AsListSelector(od => od.Product);  
            return products;  
        }  
    }  

Modified DataClasses.dbml (LINQ Entities) based on Northwind DB in sample code: - adds 3rd level relationships - replaces same file in App_Code directory
<?xml version="1.0" encoding="utf-8"?>  
<Database Name="NorthwindDB" Class="DataClassesDataContext" xmlns="http://schemas.microsoft.com/linqtosql/dbml/2007">  
  <Connection Mode="WebSettings" ConnectionString="Data Source=scburk1;AttachDbFilename=|DataDirectory|\Northwind.mdf;Initial Catalog=NorthwindDB;Integrated Security=True" SettingsObjectName="System.Configuration.ConfigurationManager.ConnectionStrings" SettingsPropertyName="NorthwindDBConnectionString" Provider="System.Data.SqlClient" /> 
  <Table Name="dbo.Customers" Member="Customers">  
    <Type Name="Customer">  
      <Column Name="CustomerID" Type="System.String" DbType="NChar(5) NOT NULL" CanBeNull="false" /> 
      <Column Name="CompanyName" Type="System.String" DbType="NVarChar(40) NOT NULL" CanBeNull="false" /> 
      <Column Name="ContactName" Type="System.String" DbType="NVarChar(30)" CanBeNull="true" /> 
      <Column Name="ContactTitle" Type="System.String" DbType="NVarChar(30)" CanBeNull="true" /> 
      <Column Name="Address" Type="System.String" DbType="NVarChar(60)" CanBeNull="true" /> 
      <Column Name="City" Type="System.String" DbType="NVarChar(15)" CanBeNull="true" /> 
      <Column Name="Region" Type="System.String" DbType="NVarChar(15)" CanBeNull="true" /> 
      <Column Name="PostalCode" Type="System.String" DbType="NVarChar(10)" CanBeNull="true" /> 
      <Column Name="Country" Type="System.String" DbType="NVarChar(15)" CanBeNull="true" /> 
      <Column Name="Phone" Type="System.String" DbType="NVarChar(24)" CanBeNull="true" /> 
      <Column Name="Fax" Type="System.String" DbType="NVarChar(24)" CanBeNull="true" /> 
      <Association Name="Customer_Order" Member="Orders" ThisKey="CustomerID" OtherKey="CustomerID" Type="Order" /> 
    </Type> 
  </Table> 
  <Table Name="dbo.Orders" Member="Orders">  
    <Type Name="Order">  
      <Column Name="OrderID" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" /> 
      <Column Name="CustomerID" Type="System.String" DbType="NChar(5)" CanBeNull="true" /> 
      <Column Name="EmployeeID" Type="System.Int32" DbType="Int" CanBeNull="true" /> 
      <Column Name="OrderDate" Type="System.DateTime" DbType="DateTime" CanBeNull="true" /> 
      <Column Name="RequiredDate" Type="System.DateTime" DbType="DateTime" CanBeNull="true" /> 
      <Column Name="ShippedDate" Type="System.DateTime" DbType="DateTime" CanBeNull="true" /> 
      <Column Name="ShipVia" Type="System.Int32" DbType="Int" CanBeNull="true" /> 
      <Column Name="Freight" Type="System.Decimal" DbType="Money" CanBeNull="true" /> 
      <Column Name="ShipName" Type="System.String" DbType="NVarChar(40)" CanBeNull="true" /> 
      <Column Name="ShipAddress" Type="System.String" DbType="NVarChar(60)" CanBeNull="true" /> 
      <Column Name="ShipCity" Type="System.String" DbType="NVarChar(15)" CanBeNull="true" /> 
      <Column Name="ShipRegion" Type="System.String" DbType="NVarChar(15)" CanBeNull="true" /> 
      <Column Name="ShipPostalCode" Type="System.String" DbType="NVarChar(10)" CanBeNull="true" /> 
      <Column Name="ShipCountry" Type="System.String" DbType="NVarChar(15)" CanBeNull="true" /> 
      <Association Name="Order_Order_Detail" Member="Order_Details" ThisKey="OrderID" OtherKey="OrderID" Type="Order_Detail" /> 
      <Association Name="Customer_Order" Member="Customer" ThisKey="CustomerID" OtherKey="CustomerID" Type="Customer" IsForeignKey="true" /> 
    </Type> 
  </Table> 
  <Table Name="dbo.[Order Details]" Member="Order_Details">  
    <Type Name="Order_Detail">  
      <Column Name="OrderID" Type="System.Int32" DbType="Int NOT NULL" IsPrimaryKey="true" CanBeNull="false" /> 
      <Column Name="ProductID" Type="System.Int32" DbType="Int NOT NULL" IsPrimaryKey="true" CanBeNull="false" /> 
      <Column Name="UnitPrice" Type="System.Decimal" DbType="Money NOT NULL" CanBeNull="false" /> 
      <Column Name="Quantity" Type="System.Int16" DbType="SmallInt NOT NULL" CanBeNull="false" /> 
      <Column Name="Discount" Type="System.Single" DbType="Real NOT NULL" CanBeNull="false" /> 
      <Association Name="Order_Order_Detail" Member="Order" ThisKey="OrderID" OtherKey="OrderID" Type="Order" IsForeignKey="true" /> 
      <Association Name="Product_Order_Detail" Member="Product" ThisKey="ProductID" OtherKey="ProductID" Type="Product" IsForeignKey="true" /> 
    </Type> 
  </Table> 
  <Table Name="dbo.Products" Member="Products">  
    <Type Name="Product">  
      <Column Name="ProductID" Type="System.Int32" DbType="Int NOT NULL IDENTITY" IsPrimaryKey="true" IsDbGenerated="true" CanBeNull="false" /> 
      <Column Name="ProductName" Type="System.String" DbType="NVarChar(40) NOT NULL" CanBeNull="false" /> 
      <Column Name="SupplierID" Type="System.Int32" DbType="Int" CanBeNull="true" /> 
      <Column Name="CategoryID" Type="System.Int32" DbType="Int" CanBeNull="true" /> 
      <Column Name="QuantityPerUnit" Type="System.String" DbType="NVarChar(20)" CanBeNull="true" /> 
      <Column Name="UnitPrice" Type="System.Decimal" DbType="Money" CanBeNull="true" /> 
      <Column Name="UnitsInStock" Type="System.Int16" DbType="SmallInt" CanBeNull="true" /> 
      <Column Name="UnitsOnOrder" Type="System.Int16" DbType="SmallInt" CanBeNull="true" /> 
      <Column Name="ReorderLevel" Type="System.Int16" DbType="SmallInt" CanBeNull="true" /> 
      <Column Name="Discontinued" Type="System.Boolean" DbType="Bit NOT NULL" CanBeNull="false" /> 
      <Association Name="Product_Order_Detail" Member="Order_Details" ThisKey="ProductID" OtherKey="ProductID" Type="Order_Detail" /> 
    </Type> 
  </Table> 
</Database> 

1 Answer, 1 is accepted

Sort by
0
Rosen
Telerik team
answered on 17 Jul 2009, 11:23 AM
Hello Scott,

As you may know RadGrid does not support hierarchy when populated client-side, thus the suggested approach is more of a workaround and may not be suitable in all scenarios. You may have noticed that this approach will generate a grid instance for every detail table, which eventually will lead to more overhead when used with more hierarchy levels. If you follow the comments on the blog post you will find a link which shows a modification of the example in which is used only one instance of the nested grid for a given level. However this implementation weakness is that only single level can be expanded at a time. 

Regards,
Rosen
the Telerik team

Instantly find answers to your questions on the new Telerik Support Portal.
Check out the tips for optimizing your support resource searches.
Tags
Grid
Asked by
Scott
Top achievements
Rank 1
Answers by
Rosen
Telerik team
Share this question
or