3 tire architecture in asp.net

Introduction

3-Tier architecture generally contains UI or Presentation Layer, Business Access Layer (BAL) or Business Logic Layer and Data Access Layer (DAL).

Presentation Layer (UI)
Presentation layer cotains pages like .aspx or windows form where data is presented to the user or input is taken from the user.

Business Access Layer (BAL) or Business Logic Layer
BAL contains business logic, validations or calculations related with the data, if needed. I will call it Business Access Layer in my demo.

Data Access Layer (DAL)
DAL contains methods that helps business layer to connect the data and perform required action, might be returning data or manipulating data (insert, update, delete etc). For this demo application, I have taken a very simple example. I am assuming that I have to play with record of persons (FirstName, LastName, Age) and I will refer only these data through out this article.
Designing 3-Tier Architecture

For the ease of understanding, I have created BAL, DAL into the App_Code folder. In real scenario, you should create separate projects for BAL, DAL (as Class Library) and UI (as Web project) and reference your BAL into UI.



Data Access Layer
Lets proceed with desiging 3-Tier architecture. To do that lets proceed with DAL, BAL and then UI. Add a class named by right clicking App_Code folder. (In my case I have a 3-Tier folder inside App_Code folder, you can directly add inside App_Code or you can create a separate project for DAL and add reference of this project into your BAL.) and copy-paste folowing code (Your can overwrite your default written code for the class file by pasting this code). Here, I have assumed that you will create the respective stored procedure yourself into the database or you may download attachment from http://www.dotnetfunda.com/articles/article18.aspx article and look for App_Data folder for complete database structure and stored procedure for this article.

Data Access Layer (DAL)

Code for Data Access Layer
using System;

