I am beginner with C# and (for learning) trying to populate TreeView control with Parent Child nodes populated from database. I have implemented IHierarchicalDataSource interface to create a Category (Parent/child) collection and populate the treeview control. When I try to populate the categorycollection (in 'GetsampleData' function), compiler throws stackoverflowexception. I suspect its because of recursive looping to populate the collection but I cannot think any other way of doing this. Please help.
//DataAccess layer
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Data;
using System.Data.Common;
namespace OnlineStore.App_Code
{
public static class DataAccess
{
public static DbCommand CreateCommand()
{
try
{
string dbConnectionString = AppConfig.DBConnectionString;
string dbProviderName = AppConfig.DBProviderName;
DbProviderFactory factory = DbProviderFactories.GetFactory(dbProviderName);//Exception is thrown here
DbConnection connection = factory.CreateConnection();
connection.ConnectionString = dbConnectionString;
DbCommand command = connection.CreateCommand();
command.CommandType = CommandType.StoredProcedure;
return command;
}
catch (Exception ex)
{
throw ex;
}
}
/* Function for selecting data from the database and
return as DataTable */
public static DataTable ExecuteSelectCommand(DbCommand command)
{
DataTable dbtable;
dbtable = new DataTable();
try
{
command.Connection.Open();
DbDataReader reader = command.ExecuteReader();
dbtable.Load(reader);
reader.Close();
}
catch (Exception ex)
{
Utilities.LogError(ex);
}
finally
{
command.Connection.Close();
}
return dbtable;
}
/* Function to execute command for inserting, deleting data and
return number of affected rows */
public static int ExecuteNonQuery(DbCommand command)
{
int rowsAffected = -1;
try
{
command.Connection.Open();
rowsAffected = command.ExecuteNonQuery();
}
catch (Exception ex)
{
Utilities.LogError(ex);
}
finally
{
command.Connection.Close();
}
return rowsAffected;
}
/* Function to execute command and return results of aggregate functions
such as count(), sum() etc. */
public static string ExecuteScalar(DbCommand command)
{
string result = "";
try
{
command.Connection.Open();
result = command.ExecuteScalar().ToString();
}
catch (Exception ex)
{
Utilities.LogError(ex);
}
finally
{
command.Connection.Close();
}
return result;
}
}
}//Datasource control
//Datasource control ------ 1
public class TreeViewDatabaseControl : HierarchicalDataSourceControl, IHierarchicalDataSource
{
public TreeViewDatabaseControl()
: base()
{
}
public TreeViewDatabaseControlView view = null;
protected override HierarchicalDataSourceView GetHierarchicalView(string viewPath)
{
if (view == null)
{
view = new TreeViewDatabaseControlView(viewPath);
}
return view;
}
protected override ControlCollection CreateControlCollection()
{
return new ControlCollection(this);
}
}
//Data --------- 2
public class Category : IHierarchyData
{
//Define category entity
public int CategoryId { get; set; }
public int ParentId { get; set; }
public string CategoryName { get; set; }
public Category()
{
}
public Category(int categoryId, int parentId, string categoryName)
{
CategoryId = categoryId;
ParentId = parentId;
CategoryName = categoryName;
}
//IHierarchyData implementation
public IHierarchicalEnumerable GetChildren()
{
CategoryCollection children = new CategoryCollection();
Category cat = new Category();
// Loop through your local data and find any children
foreach (Category category in Common.GetSampleData())
{
if (category.ParentId == this.CategoryId)
{
children.Add(category);
}
}
return children;
}
public IHierarchyData GetParent()
{
Category cat = new Category();
// Loop through your local data and report back with the parent
foreach (Category category in Common.GetSampleData())
{
if (category.CategoryId == this.ParentId)
return category;
}
return null;
}
public bool HasChildren
{
get
{
CategoryCollection children = GetChildren() as CategoryCollection;
return children.Count > 0;
}
}
public object Item
{
get { return this; }
}
public string Path
{
get { return this.CategoryId.ToString(); }
}
public string Type
{
get { return this.GetType().ToString(); }
}
public override string ToString()
{
return this.CategoryName.ToString();
}
}
//Create collection of data --------- 3
public class CategoryCollection : List<Category>, IHierarchicalEnumerable
{
public CategoryCollection()
: base()
{
}
public IHierarchyData GetHierarchyData(object enumeratedItem)
{
return enumeratedItem as IHierarchyData;
}
}
//Create data view to pass to datasource control ----------- 4
public class TreeViewDatabaseControlView : HierarchicalDataSourceView
{
private string _viewpath;
public TreeViewDatabaseControlView(string viewpath)
{
_viewpath = viewpath;
}
public override IHierarchicalEnumerable Select()
{
CategoryCollection catCollec = new CategoryCollection();
Category category = new Category();
foreach (Category cat in Common.GetSampleData())
{
if (cat.ParentId == 0)
{
catCollec.Add(cat);
}
}
return catCollec;
}
}
public static class Common
{
public static CategoryCollection GetRootCategories()
{
Category category = new Category();
CategoryCollection catCollect = new CategoryCollection();
foreach (Category cat in Common.GetSampleData())
{
if (cat.ParentId == 0)
{
catCollect.Add(cat);
}
}
return catCollect;
}
public static List<Category> GetSampleData()
{
try
{
CategoryCollection categoryCollection = new CategoryCollection();
categoryCollection.Clear();
//Get categories from database
DbCommand command = DataAccess.CreateCommand();
command.CommandText = "sp_Categories";
DataTable dbtblCategories = DataAccess.ExecuteSelectCommand(command); //CategoryId, CategoryName
//Get sub-categories from database
command.CommandText = "sp_ProductsInCategories";
DataTable dbtblProduct = DataAccess.ExecuteSelectCommand(command); //CategoryId, CategoryName, ProductName
//Convert to generic list
List<DataRow> listCat = dbtblCategories.AsEnumerable().ToList();
List<DataRow> listProducts = dbtblProduct.AsEnumerable().ToList();
//Loop through data and populate collection
foreach (var catRow in listCat)
{
var catId = Convert.ToInt32(catRow.ItemArray[0].ToString());
var catName = catRow.ItemArray[1].ToString();
categoryCollection.Add(new Category(catId, 0, catName));
IEnumerable<DataRow> results = listProducts.Where(s => Convert.ToInt32(s.ItemArray[1].ToString()) == catId);
foreach (var row in results)
{
var prodId = Convert.ToInt32(row.ItemArray[0].ToString());
var Id = Convert.ToInt32(row.ItemArray[1].ToString());
var prodName = row.ItemArray[3].ToString();
categoryCollection.Add(new Category(prodId, Id, prodName));//Somethings wrong here as commenting this line will not throw any error but it wont add the child items.
}
}
//categoryCollection.Add(new Category(1, 0, "Televisions"));
//categoryCollection.Add(new Category(2, 0, "Cameras"));
//categoryCollection.Add(new Category(3, 0, "Audio"));
//categoryCollection.Add(new Category(4, 0, "Computers"));
//categoryCollection.Add(new Category(5, 1, "HDTV"));
//categoryCollection.Add(new Category(6, 1, "LED"));
//categoryCollection.Add(new Category(7, 1, "CRT"));
//categoryCollection.Add(new Category(8, 1, "Plasma"));
//categoryCollection.Add(new Category(9, 2, "Compact"));
//categoryCollection.Add(new Category(10, 2, "DSLR"));
//categoryCollection.Add(new Category(11, 2, "Full Frame"));
//categoryCollection.Add(new Category(12, 2, "Video"));
return categoryCollection;
}
catch (Exception ex)
{
throw ex;
}
finally
{
}
}
}//Calling above datasource control and populating treeview
public partial class TreeViewDb : System.Web.UI.UserControl
{
protected void Page_Load(object sender, EventArgs e)
{
if (!IsPostBack)
{
CategoryCollection catCollec = Common.GetRootCategories();
TreeView1.DataSource = catCollec;
TreeView1.DataBind();
}
}
protected void TreeView1_SelectedNodeChanged(object sender, EventArgs e)
{
Label1.Text = TreeView1.SelectedNode.ValuePath;
}
}