Tag Archives: Design & Patterns

Multilayered architecture

Before my job as a software developer I was a software trainer. Being a software trainer I learned many things related to software engineering. But I knew very little about what is called multilayered applications. Why we use multilayered applications? Currently I know at least something about this fine and useful approach of software engineering. Blogging means to me is to share thing and thoughts. Today I want to share my thoughts and knowledge of multilayered architecture. For that I am going to develop a very small demo application. We all know about Northwnd database and I am going to develop a simple ASP.NET application using this Northwnd database Customers table. This is not only sharing of knowledge but also I want to learn more, so I will expect comments from you so that I can learn more on this particular topic

What is Multilayered Application?
Multilayered application is a particular type of software designing procedure in software engineering. In multilayered application we generally use three layers.

  • Data Access Layer (DAL)
  • Business Logic Layer (BLL)
  • User Interface Layer (UI)

This can be sub divided in more layers depending on your application requirements. We write code for data access in DAL. Business logic for our application in BLL and user interface in UI. The advantage is that if there is a change in application business logic or data access or UI logic then we have to recompile the particular component only. So we implement this architecture to make our code maintainable. One other point is that I am also defining an interface for every component through which the calling component can interact. That means I decouple the service contract and the actual implementation of that contract. The calling component that will use that component will actually use the service contract (the interface) not the actual implementation. That is the benefit of interface which is in the definition: Interface is a service contract between service provider and service consumer. The service consumer does not need to know about the implementation details. They will only dealing with the service contract which is defined in the interface.

Northwnd solution
I have created a solution for Northwnd. This solution has different components. Like POCO, DAL, BLL and UI.

Solution explorer and class view

The POCO Layer
POCO stands for Plain Old CLR Object. Here I have created a class which represents Customers table in our application. The concept is database has relational format and our application has object oriented format. So we create objects which actually map to database tables. This is the main concept of object relational map per (ORM). There are some example of ORM are LINQ TO SQL, Entity Framework, NHibernate etc. Though for this application I am not using any of this, I am writing data access code and mapping code manually. POCO is independent and plain object. For this we can use customer object from our DAL, BLL and UI in object oriented manner. We do not need to wary about how to map this object to database, which is having relational format. The customer class is defined in this POCO section of our solution. This class can be used from any other layer of this solution (DAL, BLL and UI).

// ICustomer.cs
namespace Northwind.Poco
{
    public interface ICustomer
    {
        string CustomerId { get; set; }
        string CompanyName { get; set; }
        string ContactName { get; set; }
        string ContactTitle { get; set; }
        string Address { get; set; }
        string City { get; set; }
        string Region { get; set; }
        string PostalCode { get; set; }
        string Country { get; set; }
        string Phone { get; set; }
        string Fax { get; set; }
    }
}

// Customer.cs
namespace Northwind.Poco
{
    public class Customer : ICustomer
    {
        public string CustomerId { get; set; }
        public string CompanyName { get; set; }
        public string ContactName { get; set; }
        public string ContactTitle { get; set; }
        public string Address { get; set; }
        public string City { get; set; }
        public string Region { get; set; }
        public string PostalCode { get; set; }
        public string Country { get; set; }
        public string Phone { get; set; }
        public string Fax { get; set; }

        public Customer()
        {
            this.CustomerId = string.Empty;
            this.CompanyName = string.Empty;
            this.ContactName = string.Empty;
            this.ContactTitle = string.Empty;
            this.Address = string.Empty;
            this.Country = string.Empty;
            this.Region = string.Empty;
            this.City = string.Empty;
            this.PostalCode = string.Empty;
            this.Phone = string.Empty;
            this.Fax = string.Empty;
        }

        public Customer(string customerId, string companyName, string contactName, string contactTitle,
            string address, string region, string country, string city, string postalCode, string phone,
            string fax)
        {
            this.CustomerId = customerId;
            this.CompanyName = companyName;
            this.ContactName = contactName;
            this.ContactTitle = contactTitle;
            this.Address = address;
            this.Country = country;
            this.Region = region;
            this.City = city;
            this.PostalCode = postalCode;
            this.Phone = phone;
            this.Fax = fax;
        }
    }
}

The DAL Layer
Data access layer means, in this layer we write all the code which access the data from data source. We usually write the entire sql query or call store procedure from this layer.

In my application I write an another class called ‘Common’ in which I write all the common data access codes which can be used from the other classes in the DAL. This ‘Common’ class is only used inside the component, for that I declare this class as internal.

// Common.cs
using System;
using System.Data;
using System.Data.SqlClient;

namespace Northwind.Dal
{
    // Common DAL related work.
    internal class Common : IDisposable
    {
        private static string _connectionString;
        private SqlConnection _connection;
        private SqlCommand _command;
        private SqlDataReader _reader;
        private bool _isDisposed;

        static Common()
        {
            // It will hold the connection string when the class loads.
            _connectionString = System.Web.Configuration.WebConfigurationManager.ConnectionStrings
                ["NorthwindConnectionString"].ConnectionString;
        }

        public Common()
        {
		// Initialised the connection string for every object.
            this._connection = new SqlConnection(_connectionString);

            this._command = new SqlCommand()
            {
                Connection = this._connection,
                CommandType = CommandType.Text
            };
            this._isDisposed = false;
        }

        // Gets the state of the object weather it is disposed or not.
        protected bool IsDisposed
        {
            get
            {
                lock (this) // Make it thread safe.
                {
                    return this._isDisposed;
                }
            }
        }

