Passing data between layers using Generic list collection

SheoNarayan
Posted by in C# category on for Beginner level | Views : 79578 red flag
Rating: 3 out of 5  
 1 vote(s)

This simple article describes how to pass data between different layers of your application using Generic list collections.
Update:

After going through this article, please read my response below. It's better to use Collection<T> than List<T>.

Introduction

This article is for beginners who want to know how to use .NET generics to pass data between different layers. If you do not know about generic list, please go thorugh this article http://www.dotnetfunda.com/articles/article152.aspx. In brief, List class is a generic equivalent of ArrayList class.

If you want to learn about how to develop 3-tier architecture application, please go through http://www.dotnetfunda.com/articles/article18.aspx and how to develop 4-tier architecture, please go through http://www.dotnetfunda.com/articles/article18.aspx. To show the use of generics, I have used downloadable source code available for 4-Tier architecure article.

There were discussion earlier and probably now also on whether we should use DataTable or generic List collection class to pass data between layers, you can read through http://weblogs.asp.net/bsimser/archive/2007/04/04/datatable-vs-bindinglist-t.aspx post to learn more about that but now I feel its evident that using Generic list to pass data between layers is the way to go. I personally prefer Genericl List over DataTable as I have seen its beneifts working in different projects.

There are different views on where the actual conversion of data from DataTable or DataReader to list should happen, some suggest to have a separate translation layer that accepts DataTable or DataReader and converts them into the list of desired objects. Normally people do it into Data Access Layer itself and pass the generic list objects to Presentation Layer through Business Layer. In this demonstration, I shall do the same.

Ideally, if you have preferred to use generic list to pass data between differenet layers you should prefer using entity layer (or I have called BO - Business Objects layer as described in the 4-Tier architecture application article referenced above). In this example, I have a Person business object / entity object that have properties related to a person, I am trying to fill into generic list.

Below code is the Data Access Layer code that gets data from database and convert them into generic list collection. Please note that I have used latest version of C# (3.0) that supports var keyword and other features I have used in below code snippets.

/// <summary>

/// Load records from the database using DataReader

/// </summary>

/// <returns></returns>

public List<Person> LoadThroughDataReader()

{

var list = new List<Person>();

using (SqlConnection conn = new SqlConnection(connStr))

{

using (SqlCommand cmd = new SqlCommand("LoadAll", conn))

{

cmd.CommandType = CommandType.StoredProcedure;

conn.Open();

using (SqlDataReader reader = cmd.ExecuteReader())

{

while (reader.Read())

{

Person p = new Person()

{

Age = reader.GetInt32(reader.GetOrdinal("Age")),

FirstName = reader.GetString(reader.GetOrdinal("FirstName")),

LastName = reader.GetString(reader.GetOrdinal("LastName"))

};

list.Add(p);

}

}

conn.Close();

}

return list;

}

}

 

/// <summary>

/// load returns from database using DataTable

/// </summary>

/// <returns></returns>

public List<Person> LoadThroughDataTable()

{

var list = new List<Person>();

using (SqlConnection conn = new SqlConnection(connStr))

{

using (SqlCommand cmd = new SqlCommand("LoadAll", conn))

{

cmd.CommandType = CommandType.StoredProcedure;

using (SqlDataAdapter ad = new SqlDataAdapter(cmd))

{

DataTable table = new DataTable();

conn.Open(); // open the connection

ad.Fill(table);

conn.Close(); // close the connection

// loop through all rows

foreach (DataRow row in table.Rows)

{

Person p = new Person()

{

Age = int.Parse(row["Age"].ToString()),

FirstName = row["FirstName"].ToString(),

LastName = row["LastName"].ToString()

};

list.Add(p);

}

}

}

return list;

}

}

In the above code snipeet, I have two method called LoadDataThroughDataReader and LoadDataThroughDataTable. In both method, I have declared a variable for List<Person> that is filled with the Person records returned from the database. The first method LoadDataThroughDataReader use cmd.ExecuteReader() method to get the SqlDataReader and read through all the records in the reader and add the Person object into the List. The second method is slightly different and use DataTable instead of DataReader to fetch data from database, loop through all the rows of the DataTable and add Person objects into the list.

You may wonder why these two methods when both are doing the same thing. I prefer using DataReader when I have lesser amount of data from the database and loop through because DataReader expect live connection when it loops through all the records. For large amount of data, it may be a bottleneck for the database to keep the connection alive and loop through. So for large amount of data I prefer to fill the DataTable, close the database connection and then loop through all the records do the required operation.

Once you have your Data Access Layer method ready to return the generic list collection, you can call it through your business layer (My business layer class name is PersonBAL) as follows:

/// <summary>

/// load records through data reader

/// </summary>

/// <returns></returns>

public List<Person> LoadThroughDataReader()

{

return new PersonDAL().LoadThroughDataReader();

}

 

/// <summary>

/// load records through datatable

/// </summary>

/// <returns></returns>

public List<Person> LoadThroughDataTable()

{

return new PersonDAL().LoadThroughDataTable();

}

Get solutions of .NET problems with video explanations, .pdf and source code in .NET How to's.

As I do not have any logic in the Business Layer, so I have simply instantiated the PersonDAL data access layer object and called the respective methods.

Subsequently, this business layer methods can be called from presentation layer as follows:

GridView g1 = new GridView();

