In this article I will explain with an example, how to implement Three Tier Architecture in ASP.Net using C#.
Database
I have made use of the following table Customers with the schema as follows.
I have already inserted few records in the table.
Note: You can download the database table SQL by clicking the download link below.
What is Three Tier Architecture?
Three Tier Architecture means, dividing a project into 3 layers i.e. Data Access Layer, Business Layer and the UI (Front End) Layer.
The benefit of the Three Tier Architecture is that these tiers are developed and maintained independently.
So it will not impact the others in case of any modification.
It improves the performance and scalability of the application.
Data Transfer Object
This layer is used to define the properties. It is used to transfer data between itself and the front end layer.
It does not contain any logic and it is only used to hold data.
In order to add Data Transfer Object project, select File from the menu and click Add, then New Project.
From the Add a new project dialog window, select Class Library and click Next.
Then, give a suitable Project Name i.e. DTO and click Create.
Once the project is created, we need to now create the Data Transfer Object (DTO) class for each Table. For example, to handle operations of Customers Table we are creating Customer class.
The class will contain properties with names similar to the Customers Table. This is necessary as it will be used to load the appropriate fields in appropriate properties i.e. CustomerId will be loaded in CustomerId property.
namespace DTO
{
///<summary>
/// Customer Data Transfer Object.
///</summary>
public class Customer
{
public int CustomerId { get; set; }
public string Name { get; set; }
public string Country { get; set; }
public DateTime BirthDate { get; set; }
public int Age { get; set; }
}
}
Data Access Layer (DAL)
Data Access Layer (DAL) is used to retrieve data from data store (database) separate from Business Layer (BL) and Front End (UI) layer.
Thus, if you have to change data stores, you don't have to rewriting the whole code again.
In order to add DAL, select File from the menu and click Add, then New Project.
From the Add a new project dialog window, select Class Library and click Next.
Then, give a suitable Project Name i.e. Data_Layer and click Create.
In order to use the DTO classes in the DAL project, you need to add the reference of DTO project to the DAL project. For that right click on the References, then click on Add Reference.
Now select the Projects node and check the DTO CheckBox and click OK.
Now we will create a new class in the DAL project i.e. Helper class, which will be used to keep all the necessary methods for Database operations.
Helper Class
The Connection String to the database will be fetched and stored in the ConString property.
The Helper Class consists of following methods.
Private methods
HasColumn: This method is used to check whether a Column exists in the DataRecord.
GetSingleObject: This method is used to read the Column value from SqlDataReader and set into the respective property.
Note: The name of the property in DTO classes and the Database Table must be exact same.
Public methods
The following methods are used by the Business Layer to perform the Database operations. Each of these methods accepts the following parameters.
sql – The query or the stored procedure name.
parameters – Default value null. The parameters to be passed to the query or stored procedure.
isStoredProc – Default value false. Value true means stored procedure.
Execute: Performs ExecuteNonQuery operation and returns rows affected.
GetScalar: Performs ExecuteScalar operation and returns single scalar value.
GetSingle: Performs ExecuteReader operation and returns single row i.e. single object.
GetList: Performs ExecuteReader operation and returns multiple rows i.e. List of objects.
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.SqlClient;
using System.Reflection;
namespace Data_Layer
{
public static class Helper
{
//Connection string to the Database.
private static string ConString = ConfigurationManager.ConnectionStrings["constr"].ConnectionString;
/// <summary>
/// Gets Single DTO object.
/// </summary>
/// <typeparam name="T">Dynamic object.</typeparam>
/// <param name="sql">SQL Query.</param>
/// <param name="parameters">Parameter collection.</param>
/// <param name="isStoredProc">Boolean value. True if Stored Procedure.</param>
/// <returns>DTO object.</returns>
public static T GetSingle<T>(this string sql, Dictionary<string, object> parameters = null, bool isStoredProc = false)
{
PropertyInfo[] info = typeof(T).GetProperties();
object t = Activator.CreateInstance(typeof(T));
using (SqlConnection con = new SqlConnection(Helper.ConString))
{
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = isStoredProc ? CommandType.StoredProcedure : CommandType.Text;
if (parameters != null)
{
foreach (var p in parameters)
{
cmd.Parameters.AddWithValue(p.Key, p.Value);
}
}
con.Open();
using (SqlDataReader sdr = cmd.ExecuteReader())
{
if (sdr.Read())
{
t = GetSingleObject<T>(sdr);
}
sdr.Close();
}
con.Close();
}
}
return (T)t;
}
/// <summary>
/// Gets List of DTO object.
/// </summary>
/// <typeparam name="T">Dynamic object.</typeparam>
/// <param name="sql">SQL Query.</param>
/// <param name="parameters">Parameter collection.</param>
/// <param name="isStoredProc">Boolean value. True if Stored Procedure.</param>
/// <returns>List of DTO objects.</returns>
public static List<T> GetList<T>(this string sql, Dictionary<string, object> parameters = null, bool isStoredProc = false)
{
PropertyInfo[] info = typeof(T).GetProperties();
List<T> t = new List<T>();
using (SqlConnection con = new SqlConnection(Helper.ConString))
{
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = isStoredProc ? CommandType.StoredProcedure : CommandType.Text;
if (parameters != null)
{
foreach (var p in parameters)
{
cmd.Parameters.AddWithValue(p.Key, p.Value);
}
}
con.Open();
using (SqlDataReader sdr = cmd.ExecuteReader())
{
while (sdr.Read())
{
t.Add(GetSingleObject<T>(sdr));
}
sdr.Close();
}
con.Close();
}
}
return t;
}
/// <summary>
/// Gets Single Scalar value.
/// </summary>
/// <param name="sql">SQL Query.</param>
/// <param name="parameters">Parameter collection.</param>
/// <param name="isStoredProc">Boolean value. True if Stored Procedure.</param>
/// <returns>Scalar object.</returns>
public static object GetScalar(this string sql, Dictionary<string, object> parameters = null, bool isStoredProc = false)
{
object t;
using (SqlConnection con = new SqlConnection(Helper.ConString))
{
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = isStoredProc ? CommandType.StoredProcedure : CommandType.Text;
if (parameters != null)
{
foreach (var p in parameters)
{
cmd.Parameters.AddWithValue(p.Key, p.Value);
}
}
con.Open();
t = cmd.ExecuteScalar();
con.Close();
}
}
return t;
}
/// <summary>
/// Executes query.
/// </summary>
/// <param name="sql">SQL Query.</param>
/// <param name="parameters">Parameter collection.</param>
/// <param name="isStoredProc">Boolean value. True if Stored Procedure.</param>
/// <returns>Rows affected.</returns>
public static int Execute(this string sql, Dictionary<string, object> parameters = null, bool isStoredProc = false)
{
int rowsAffected = 0;
using (SqlConnection con = new SqlConnection(Helper.ConString))
{
using (SqlCommand cmd = new SqlCommand(sql, con))
{
cmd.CommandType = isStoredProc ? CommandType.StoredProcedure : CommandType.Text;
if (parameters != null)
{
foreach (var p in parameters)
{
cmd.Parameters.AddWithValue(p.Key, p.Value);
}
}
con.Open();
rowsAffected = cmd.ExecuteNonQuery();
con.Close();
}
}
return rowsAffected;
}
/// <summary>
/// Reads data from DataReader.
/// </summary>
/// <typeparam name="T">Dynamic object.</typeparam>
/// <param name="sdr">SqlDataReader object</param>
/// <returns>DTO object.</returns>
private static T GetSingleObject<T>(this SqlDataReader sdr)
{
PropertyInfo[] info = type of(T).GetProperties();
object t = Activator.CreateInstance(type of(T));
foreach (PropertyInfo field in info)
{
if (sdr.HasColumn(field.Name))
{
field.SetValue(t, sdr[field.Name]);
}
}
return (T)t;
}
/// <summary>
/// Checks whether Column present in DataRecord.
/// </summary>
/// <param name="dr">DataRcord object.</param>
/// <param name="columnName">Column Name.</param>
/// <returns>Boolean value.</returns>
public static bool HasColumn(this IDataRecord dr, string columnName)
{
for (int i = 0; i < dr.FieldCount; i++)
{
if (dr.GetName(i).Equals(columnName, StringComparison.InvariantCultureIgnoreCase))
{
return true;
}
}
return false;
}
}
}
CustomerDL Class
Once the Helper class is complete, we need to now create the Data Layer (DL) class for each Table. For example, to handle operations of Customers Table we are creating CustomerDL class.
This Class consists of following methods.
GetCustomerName
This method accepts the CustomerId as parameter.
Inside this method, the SQL query is generated and the parameter is passed as Dictionary object to the GetScalar method.
Finally, the GetScalar method returns the Customer Name as scalar value.
GetCustomer
This method accepts the CustomerId as parameter.
Inside this method, the SQL query is generated and the parameter is passed as Dictionary object to the GetSingle method.
Finally, the GetSingle method returns single Customer.
GetCustomers
Inside this method, the SQL query is generated and GetList method is called.
Finally, the GetList method returns list of Customers.
using DTO;
using System;
using System.Collections.Generic;
namespace Data_Layer
{
public class CustomerDL
{
///<summary>
/// Gets Customer Name.
///</summary>
///<param name="customerId">CustomerId object.</param>
///<returns>Customer Name.</returns>
public static string GetCustomerName(int customerId)
{
string sql = "SELECT Name FROM Customers WHERE CustomerId=@CustomerId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@CustomerId", customerId);
return sql.GetScalar(parameters).ToString();
}
///<summary>
/// Gets Customer based on Id.
///</summary>
///<param name="customerId">CustomerId object.</param>
///<returns>Customer object.</returns>
public static Customer GetCustomer(int customerId)
{
string sql = "SELECT CustomerId, Name, Country, BirthDate FROM Customers WHERE CustomerId=@CustomerId";
Dictionary<string, object> parameters = new Dictionary<string, object>();
parameters.Add("@CustomerId", customerId);
Customer customer = sql.GetSingle<Customer>(parameters);
return customer;
}
///<summary>
/// Gets all Customers.
///</summary>
///<returns>List of Customers.</returns>
public static List<Customer> GetCustomers()
{
string sql = "SELECT CustomerId, Name, Country, BirthDate FROM Customers";
List<Customer> customers = sql.GetList<Customer>();
return customers;
}
}
}
Business Layer (BL)
The Business Layer (BL) act as an intermediate between the Front End Layer and the Data Access Layer.
This layer handles the business logic, business rules as well as calculations.
In order to add BL project, select File from the menu and click Add, then New Project.
From the Add a new project dialog window, select Class Library and click Next.
Then, give a suitable Project Name i.e. Business_Layer and click Create.
In order to use the DTO classes and Data_Layer classes in the BL project, you need to add their reference to the BL project by right clicking on the References in the Solution Explorer, then clicking on Add Reference.
Now select the Projects node and check the Data_Layer and DTO CheckBoxes and click OK.
CustomerBL
The following class inherits the ICustomerBL interface.
Inside this class, the implementation of all the methods are written.
Methods:–
GetCustomerName – This method returns name of the Customer based on the CustomerId value.
GetCustomer – This method returns details of Customer based on the CustomerId value.
GetCustomers – This method returns multiple Customers.
Inside GetCustomers method, a FOR EACH loop is executed over the list collection and Age is calculated based on the Current Year and the Birth Year.
using Data_Layer;
using DTO;
using System.Collections.Generic;
namespace Business_Layer
{
public class CustomerBL : ICustomerBL
{
///<summary>
/// Gets Customer Name.
///</summary>
///<param name="customerId">CustomerId object.</param>
///<returns>Customer Name.</returns>
public string GetCustomerName(int customerId)
{
return CustomerDL.GetCustomerName(customerId);
}
///<summary>
/// Gets Customer based on Id.
///</summary>
///<param name="customerId">CustomerId object.</param>
///<returns>Customer object.</returns>
public Customer GetCustomer(int customerId)
{
return CustomerDL.GetCustomer(customerId);
}
///<summary>
/// Gets all Customers.
///</summary>
///<returns>List of Customers.</returns>
public List<Customer> GetCustomers()
{
List<Customer> customers = CustomerDL.GetCustomers();
//Loop and calculate Age from Birth Date.
foreach (Customer customer in customers)
{
customer.Age = (DateTime.Today.Year - customer.BirthDate.Year);
}
return customers;
}
}
}
Now we will create a new interface in the BL project i.e. ICustomerBL class.
The interface implements following methods of the CustomerBL class.
GetCustomerName
GetCustomer
GetCustomers
using DTO;
using System.Collections.Generic;
namespace Business_Layer
{
public interface ICustomerBL
{
///<summary>
/// Gets Customer Name.
///</summary>
///<param name="customerId">CustomerId object.</param>
///<returns>Customer Name.</returns>
string GetCustomerName(int customerId);
///<summary>
/// Gets Customer based on Id.
///</summary>
///<param name="customerId">CustomerId object.</param>
///<returns>Customer object.</returns>
Customer GetCustomer(int customerId);
///<summary>
/// Gets all Customers.
///</summary>
///<returns>List of Customers.</returns>
List<Customer> GetCustomers();
}
}
Front End Layer
Front End (UI) Layer will be a Website project.
In order to add FrontEnd Layer, select File from the menu and click Add, then New Project.
From the Add a new project dialog window, select ASP.Net Empty Web Site and click Next.
Then, give a suitable Project Name i.e. Frontend_Layer and click Create.
In order to use the DTO and BL classes in the UI project, you need to add the reference of DTO and BL projects to the UI project. For that right click on the References, then click on Add Reference.
Now select the Projects node and check the DTO and Business_Layer and click OK.
Now, you will see DLL reference has been added to the project inside the Bin folder.
Finally, add a Web Form to your project by right clicking on the project, then clicking on Add and select Add New Item.
Then, in the Add New Item dialog window select Web Form and give a suitable name and click on Add.
Namespaces
You will need to inherit the following namespaces.
using Business_Layer;
using DTO;
Code
Inside the Page_Load event handler of page, an object of CustomerBL is created and the GetCustomerName, GetCustomer and GetCustomers methods are called.
protected void Page_Load(object sender, EventArgs e)
{
ICustomerBL customerBL = new CustomerBL();
//Get single Customer Name.
string name = customerBL.GetCustomerName(2);
//Get single Customer data.
Customer customer = customerBL.GetCustomer(2);
//Get multiple Customers data.
List<Customer> customers = customerBL.GetCustomers();
}
Screenshots
Returning Customer Name
Returning single Customer
Returning multiple Customers
Downloads