        public SqlDataReader ExecuteReader(string query)
        {
            if (!this.IsDisposed) // Check if the object is live or disposed.
            {
                this._command.CommandText = query;

                this._connection.Open();
                this._reader = _command.ExecuteReader(CommandBehavior.CloseConnection);

                return this._reader;
            }
            else
            {
                throw new ObjectDisposedException("Common class object is already disposed.");
            }
        }

        public SqlDataReader ExecuteReader(string query, params  SqlParameter[] parameters)
        {
            if (!this.IsDisposed)
            {
                this._command.CommandText = query;
                foreach (SqlParameter parameter in parameters)
                {
                    this._command.Parameters.Add(parameter);
                }

                this._connection.Open();
                this._reader = _command.ExecuteReader(CommandBehavior.CloseConnection);

                return _reader;
            }
            else
            {
                throw new ObjectDisposedException("Common class object is already disposed.");
            }
        }

        public int ExecuteNonQuery(string query)
        {
            if (!this.IsDisposed)
            {
                this._command.CommandText = query;

                int effectedRecords = 0;
                try
                {
                    this._connection.Open();
                    effectedRecords = this._command.ExecuteNonQuery();
                }
                finally
                {
                    this._connection.Close();
                }

                return effectedRecords;
            }
            else
            {
                throw new ObjectDisposedException("Common class object is already disposed.");
            }
        }

        public int ExecuteNonQuery(string query, params SqlParameter[] parameters)
        {
            if (!this.IsDisposed)
            {
                this._command.CommandText = query;
                foreach (SqlParameter parameter in parameters)
                {
                    this._command.Parameters.Add(parameter);
                }

                int effectedRecords = 0;
                try
                {
                    this._connection.Open();
                    effectedRecords = this._command.ExecuteNonQuery();
                }
                finally
                {
                    this._connection.Close();
                }

                return effectedRecords;
            }
            else
            {
                throw new ObjectDisposedException("Common class object is already disposed.");
            }
        }

        public object ExecuteScalar(string query)
        {
            if (!this.IsDisposed)
            {
                this._command.CommandText = query;

                this._connection.Open();
                object scalarValue = this._command.ExecuteScalar();
                this._connection.Close();

                return scalarValue;
            }
            else
            {
                throw new ObjectDisposedException("Common class object is already disposed.");
            }
        }

        public object ExecuteScalar(string query, params SqlParameter[] parameters)
        {
            if (!this.IsDisposed)
            {
                this._command.CommandText = query;
                foreach (SqlParameter parameter in parameters)
                {
                    this._command.Parameters.Add(parameter);
                }

                object scalarValue = null;
                try
                {
                    this._connection.Open();
                    scalarValue = this._command.ExecuteScalar();
                }
                finally
                {
                    this._connection.Close();
                }

                return scalarValue;
            }
            else
            {
                throw new ObjectDisposedException("Common class object is already disposed.");
            }
        }

        public void Dispose()
        {
            lock (this) // Make it thread safe.
            {
                if (!this.IsDisposed)
                {
                    this.CleanUp();
                    this._isDisposed = true;
                    GC.SuppressFinalize(this); // No longer need finalizing.
                }
            }
        }

        // Derive class can override this method to implement specific logic.
        protected virtual void CleanUp()
        {
            if (this._connection != null)
            {
                this._connection.Dispose();
            }

            if (this._command != null)
            {
                this._command.Dispose();
            }

            if (this._reader != null)
            {
                this._reader.Dispose();
            }
        }
    }
}

This common class is used by the Customer class which is implementing the data access logic of customer entity.

// ICustomer.cs
using System;
using System.Collections.Generic;
using Poco = Northwind.Poco;

namespace Northwind.Dal
{
    public interface ICustomer : IDisposable
    {
        IList GetData();
        IList GetData(string customerId);
        int Create(Poco::ICustomer newCustomer);
        int Update(Poco::ICustomer existingCustomer);
        int Delete(string customerId);
    }
}

// Customer.cs
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Text;
using Northwind.Dal.Extentions;
using Poco = Northwind.Poco;

namespace Northwind.Dal
{
    public class Customer : ICustomer
    {
        private Common _common;
        private bool _isDisposed;

        public Customer()
        {
            this._common = new Common();
            this._isDisposed = false;
        }

        protected bool IsDisposed
        {
            get
            {
                lock (this) // Make it thread safe.
                {
                    return this._isDisposed;
                }
            }
        }

        protected virtual void CleanUp()
        {
            this._common.Dispose();
        }

        public IList GetData()
        {
            if (!this.IsDisposed) // Check if the object is live or disposed.
            {
                List customers;

                SqlDataReader reader = this._common.ExecuteReader("SELECT * FROM Customers");
                customers = reader.GetCustomers(); // Thanks to extention method.

                return customers;
            }
            else
            {
                throw new ObjectDisposedException("Dal.Customer object is already disposed.");
            }
        }

        public IList GetData(string customerId)
        {
            if (!this.IsDisposed)
            {
                SqlParameter customerIdParameter = new SqlParameter("@CustomerID", SqlDbType.NChar, 5)
                {
                    Value = customerId,
                    Direction = ParameterDirection.Input
                };

                List customers;
                SqlDataReader reader = this._common.ExecuteReader
                    ("SELECT * FROM Customers WHERE CustomerID = @CustomerID", customerIdParameter);
                customers = reader.GetCustomers(); // Thanks to extention method.

                return customers;
            }
            else
            {
                throw new ObjectDisposedException("Dal.Customer object is already disposed.");
            }
        }