g1.DataSource = new PersonBAL().LoadThroughDataReader();

g1.DataBind();

PlaceHolder1.Controls.Add(g1);

 

GridView g2 = new GridView();

g2.DataSource = new PersonBAL().LoadThroughDataTable();

g2.DataBind();

 

// I have a place holder control in the .aspx page in which both above gridview is added

PlaceHolder1.Controls.Add(g2);

As GridView DataSource supports generic list also as data source so we can directly assign the business layer method as the GridView datasource and all the records from the generic list collection will be populated as rows into the GridView. All the properties of the Person object will be treated as the column of the GridView unless you have explictely assigned the columns in the GridView.

Hope you find this article interesting, please subscribe for the subsequent article alert directly in your email box. Happy coding!


 

Page copy protected against web site content infringement by Copyscape

About the Author

SheoNarayan
Full Name: Sheo Narayan
Member Level: HonoraryPlatinum
Member Status: Administrator
Member Since: 7/8/2008 6:32:14 PM
Country: India
Regards, Sheo Narayan http://www.dotnetfunda.com

Ex-Microsoft MVP, Author, Writer, Mentor & architecting applications since year 2001. Connect me on http://www.facebook.com/sheo.narayan | https://twitter.com/sheonarayan | http://www.linkedin.com/in/sheonarayan

Login to vote for this post.

Comments or Responses

Posted by: Eti on: 8/21/2009
Hi,

It's generally considered bad practice to return List<T> instances from methods in your public API.

See this:

http://msdn.microsoft.com/en-us/library/ms182142.aspx

and

http://blogs.msdn.com/kcwalina/archive/2005/09/26/474010.aspx
Posted by: SheoNarayan on: 8/21/2009
@Eti:
Thanks for noticing this Eti and providing useful link.

For readers:


As per below suggestions and with the reference of http://msdn.microsoft.com/en-us/library/ms182142.aspx article, we can use System.Collections.ObjectModel.Collection(T) instead of System.Collections.Generic.List(T).

All the code will look same, instead of return type as List<Person>, please use Collection<Person> and everything will be alright.

So modified Data Access Layer function definition will look like

public Collection<Person> LoadThroughDataReader()

{
var list = new Collection<Person>();
...
...
}


Business Layer function will look like
public Collection<Person> LoadThroughDataReader()


No change is required in presentation layer.

Thanks again Eti!

--
Warm regards,
Sheo Narayan

Posted by: StevenSw on: 8/21/2009
I believe the following will perform a bit faster rather the use of a DataAdapter to populate the DataTabe.. This is untested..

public List<Person> LoadThroughDataTable()

{
var list = new List<Person>();

using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand cmd = new SqlCommand("LoadAll", conn))
{
cmd.CommandType = CommandType.StoredProcedure;

SqlDataReader dr = cmd.ExecuteReader(CommandBehavior.CloseConnection);
DataTable dt = new DataTable();
dt.Load(dr);

// loop through all rows
foreach (DataRow row in table.Rows)
{
Person p = new Person()
{
Age = int.Parse(row["Age"].ToString()),
FirstName = row["FirstName"].ToString(),
LastName = row["LastName"].ToString()
};
list.Add(p);
}
}
return list;
}
}

Posted by: SheoNarayan on: 8/21/2009
Thanks Steven,

Yes, I also feel that filling DataTable through reader is faster than DataAdapter. In this sample, I had used DataAdapter just for the sake of demo application. Thanks for modifying the code snippet to use DataReader however, I feel that it can be modified further to ensure that DataReader is closed immediately after loading the rows into DataTable. I have modified the code snippet properly to use DataReader to fill the DataTable and here it goes.

public List<Person> LoadThroughDataTable()

{
var list = new List<Person>();
using (SqlConnection conn = new SqlConnection(connStr))
{
using (SqlCommand cmd = new SqlCommand("LoadAll", conn))
{
cmd.CommandType = CommandType.StoredProcedure;
DataTable table = new DataTable();
conn.Open();
using (SqlDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
{
table.Load(reader);
}

// loop through all rows
foreach (DataRow row in table.Rows)
{
Person p = new Person()
{
Age = int.Parse(row["Age"].ToString()),
FirstName = row["FirstName"].ToString(),
LastName = row["LastName"].ToString()
};
list.Add(p);
}
}
return list;
}
}


Thanks again for your feedback.
Regards,
Sheo Narayan
Posted by: Ashoks104 on: 9/14/2009
Is there any advantage of loading the DataReader to DataTable and then accessing its columns. Would it be better to read data directly from DataReader and set values to my business objects?
Posted by: SheoNarayan on: 9/14/2009
Hi Ashoks104,

Thanks for your query.

I have already a method called LoadThroughDataReader that is doing that.

Sometimes its not good to read loads of data into reader and loop through as Reader works in connected architecture and till the time you loop through all records and close the reader, you will have to keep the database connection of the reader open. Also reader is forward only so you can't play with it going forward, backword and getting data of a particular column of a particular row.

Thanks
Posted by: Vinbais on: 2/28/2012 | Points: 25
Thanks Sheo, I found this article, some thing I desperately needed. Since this article is very old. Hence I do not know if you will reply to my query. If you do, it would be great help.

My question is when we attach generic collection as datasource to the gridview, how we can specify primary keyvalue in the gridview in a hidden manner?

Login to post response

Comment using Facebook(Author doesn't get notification)