using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;
using System.Data.SqlClient;
    /// <summary>
    /// Summary description for PersonDAL3
    /// </summary>

    public class PersonDAL3
    {
        string connStr = ConfigurationManager.ConnectionStrings["TutTestConn"].ToString();
        public PersonDAL3()
        {
        }
        /// <summary>
        /// Used to insert records into database
        /// </summary>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public int Insert(string firstName, string lastName, int age)
        {
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            SqlCommand dCmd = new SqlCommand("InsertData", conn);
            dCmd.CommandType = CommandType.StoredProcedure;
            try
            {
                dCmd.Parameters.AddWithValue("@firstName", firstName);
                dCmd.Parameters.AddWithValue("@lastName", lastName);
                dCmd.Parameters.AddWithValue("@age", age);
                return dCmd.ExecuteNonQuery();
            }
            catch
            {
                throw;
            }
            finally
            {
                dCmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
        }
        /// <summary>
        /// Update record into database
        /// </summary>
        /// <param name="personID"></param>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public int Update(int personID, string firstName, string lastName, int age)
        {
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            SqlCommand dCmd = new SqlCommand("UpdateData", conn);
            dCmd.CommandType = CommandType.StoredProcedure;
            try
            {
                dCmd.Parameters.AddWithValue("@firstName", firstName);
                dCmd.Parameters.AddWithValue("@lastName", lastName);
                dCmd.Parameters.AddWithValue("@age", age);
                dCmd.Parameters.AddWithValue("@personID", personID);
                return dCmd.ExecuteNonQuery();
            }
            catch
            {
                throw;
            }
            finally
            {
                dCmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
        }

        /// <summary>
        /// Load all records from database
       /// </summary>
        /// <returns></returns>
        public DataTable Load()
        {
            SqlConnection conn = new SqlConnection(connStr);
            SqlDataAdapter dAd = new SqlDataAdapter("LoadAll", conn);
            dAd.SelectCommand.CommandType = CommandType.StoredProcedure;
            DataSet dSet = new DataSet();
            try
            {
                dAd.Fill(dSet, "PersonTable");
                return dSet.Tables["PersonTable"];
            }
            catch
            {
                throw;
            }
            finally
            {
                dSet.Dispose();
                dAd.Dispose();
                conn.Close();
                conn.Dispose();
            }
        }
        /// <summary>
        /// Delete record from database
        /// </summary>
        /// <param name="personID"></param>
        /// <returns></returns>
        public int Delete(int personID)
        {
            SqlConnection conn = new SqlConnection(connStr);
            conn.Open();
            SqlCommand dCmd = new SqlCommand("DeleteData", conn);
            dCmd.CommandType = CommandType.StoredProcedure;
            try
            {
                dCmd.Parameters.AddWithValue("@personID", personID);
                return dCmd.ExecuteNonQuery();
            }
            catch
            {
                throw;
            }
            finally
            {
                dCmd.Dispose();
                conn.Close();
                conn.Dispose();
            }
        }
    }

In the above code, I have a member variable called connStr that is getting database connection string from my web.config file that is being used through out the class. I have separate method for inserting, deleting, updating records into database and loading records from database. I am not goint into details of how I am connecting database and manipulating the data just to make this tutorials short.

Business Access Layer (BAL)

Now, create a class named PersonBAL3 into App_Code folder by right clicking it and write respective methods for calling Insert, Delete, Update and Load methods of Data Access Layer class file (PersonDAL3) (In my case I have a 3-Tier folder inside App_Code folder, you can directly add inside App_Code or you can create a separate project for BAL and add reference of this project into your Presentation Layer). As we don't have any business logic here so simply instantiate the PersonDAL3 class of DAL and call methods. Below is the code for BAL (Your can overwrite your default written code for the class file by pasting this code).

Code for Business Access Layer
using System;
using System.Data;
using System.Configuration;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Web.UI.WebControls.WebParts;
using System.Web.UI.HtmlControls;

    /// <summary>
    /// Summary description for PersonBAL3
    /// </summary>
    public class PersonBAL3
    {
        public PersonBAL3()
        {

        }

        /// <summary>
        /// insert records into database
        /// </summary>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public int Insert(string firstName, string lastName, int age)
        {
            PersonDAL3 pDAL = new PersonDAL3();
            try
            {
                return pDAL.Insert(firstName, lastName, age);
            }
            catch
            {
                throw;
            }
            finally
            {
                pDAL = null;
            }
        }

        /// <summary>
        /// Update records into database
        /// </summary>
        /// <param name="personID"></param>
        /// <param name="firstName"></param>
        /// <param name="lastName"></param>
        /// <param name="age"></param>
        /// <returns></returns>
        public int Update(int personID, string firstName, string lastName, int age)
        {
            PersonDAL3 pDAL = new PersonDAL3();
            try
            {
                return pDAL.Update(personID, firstName, lastName, age);
            }
            catch
            {
                throw;
            }
            finally
            {
                pDAL = null;
            }
        }

        /// <summary>
        /// Load records from database
        /// </summary>
        /// <returns></returns>
        public DataTable Load()
        {
            PersonDAL3 pDAL = new PersonDAL3();
            try
            {
                return pDAL.Load();
            }
            catch
            {
                throw;
            }
            finally
            {
                pDAL = null;
            }
        }

        /// <summary>
        /// Delete record from database
        /// </summary>
        /// <param name="personID"></param>
        /// <returns></returns>
        public int Delete(int personID)
        {
            PersonDAL3 pDAL = new PersonDAL3();
            try
            {
                return pDAL.Delete(personID);
            }
            catch
            {
                throw;
            }
            finally
            {
                pDAL = null;
            }
        }

    }

Till now we haev our Business Access Layer and Database Access Layer ready. Now we have to write our Presentation Layer that will use our Business Access Layer methods. Lets create a form that will have three textboxes for FirstName, LastName and Age.

Presentation Layer




Create an Insert.aspx page (make is as Startup page) and copy paste following code to bring the insert form something like displaying in the picture.
Code for Insert Record form
<asp:Label ID="lblMessage" runat="Server" ForeColor="red" EnableViewState="False"></asp:Label>
        <table style="border:2px solid #cccccc;">
            <tr style="background-color:#507CD1;color:White;">
                <th colspan="3">Add Records</th>
            </tr>
            <tr>
                <td>
                    First Name:
                </td>
                <td>
                    <asp:TextBox ID="txtFirstName" runat="Server"></asp:TextBox>
                </td>
                <td>
                    <asp:RequiredFieldValidator ID="req1" runat="Server" Text="*" ControlToValidate="txtFirstName" 
                         Display="dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
            <tr>
                <td>
                    Last Name:
                </td>
                <td>
                    <asp:TextBox ID="txtLastName" runat="Server"></asp:TextBox>
                </td>
                <td>
                    <asp:RequiredFieldValidator ID="req2" runat="Server" Text="*" ControlToValidate="txtLastName" 
                      Display="dynamic"></asp:RequiredFieldValidator>
                </td>
            </tr>
            <tr>
                <td>
                    Age:
                </td>
                <td>
                    <asp:TextBox ID="txtAge" runat="Server" Columns="4"></asp:TextBox>
                </td>
                <td>
                    <asp:RequiredFieldValidator ID="req3" runat="Server" Text="*" ControlToValidate="txtAge" 
                        Display="dynamic"></asp:RequiredFieldValidator>
                    <asp:CompareValidator ID="Comp1" runat="Server" Text="Only integer" ControlToValidate="txtAge" 
                       Operator="DataTypeCheck" Type="Integer"></asp:CompareValidator>
                </td>
            </tr>
            <tr>
                <td>&nbsp;</td>
                <td>
                    <asp:Button ID="btnSubmit" runat="server" Text="Submit" OnClick="AddRecords" />
                </td>
            </tr>
        </table>


Now, lets write method that will fire when Submit button will be clicked on the from.
Code for AddRecords method
protected void AddRecords(object sender, EventArgs e)
    {
        //Lets validate the page first
        if (!Page.IsValid)
           return;

        int intResult = 0;
        // Page is valid, lets go ahead and insert records
        // Instantiate BAL object
        PersonBAL3 pBAL = new PersonBAL3();
        // Instantiate the object we have to deal with
        string firstName = txtFirstName.Text;
        string lastName = txtLastName.Text;
        int age = Int32.Parse(txtAge.Text);
        
        try
        {
            intResult = pBAL.Insert(firstName, lastName, age);
            if (intResult > 0)
                lblMessage.Text = "New record inserted successfully.";
            else
                lblMessage.Text = "FirstName [<b>"+ txtFirstName.Text +"</b>] alredy exists, try another name";

        }
        catch (Exception ee)
        {
            lblMessage.Text = ee.Message.ToString();
        }
        finally
        {
            pBAL = null;
        }        
    }

In the above code, first I am validating the page by using Page.IsValid method just to check if correct data has been entered. Then I have instantiated PersonBAL3 and calling Insert method of it (pBAL.Insert) by passing firstName, lastName, age as parameter.

Dispalying Records into GridView



Create a .aspx file called List.aspx and create a GridView something like displayed into the picture. To list the record into GridView that will also enable us to Edit, Delete record, copy paste following code.

Code for GridView
<asp:GridView ID="GridView1" runat="server" CellPadding="4" ForeColor="#333333" GridLines="None"
         DataKeyNames="PersonID" AutoGenerateEditButton="True" AutoGenerateColumns="False"
          OnRowEditing="EditRecord" OnRowUpdating="UpdateRecord" OnRowCancelingEdit="CancelRecord"
           OnRowDeleting="DeleteRecord" PageSize="5" >
            <FooterStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <RowStyle BackColor="#EFF3FB" />
            <EditRowStyle BackColor="#2ff1BF" />
            <SelectedRowStyle BackColor="#D1DDF1" Font-Bold="True" ForeColor="#333333" />
            <PagerStyle BackColor="#2461BF" ForeColor="White" HorizontalAlign="Center" />
            <HeaderStyle BackColor="#507CD1" Font-Bold="True" ForeColor="White" />
            <AlternatingRowStyle BackColor="White" />
            <Columns>
                <asp:BoundField DataField="PersonID" HeaderText="Person ID" ReadOnly="True"  SortExpression="PersonID" />
                <asp:TemplateField HeaderText="First Name" SortExpression="FirstName">
                    <ItemTemplate>
                        <%# Eval("FirstName") %>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="txtFName" runat="Server" Text='<%# Eval("FirstName") %>'></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Last Name" SortExpression="LastName">
                    <ItemTemplate>
                        <%# Eval("LastName") %>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="txtLName" runat="Server" Text='<%# Eval("LastName") %>'></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Age" SortExpression="Age">
                    <ItemTemplate>
                        <%# Eval("Age") %>
                    </ItemTemplate>
                    <EditItemTemplate>
                        <asp:TextBox ID="txtAge" runat="Server" Text='<%# Eval("Age") %>'></asp:TextBox>
                    </EditItemTemplate>
                </asp:TemplateField>
                <asp:TemplateField HeaderText="Delete?">
                    <ItemTemplate>
                        <span onclick="return confirm('Are you sure to Delete?')">
                            <asp:LinkButton ID="lnBD" runat="server" Text="Delete" CommandName="Delete"></asp:LinkButton>
                        </span>
                    </ItemTemplate>
                </asp:TemplateField>
            </Columns>
        </asp:GridView>


Code to Load records and Displaying Records into GridView
private DataTable BindGrid()
    {
        PersonBAL3 p = new PersonBAL3();
        try
        {
            DataTable dTable = p.Load();
            GridView1.DataSource = dTable;
            GridView1.DataBind();
        }
        catch (Exception ee)
        {
            lblMessage.Text = ee.Message.ToString();
        }
        finally
        {
            p = null;
        }

        return dTable;
    }

In the above method I am instantiating PersonBAL3 class and calling Load method to get the record into DataTable and binding it into GridView.

Code to Delete Records

protected void DeleteRecord(object sender, GridViewDeleteEventArgs e)
    {
        int personID = Int32.Parse(GridView1.DataKeys[e.RowIndex].Value.ToString()); 
        // instantiate BAL
        PersonBAL3 pBAL = new PersonBAL3();
        try
        {
            pBAL.Delete(personID);
            lblMessage.Text = "Record Deleted Successfully.";
        }
        catch (Exception ee)
        {
            lblMessage.Text = ee.Message.ToString();
        }
        finally
        {
            pBAL = null;
        }

        GridView1.EditIndex = -1;
        // Refresh the list
        BindGrid();
    }

Above method will fire when Delete link will be clicked on the GridView. In the above code, I am instantiating PersonBAL3 and calling Delete method by passing personID as parameter so that select reocrds will be deleted from datbase.

Code to Update records

protected void UpdateRecord(object sender, GridViewUpdateEventArgs e)
    {
        int personID = Int32.Parse(GridView1.DataKeys[e.RowIndex].Value.ToString());
        int intResult = 0;
        GridViewRow row = GridView1.Rows[e.RowIndex];

        TextBox tFN = (TextBox) row.FindControl("txtFName");
        TextBox tLN = (TextBox)row.FindControl("txtLName");
        TextBox tAge = (TextBox)row.FindControl("txtAge");

        // instantiate BAL
        PersonBAL3 pBAL = new PersonBAL3();

        try
        {
            intResult = pBAL.Update(personID, tFN.Text, tLN.Text, int.Parse(tAge.Text));
            if (intResult > 0)
                lblMessage.Text = "Record Updated Successfully.";
            else
                lblMessage.Text = "Record couldn't updated";
        }
        catch (Exception ee)
        {
            lblMessage.Text = ee.Message.ToString();
        }        finally
        {
            pBAL = null;
        }

        GridView1.EditIndex = -1;
        // Refresh the list
        BindGrid();
    }

Above method will fire when Update link will be clicked for a particular row of the GridView in edit mode. In the above method, I am instantiating PersonBAL3 and calling the Update method by p[assing required parameters.

Now we have all set to go, now just run your project and try inserting records. You can also navigate to another page your created (list.aspx) and try updating, deleting records.

Conclusion

By using 3-Tier architecture in your project you can achive

1. Seperation - the functionality is seperated from the data access and presentation so that it is more maintainable
2. Independence - layers are established so that if one is modified (to some extent) it will not affect other layers.
3. Reusability - As the layers are seperated, it can exist as a module that can be reused by other application by referencing it.

Hope this article helped you understanding 3-Tier architecture and desiging it.

Thanks and Happy Coding !!!
http://www.dotnetfunda.com/articles/article71.aspx