        public int Create(Poco::ICustomer newCustomer)
        {
            if (!this.IsDisposed)
            {
                SqlParameter[] parameters = GetParameters(newCustomer);
                StringBuilder insertCommand = GetInsertCommand();
                int effectedRecords = this._common.ExecuteNonQuery(insertCommand.ToString(), parameters);

                return effectedRecords;
            }
            else
            {
                throw new ObjectDisposedException("Dal.Customer object is already disposed.");
            }
        }

        public int Update(Poco::ICustomer existingCustomer)
        {
            if (!this.IsDisposed)
            {
                SqlParameter[] parameters = GetParameters(existingCustomer);
                StringBuilder updateCommand = GetUpdateCommand();
                int effectedRecords = this._common.ExecuteNonQuery(updateCommand.ToString(), parameters);

                return effectedRecords;
            }
            else
            {
                throw new ObjectDisposedException("Dal.Customer object is already disposed.");
            }
        }

        public int Delete(string customerId)
        {
            if (!this.IsDisposed)
            {
                SqlParameter customerIdParameter = new SqlParameter("@CustomerID", SqlDbType.NChar, 5)
                {
                    Value = customerId,
                    Direction = ParameterDirection.Input
                };

                int effectedRecords = this._common.ExecuteNonQuery
                    ("DELETE Customers WHERE CustomerID = @CustomerId", customerIdParameter);

                return effectedRecords;
            }
            else
            {
                throw new ObjectDisposedException("Dal.Customer object is already disposed.");
            }
        }

        public void Dispose()
        {
            lock (this) // Make it thread safe.
            {
                if (!this.IsDisposed)
                {
                    this.CleanUp();
                    this._isDisposed = true;
                    GC.SuppressFinalize(this); // No longer need finalizing.
                }
            }
        }

        private static SqlParameter[] GetParameters(Poco::ICustomer newCustomer)
        {
            SqlParameter customerIdParameter = new SqlParameter("@CustomerID", SqlDbType.NChar, 5)
            {
                Value = newCustomer.CustomerId != null ? newCustomer.CustomerId : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter companyNameParameter = new SqlParameter("@CompanyName", SqlDbType.NVarChar, 40)
            {
                Value = newCustomer.CompanyName != null ? newCustomer.CompanyName : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter contactNameParameter = new SqlParameter("@ContactName", SqlDbType.NVarChar, 30)
            {
                Value = newCustomer.ContactName != null ? newCustomer.ContactName : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter contactTitleParameter = new SqlParameter("@ContactTitle", SqlDbType.NVarChar, 30)
            {
                Value = newCustomer.ContactTitle != null ? newCustomer.ContactTitle : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter addressParameter = new SqlParameter("@Address", SqlDbType.NVarChar, 60)
            {
                Value = newCustomer.Address != null ? newCustomer.Address : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter cityParameter = new SqlParameter("@City", SqlDbType.NVarChar, 15)
            {
                Value = newCustomer.City != null ? newCustomer.City : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter regionParameter = new SqlParameter("@Region", SqlDbType.NVarChar, 15)
            {
                Value = newCustomer.Region != null ? newCustomer.Region : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter postalCodeParameter = new SqlParameter("@PostalCode", SqlDbType.NVarChar, 10)
            {
                Value = newCustomer.PostalCode != null ? newCustomer.PostalCode : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter countryParameter = new SqlParameter("@Country", SqlDbType.NVarChar, 15)
            {
                Value = newCustomer.Country != null ? newCustomer.Country : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter phoneParameter = new SqlParameter("@Phone", SqlDbType.NVarChar, 24)
            {
                Value = newCustomer.Phone != null ? newCustomer.Phone : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter faxParameter = new SqlParameter("@Fax", SqlDbType.NVarChar, 24)
            {
                Value = newCustomer.Fax != null ? newCustomer.Fax : string.Empty,
                Direction = ParameterDirection.Input
            };

            SqlParameter[] parameters =
            {
                customerIdParameter,
                companyNameParameter,
                contactNameParameter,
                contactTitleParameter,
                addressParameter,
                cityParameter,
                countryParameter,
                postalCodeParameter,
                phoneParameter,
                faxParameter,
                regionParameter
            };

            return parameters;
        }

        private static StringBuilder GetInsertCommand()
        {
            StringBuilder insertCommand = new StringBuilder();

            insertCommand = insertCommand.Append("INSERT INTO Customers");
            insertCommand = insertCommand.Append("(CustomerId, CompanyName, ContactName, ContactTitle, ");
            insertCommand = insertCommand.Append("Country, Address, City, Region, PostalCode, Phone, Fax)");
            insertCommand = insertCommand.Append("VALUES (@CustomerId, @CompanyName, @ContactName,");
            insertCommand = insertCommand.Append("@ContactTitle, @Country, @Address, @City, @Region, ");
            insertCommand = insertCommand.Append("@PostalCode, @Phone, @Fax)");

            return insertCommand;
        }

        private static StringBuilder GetUpdateCommand()
        {
            StringBuilder insertCommand = new StringBuilder();

            insertCommand = insertCommand.Append("UPDATE Customers ");
            insertCommand = insertCommand.Append("SET CompanyName = @CompanyName, ");
            insertCommand = insertCommand.Append("ContactName = @ContactName, ContactTitle = @ContactTitle, ");
            insertCommand = insertCommand.Append("Country = @Country, Address = @Address, City = @City, ");
            insertCommand = insertCommand.Append("Region = @Region, PostalCode = @PostalCode, Phone = @Phone, Fax = @Fax ");
            insertCommand = insertCommand.Append("WHERE CustomerId = @CustomerId");

            return insertCommand;
        }
    }
}

Another class in the DAL component is a static one and this is for extension method for ‘SqlDataReader’ class. Extension method is a procedure by which we can extents an existing type with non object oriented manner.

// SqlDataReaderExtention.cs
using System.Collections.Generic;
using System.Data.SqlClient;

namespace Northwind.Dal.Extentions
{
    // Having the extention method of SqlDataReader.
    internal static class SqlDataReaderExtention
    {
        // Get customer collection from data reader.
        public static List GetCustomers(this SqlDataReader reader)
        {
            List customers = new List();

            if (reader.HasRows)
            {
                int customerIdOrdinal = reader.GetOrdinal("CustomerID");
                int companyNameOrdinal = reader.GetOrdinal("CompanyName");
                int contactNameOrdinal = reader.GetOrdinal("ContactName");
                int contactTitleOrdinal = reader.GetOrdinal("ContactTitle");
                int addressOrdinal = reader.GetOrdinal("Address");
                int cityOrdinal = reader.GetOrdinal("City");
                int regionOrdinal = reader.GetOrdinal("Region");
                int postalCodeOrdinal = reader.GetOrdinal("PostalCode");
                int countryOrdinal = reader.GetOrdinal("Country");
                int phoneOrdinal = reader.GetOrdinal("Phone");
                int faxOrdinal = reader.GetOrdinal("Fax");

                while (reader.Read())
                {
                    Northwind.Poco.ICustomer customer = new Northwind.Poco.Customer();

                    customer.CustomerId = GetStringOrEmpty(reader, customerIdOrdinal);
                    customer.CompanyName = GetStringOrEmpty(reader, companyNameOrdinal);
                    customer.ContactName = GetStringOrEmpty(reader, contactNameOrdinal);
                    customer.ContactTitle = GetStringOrEmpty(reader, contactTitleOrdinal);
                    customer.Address = GetStringOrEmpty(reader, addressOrdinal);
                    customer.City = GetStringOrEmpty(reader, cityOrdinal);
                    customer.Region = GetStringOrEmpty(reader, regionOrdinal);
                    customer.PostalCode = GetStringOrEmpty(reader, postalCodeOrdinal);
                    customer.Country = GetStringOrEmpty(reader, countryOrdinal);
                    customer.Phone = GetStringOrEmpty(reader, phoneOrdinal);
                    customer.Fax = GetStringOrEmpty(reader, faxOrdinal);

                    customers.Add(customer);
                }
            }

            return customers;
        }

        // Return the string value if present or empty string if null.
        private static string GetStringOrEmpty(SqlDataReader reader, int ordinal)
        {
            return !reader.IsDBNull(ordinal) ? reader.GetString(ordinal) : string.Empty;
        }
    }
}

The BLL Layer
Every mid level to high level application has some business logic. In three layered architecture we writes business logic in BLL. In simple term, if data access layer has the responsibility to access the data source then data manipulation is done in business logic layer. In the case of data retrieval it makes the data ready for UI and in the case of insert, update and delete it makes the data ready for data access layer. So it actually stay just between the DAL and UI.

Though there is not much business logic for this demo application. But in real world there are usually much to do in BLL.

// ICustomer.cs
using System;
using System.Collections.Generic;
using Poco = Northwind.Poco;

namespace Northwind.Bll
{
    public interface ICustomer : IDisposable
    {
        IList GetData();
        IList GetData(string customerId);
        int Create(Poco::ICustomer newCustomer);
        int Update(Poco::ICustomer existingCustomer);
        int Delete(string customerId);
    }
}

// Customer.cs
using System;
using System.Collections.Generic;
using Dal = Northwind.Dal;
using Poco = Northwind.Poco;

namespace Northwind.Bll
{
    public class Customer : ICustomer
    {
        private Dal::ICustomer _customer; // _customer is a Interface type to decouple the DAL.
        private bool _isDisposed;

        public Customer()
        {
            this._customer = new Dal::Customer();
            this._isDisposed = false;
        }

        protected bool IsDisposed
        {
            get
            {
                lock (this) // Make it thread safe.
                {
                    return this._isDisposed;
                }
            }
        }

        protected virtual void CleanUp()
        {
            this._customer.Dispose();
        }

        public IList GetData()
        {
            if (!this.IsDisposed) // Check if the object is live or disposed.
            {
                return this._customer.GetData();
            }
            else
            {
                throw new ObjectDisposedException("Bll.Customer class object is already disposed.");
            }
        }

        public IList GetData(string customerId)
        {
            if (!this.IsDisposed)
            {
                return this._customer.GetData(customerId);
            }
            else
            {
                throw new ObjectDisposedException("Bll.Customer class object is already disposed.");
            }
        }

        public int Create(Poco::ICustomer newCustomer)
        {
            if (!this.IsDisposed)
            {
                return this._customer.Create(newCustomer);
            }
            else
            {
                throw new ObjectDisposedException("Bll.Customer class object is already disposed.");
            }
        }

        public int Update(Poco::ICustomer existingCustomer)
        {
            if (!this.IsDisposed)
            {
                return this._customer.Update(existingCustomer);
            }
            else
            {
                throw new ObjectDisposedException("Bll.Customer class object is already disposed.");
            }
        }

        public int Delete(string customerId)
        {
            if (!this.IsDisposed)
            {
                return this._customer.Delete(customerId);
            }
            else
            {
                throw new ObjectDisposedException("Bll.Customer class object is already disposed.");
            }
        }

        public void Dispose()
        {
            lock (this) // Make it thread safe.
            {
                if (!this.IsDisposed)
                {
                    this.CleanUp();
                    this._isDisposed = true;
                    GC.SuppressFinalize(this); // No longer need finalizing.
                }
            }
        }
    }
}

In the DAL and BLL I also do deterministic life cycle memory management.

The UI Layer
The main responsibility of UI is to render the result and the user interaction. I have implemented the validation logic in UI using ASP.NET validator control. There are two aspx pages. One to show the customer data in a ‘GridView’ control and another is edit, delete and insert data.

Customer.aspx

<%@ Page Title="Customer" Language="C#" AutoEventWireup="true" CodeBehind="Customer.aspx.cs"
    Inherits="Northwind.Ui.Customer" MasterPageFile="~/Site.Master" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <asp:GridView ID="grvCustomers" runat="server">
        <Columns>
            <asp:HyperLinkField DataTextField="CompanyName" DataNavigateUrlFields="CustomerId"
                DataNavigateUrlFormatString="~/ManageCustomer.aspx?CustomerId={0}" HeaderText="Company Name" />
            <asp:BoundField DataField="ContactName" HeaderText="Contact Name" />
            <asp:BoundField DataField="ContactTitle" HeaderText="Contact Title" />
            <asp:BoundField DataField="Address" HeaderText="Address" />
            <asp:BoundField DataField="Country" HeaderText="Country" />
            <asp:BoundField DataField="Region" HeaderText="Region" />
            <asp:BoundField DataField="City" HeaderText="City" />
            <asp:BoundField DataField="PostalCode" HeaderText="Postal Code" />
            <asp:BoundField DataField="Phone" HeaderText="Phone" />
            <asp:BoundField DataField="Fax" HeaderText="Fax" />
        </Columns>
    </asp:GridView>
</asp:Content>

// Customer.aspx.cs
using System;
using System.Collections.Generic;
using System.Web.UI;

namespace Northwind.Ui
{
    public partial class Customer : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            IList customers;

            // customer is an interface type to decouple the BLL.
            using (Northwind.Bll.ICustomer customer = new Northwind.Bll.Customer())
            {
                customers = customer.GetData();
            }

            if (customers != null)
            {
                this.grvCustomers.DataSource = customers;
                this.grvCustomers.DataBind();
            }
        }
    }
}

In manage customer page I have used a ‘DetailsView’ control to insert update and delete.

ManageCustomer.aspx

<%@ Page Title="Manage Customer" Language="C#" AutoEventWireup="true" CodeBehind="ManageCustomer.aspx.cs"
    Inherits="Northwind.Ui.ManageCustomer" MasterPageFile="~/Site.Master" %>

<asp:Content ID="Content1" ContentPlaceHolderID="head" runat="server">
</asp:Content>
<asp:Content ID="Content2" ContentPlaceHolderID="ContentPlaceHolder1" runat="server">
    <asp:ValidationSummary ID="ValidationSummary1" runat="server" HeaderText="Please solve the following errors:"
        ShowMessageBox="True" />
    <asp:DetailsView ID="dtlvCustomer" runat="server" OnModeChanging="dtlvCustomer_ModeChanging"
        OnItemDeleting="dtlvCustomer_ItemDeleting" OnItemInserting="dtlvCustomer_ItemInserting"
        OnItemUpdating="dtlvCustomer_ItemUpdating" DataKeyNames="CustomerId">
        <Fields>
            <asp:TemplateField HeaderText="Customer Id">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("CustomerId") %>' ReadOnly="true"></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator1" runat="server" ControlToValidate="TextBox1"
                        Display="Dynamic" ErrorMessage="Please enter customer id" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox1" runat="server" Text='<%# Bind("CustomerId") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator2" runat="server" ControlToValidate="TextBox1"
                        Display="Dynamic" ErrorMessage="Please enter customer id" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label1" runat="server" Text='<%# Bind("CustomerId") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Company Name">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("CompanyName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator3" runat="server" ControlToValidate="TextBox2"
                        Display="Dynamic" ErrorMessage="Please enter company name" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox2" runat="server" Text='<%# Bind("CompanyName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator4" runat="server" ControlToValidate="TextBox2"
                        Display="Dynamic" ErrorMessage="Please enter company name" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label2" runat="server" Text='<%# Bind("CompanyName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Contact Name">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox3" runat="server" Text='<%# Bind("ContactName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator5" runat="server" ControlToValidate="TextBox3"
                        Display="Dynamic" ErrorMessage="Please enter contact name" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox3" runat="server" Text='<%# Bind("ContactName") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator6" runat="server" ControlToValidate="TextBox3"
                        Display="Dynamic" ErrorMessage="Please enter contact name" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label3" runat="server" Text='<%# Bind("ContactName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Contact Title">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox4" runat="server" Text='<%# Bind("ContactTitle") %>'></asp:TextBox>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox4" runat="server" Text='<%# Bind("ContactTitle") %>'></asp:TextBox>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label4" runat="server" Text='<%# Bind("ContactTitle") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Country">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox6" runat="server" Text='<%# Bind("Country") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator7" runat="server" ControlToValidate="TextBox6"
                        Display="Dynamic" ErrorMessage="Please enter country" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox6" runat="server" Text='<%# Bind("Country") %>'></asp:TextBox>
                    <asp:RequiredFieldValidator ID="RequiredFieldValidator8" runat="server" ControlToValidate="TextBox6"
                        Display="Dynamic" ErrorMessage="Please enter country" SetFocusOnError="True">*</asp:RequiredFieldValidator>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label6" runat="server" Text='<%# Bind("Country") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Region">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox7" runat="server" Text='<%# Bind("Region") %>'></asp:TextBox>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox7" runat="server" Text='<%# Bind("Region") %>'></asp:TextBox>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label7" runat="server" Text='<%# Bind("Region") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="City">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox8" runat="server" Text='<%# Bind("City") %>'></asp:TextBox>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox8" runat="server" Text='<%# Bind("City") %>'></asp:TextBox>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label8" runat="server" Text='<%# Bind("City") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Postal Code">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox9" runat="server" Text='<%# Bind("PostalCode") %>'></asp:TextBox>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox9" runat="server" Text='<%# Bind("PostalCode") %>'></asp:TextBox>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label9" runat="server" Text='<%# Bind("PostalCode") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Phone">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox10" runat="server" Text='<%# Bind("Phone") %>'></asp:TextBox>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox10" runat="server" Text='<%# Bind("Phone") %>'></asp:TextBox>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label10" runat="server" Text='<%# Bind("Phone") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Fax">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox11" runat="server" Text='<%# Bind("Fax") %>'></asp:TextBox>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox11" runat="server" Text='<%# Bind("Fax") %>'></asp:TextBox>
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label11" runat="server" Text='<%# Bind("Fax") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField HeaderText="Address">
                <EditItemTemplate>
                    <asp:TextBox ID="TextBox12" runat="server" SkinID="MultilineTextBox" TextMode="MultiLine"
                        Text='<%# Bind("Address") %>' />
                </EditItemTemplate>
                <InsertItemTemplate>
                    <asp:TextBox ID="TextBox12" runat="server" SkinID="MultilineTextBox" TextMode="MultiLine"
                        Text='<%# Bind("Address") %>' />
                </InsertItemTemplate>
                <ItemTemplate>
                    <asp:Label ID="Label5" runat="server" Text='<%# Bind("Address") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:TemplateField>
                <ItemTemplate>
                    <div class="RightAlign">
                        <asp:Button ID="btnEdit" runat="server" Text="Edit" CommandName="Edit" CausesValidation="false" />
                        <asp:Button ID="btnDelete" runat="server" Text="Delete" CommandName="Delete" CausesValidation="false" />
                        <asp:Button ID="btnNew" runat="server" Text="New" CommandName="New" CausesValidation="false" />
                    </div>
                </ItemTemplate>
                <EditItemTemplate>
                    <div class="RightAlign">
                        <asp:Button ID="btnUpdate" runat="server" Text="Update" CommandName="Update" />
                        <asp:Button ID="btnCancel" runat="server" Text="Cancel" CommandName="Cancel" CausesValidation="false" />
                    </div>
                </EditItemTemplate>
                <InsertItemTemplate>
                    <div class="RightAlign">
                        <asp:Button ID="btnInsert" runat="server" Text="Insert" CommandName="Insert" />
                        <asp:Button ID="btnCancel" runat="server" Text="Cancel" CommandName="Cancel" CausesValidation="false" />
                    </div>
                </InsertItemTemplate>
            </asp:TemplateField>
        </Fields>
    </asp:DetailsView>
</asp:Content>
// ManageCustomer.aspx.cs
using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Web.UI;
using System.Web.UI.WebControls;
using Bll = Northwind.Bll;
using Poco = Northwind.Poco;

namespace Northwind.Ui
{
    public partial class ManageCustomer : Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!this.IsPostBack)
            {
                if (Request.QueryString["CustomerId"] != null)
                {
                    IList customers = this.GetCustomers();
                    this.BindDetailsView(customers);
                }
            }
        }

        protected void dtlvCustomer_ModeChanging(object sender, DetailsViewModeEventArgs e)
        {
            dtlvCustomer.ChangeMode(e.NewMode);
            IList customers = this.GetCustomers();
            this.BindDetailsView(customers);
        }

        protected void dtlvCustomer_ItemInserting(object sender, DetailsViewInsertEventArgs e)
        {
            if (this.IsValid)
            {
                try
                {
                    Poco::ICustomer newCustomer = this.GetCustomerObject();
                    using (Bll::ICustomer blCustomer = new Bll::Customer())
                    {
                        blCustomer.Create(newCustomer);
                    }

                    Response.Redirect("~/Customer.aspx"); // Go and show all data.
                }
                catch (SqlException ex)
                {
                    Response.Write(ex.ToString());
                }
                catch (ObjectDisposedException ex)
                {
                    Response.Write(ex.ToString());
                }
                catch (Exception ex)
                {
                    Response.Write(ex.ToString());
                }
            }
        }

        protected void dtlvCustomer_ItemUpdating(object sender, DetailsViewUpdateEventArgs e)
        {
            if (this.IsValid)
            {
                try
                {
                    Poco::ICustomer existingCustomer = this.GetCustomerObject();
                    using (Bll::ICustomer blCustomer = new Bll::Customer())
                    {
                        blCustomer.Update(existingCustomer);
                    }

                    // Back to read only view.
                    dtlvCustomer.ChangeMode(DetailsViewMode.ReadOnly);
                    IList customers = this.GetCustomers();
                    this.BindDetailsView(customers);
                }
                catch (SqlException ex)
                {
                    Response.Write(ex.ToString());
                }
                catch (ObjectDisposedException ex)
                {
                    Response.Write(ex.ToString());
                }
                catch (Exception ex)
                {
                    Response.Write(ex.ToString());
                }
            }
        }

        protected void dtlvCustomer_ItemDeleting(object sender, DetailsViewDeleteEventArgs e)
        {
            try
            {
                using (Bll::ICustomer blCustomer = new Bll::Customer())
                {
                    if (Request.QueryString["CustomerId"] != null)
                    {
                        blCustomer.Delete(Request.QueryString["CustomerId"]);
                    }

                    Response.Redirect("~/Customer.aspx"); // Go and show all data.
                }
            }
            catch (SqlException ex)
            {
                Response.Write(ex.ToString());
            }
            catch (ObjectDisposedException ex)
            {
                Response.Write(ex.ToString());
            }
            catch (Exception ex)
            {
                Response.Write(ex.ToString());
            }
        }

        private IList GetCustomers()
        {
            IList customers;

            using (Northwind.Bll.ICustomer customer = new Northwind.Bll.Customer())
            {
                customers = customer.GetData(Request.QueryString["CustomerId"]);
            }

            return customers;
        }

        private void BindDetailsView(IList customers)
        {
            if (customers != null)
            {
                this.dtlvCustomer.DataSource = customers;
                this.dtlvCustomer.DataBind();
            }
        }

        private Poco::ICustomer GetCustomerObject()
        {
            // Create customer object with data from details view.
            Poco::ICustomer newCustomer = new Poco::Customer()
            {
                CustomerId = ((TextBox)dtlvCustomer.Rows[0].Cells[1].FindControl("TextBox1")) != null
                    ? ((TextBox)dtlvCustomer.Rows[0].Cells[1].FindControl("TextBox1")).Text : string.Empty,
                CompanyName = ((TextBox)dtlvCustomer.Rows[1].Cells[1].FindControl("TextBox2")) != null
                    ? ((TextBox)dtlvCustomer.Rows[1].Cells[1].FindControl("TextBox2")).Text : string.Empty,
                ContactName = ((TextBox)dtlvCustomer.Rows[2].Cells[1].FindControl("TextBox3")) != null
                    ? ((TextBox)dtlvCustomer.Rows[2].Cells[1].FindControl("TextBox3")).Text : string.Empty,
                ContactTitle = ((TextBox)dtlvCustomer.Rows[3].Cells[1].FindControl("TextBox4")) != null
                    ? ((TextBox)dtlvCustomer.Rows[3].Cells[1].FindControl("TextBox4")).Text : string.Empty,
                Country = ((TextBox)dtlvCustomer.Rows[10].Cells[1].FindControl("TextBox6")) != null
                    ? ((TextBox)dtlvCustomer.Rows[10].Cells[1].FindControl("TextBox6")).Text : string.Empty,
                Region = ((TextBox)dtlvCustomer.Rows[4].Cells[1].FindControl("TextBox7")) != null
                    ? ((TextBox)dtlvCustomer.Rows[4].Cells[1].FindControl("TextBox7")).Text : string.Empty,
                City = ((TextBox)dtlvCustomer.Rows[5].Cells[1].FindControl("TextBox8")) != null
                    ? ((TextBox)dtlvCustomer.Rows[5].Cells[1].FindControl("TextBox8")).Text : string.Empty,
                PostalCode = ((TextBox)dtlvCustomer.Rows[6].Cells[1].FindControl("TextBox9")) != null
                    ? ((TextBox)dtlvCustomer.Rows[6].Cells[1].FindControl("TextBox9")).Text : string.Empty,
                Phone = ((TextBox)dtlvCustomer.Rows[7].Cells[1].FindControl("TextBox10")) != null
                    ? ((TextBox)dtlvCustomer.Rows[7].Cells[1].FindControl("TextBox10")).Text : string.Empty,
                Fax = ((TextBox)dtlvCustomer.Rows[8].Cells[1].FindControl("TextBox11")) != null
                    ? ((TextBox)dtlvCustomer.Rows[8].Cells[1].FindControl("TextBox11")).Text : string.Empty,
                Address = ((TextBox)dtlvCustomer.Rows[9].Cells[1].FindControl("TextBox12")) != null
                    ? ((TextBox)dtlvCustomer.Rows[9].Cells[1].FindControl("TextBox12")).Text : string.Empty
            };

            return newCustomer;
        }
    }
}

multilayered architecture

Is the design ok? Am I missing something? Please give comments.
Download full code here

Advertisements

Polymorphism in software development

The word is ‘Polymorphism’. Greek origin. Meaning is different functions under same interface. For example, steering of a car. It may be manual steering or power steering. Their internal function is different. But for a driver steering interface is same. So that one driver can drive with two types of steering. There is no need to learn two different driving skills for two types of steering. So by polymorphism we can use our knowledge in different closely related implementations. Because for us the interface is same. We are familiar with that interface.

Polymorphism in software development

Just like our practical example polymorphism is applicable in our software development. Suppose we are developing software for client. After some times we also upgrade that software and add some new functionality in to that, as well as change some existing functionality. If we maintain the user interface as unchanged or familiar when we are upgrading the functionality, then it is better to use the new functionality for our client without any further training. Because for the client user interface is same, only under the same user interface the functionality has changed. This is polymorphism.

Polymorphism in Microsoft .NET Framework

In this post I will mention what are types of polymorphism and how we can implement polymorphism in our daily software development with Microsoft .NET Framework and C# programming language.

What are the types of polymorphism?

There are two types of polymorphism in .NET.

  • Static or compilation time polymorphism. (Early binding)
  • Dynamic or runtime polymorphism. (Late binding)

Static polymorphism

Static polymorphism means the polymorphism which is performed in compilation time. We can implement static polymorphism in three ways.

  • Method overloading.
  • Operator overloading.
  • Indexer overloading.

Method overloading

When we declare more than two methods in a class having the same name but different parameter list, then it is called method overloading. The methods are called overloaded methods. When we call the method, depending on the parameter list compiler makes a decision that which version of the overloaded method will be called.

public int Add(int x, int y)
{
    return (x + y);
}

public double Add(double x, double y)
{
    return (x + y);
}

Operator overloading

We use operator to perform some operation on operands. There are many types of operator in programming language. Now the operands are primitive types. If we want to use the objects of our user defined class to be as operands of a particular operator, we have to overloads that particular operator in our user defined class.

class MyClass
{
    private int x;
    private int y;

    public MyClass()
    {
    }

    public MyClass(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public static MyClass operator +(MyClass obj1, MyClass obj2)
    {
        MyClass obj = new MyClass()
        {
            x = obj1.x + obj2.x,
            y = obj1.y + obj2.y
        };
        return obj;
    }

    public override string ToString()
    {
        return string.Format("X = {0}\nY = {1}", x, y);
    }
}

Indexer overloading

It is same as method overloading. Compiler decides which version of the indexer will be called based on the parameter type.

public string this[int index]
{
    get{ }
    set{ }
}

public string this[string position]
{
    get{ }
    set{ }
}

Dynamic polymorphism

Dynamic polymorphism means the polymorphism which is done not in compilation time but in runtime. There are four ways through which we can implement runtime polymorphism.

  • Method overriding.
  • Property overriding.
  • Polymorphism through Delegate.
  • Polymorphism through Interface.
  • Method and property overriding

Base class having general functionality, derive class means specific functionality. So base class having the methods and property which implement general functionality for all derive classes (some times they become abstract). Derive class may override that general functionality of base class to a more specific functionality. Then this is called overriding. Base class method or property is declared as ‘virtual’ and derive class method or property is declared as ‘override’. At runtime it is decided that what version (base or derive) will be called depending on the particular object.

class Base
{
    public virtual string SomeProperty
    {
        get
        {
            // get base implimentation.
        }
        set
        {
            // set base implimentation.
        }
    }

    public virtual string SomeMethod()
    {
        // Base implimentation.
    }
}

class Derive : Base
{
    public override string SomeProperty
    {
        get
        {
            // get derive implimentation.
        }
        set
        {
            // set derive implimentation.
        }
    }

    public override string SomeMethod()
    {
        // Derive implimentation.
    }
}

Notice its polymorphic nature.

Base b1 = new Base();
b1.SomeMethod(); // Call base version.

Base b2 = new Derive();
b2.SomeMethod(); // Call derive version.

Polymorphism through Delegate

Delegate means managed function pointer in .NET Framework. We can assign method reference in to a delegate, if the signature of the delegate and method are same. Through delegate we can create a polymorphic type of data structure.

delegate string GetMessage(string name);

class Welcome
{
    public string GetWelcomeMessage(string name)
    {
        return string.Format("Welcome {0}", name);<br />
    }
}

class Bye
{
    public string GetByeMessage(string name)
    {
        return string.Format("Bye {0}", name);
    }
}

class DelegateDemo
{
    static void Main(string[] args)
    {
        GetMessage g;

        Welcome w = new Welcome();
        g = w.GetWelcomeMessage;
        System.Console.WriteLine(g("Rahul")); // Out put: Welcome Rahul

        Bye b = new Bye();
        g = b.GetByeMessage;
        System.Console.WriteLine(g("Rahul")); // Out put: Bye Rahul
    }
}

Polymorphism through Interface

Interface is a service contract between service provider and service consumer, irrespective of how the service will be provided. Many different classes can implement a single interface with different implementation.

interface IGetMessage
{
    string Get(string name);
}

class Welcome : IGetMessage
{
    public string Get(string name)
    {
        return string.Format("Welcome {0}", name);
    }
}

class Bye : IGetMessage
{
    public string Get(string name)
    {
        return string.Format("Bye {0}", name);
    }
}

Notice its polymorphic nature.

IGetMessage message;

message = new Welcome();
System.Console.WriteLine(message.Get("Rahul")); // Out put: Welcome Rahul

message = new Bye();
System.Console.WriteLine(message.Get("Rahul")); // Out put: Bye Rahul

Here I have tried to show how polymorphism is implemented in software development through some small and easy to understand examples. Hope this will help you a lot in your development time.

You can also download this code from MSDN code sample.
Download from MSDN code sample

Thank you.