Writing Data Access Layer (DAL) and Business Access Layer (BAL) with CsharpGears Framework

Today I am going to show you how to write Data Access Layer (DAL) and Business Access Layer (BAL) using the CsharpGears Framework.
One of the most accepted approaches of the today's application architecture is the 3-tier architecture, or it's general version n-tier architecture. The idea is to separate the logic of the application in separate layers, each of which will be able to communicate in a standardized way with another layer. Most often, the tier that is responsible for displaying the data is called the presentation layer, which is capable of invoking the layer that holds all the business logic - namely the business layer. The business layer, however can either be invoked by the presentation layer and return the results of the execution to it. In certain situations, the business layer will need to access some amount of data in order to perform all the calculations. In that case, the business layer invokes the third layer - data layer. The data layer is responsible for retrieving the sets of data from some source - be it a database or xml file or web service - it can be literally anything.



Benefits of the three-tier architecture
Having the application separated in separate tiers (note: tier is synonym for layer) , brings a lot of advantages, among which are:
  • The application is divided in pieces that whose independence is brought to maximum, meaning that people can simultenously work on different layer, leading to cutting the development costs in both aspects of time and money.
  • Transparency. Each layer brings transparency for the other layers - this means that each layer minds it's own business, and when it needs help, it simply invokes other layers to help out. The good thing is, nobody interferes in the lives of the others. Moreover, the transparency brings flexibility, such as - the business layer does not care where the data layer gets the data from, its only concern is to get data. The presentation layer does not know nor cares who the data layer is, but it also does not know the details of what the business rules are; instead, the presentation layer only cares to display the results properly so that the end user sees what she needs to see. 

What is the Data Access Layer (DAL)?
Well, I have mentioned the data layer is the layer that cares about the data, but it might be essentially observed as the Data Storage Layer, with or without capabilities to format the data. The raw data retrieval is usually accomplished by means of using SQL stored procedures, SQL ad-hoc queries, XPath expressions etc. But the thing is, the retrieved data is not always well formatted to align with the benefits of the Object-oriented Programming. In the  case of SQL, (the more common I guess), although the data is retrieved as tables, sometimes it is not sufficient. A developer would still need to map that data to objects in the application. Having all this considered, one can define DAL as the part of the Data Layer that cares for mapping the retrieved rows into business classes (in C#, for instance). Please note that even if the DAL is often written in .NET, it still belongs to the Data Layer, do not directly assume that the Database represents the whole Data Layer.
What is Business Access Layer (BAL)?
BAL can be observed as an encapsulation of the business logic so that it keeps the transparency to the presentation layer. The part of the business layer that is responsible for invoking the business layer from upper layers - namely the presentation layer is called Business Access Layer. The BAL always returns objects to the presentation layer that it finds them suitable for displaying. BAL represents the encapsulation of the business layer.
Standardizing the Communication between Layers: Common Objects
Wouldn't it be nice if all the tiers communicated in the same fashion ? Of course, the more standardized the communication, the less effort to develop the application, because of the subtle error prevention the standardized communication. Even in practice, people have come to the conclusion that they must have a convention so that they can share ideas and thoughts: that is how protocols were invented. So the main idea here is to write a set of C# classes which will define the communication core between the layers. Those classes will contain no logic inside them, they will be more like OO placeholders for the data. Taking this approach, it becomes obvious that the only way the presentation layer invokes the business layer with some parameters is to construct common objects, fill them with data and send them to the business layer. The business layer, based on what kind of object received, decides to change it, return it, or invoke the data layer. The communication between the data layer and the business layer is also within the common objects domain - that is, the business tier must construct common objects that both the business tier and the data tier understand and send them to it. Once the data layer gets the common objects as parameters, it talks to the persistant medium and returns - a set of common objects to the business layer. The DAL should be well written to handle this transformation from raw data to sets of common objects. The BAL also will return as a result a set of common objects. With this approach in mind, it becomes very easy to develop applications because the code will contain less errors, it maintains its extensibility, new developers could jump in to the project with less effort etc.
Implementing DAL and BAL with CsharpGears Framework
Have you been following me up to now ? So far, so good. Now lets see how will everything that we said work in practice:


   

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using DevGears.DataBase;
using System.Data;
using DataLayer.Properties;
using System.Configuration;

namespace DataLayer
{
    public abstract class DataLayerBase
    {
        private IDataBase dbEntity;
        private string connectionString;

        protected IDataBase DbEntity
        {
            get { return dbEntity; }
            set { dbEntity = value; }
        }

        protected string ConnectionString
        {
            get { return connectionString; }
            set { connectionString = value; }
        }

        public DataLayerBase() 
        {
            connectionString = Settings.Default.connectionString;        
        }

        protected IDataBase CreateDbEntity(string StoredProcedure) 
        {
            return new DataBaseEntity(StoredProcedure, connectionString);
        }

        protected abstract void AddParameters();
        public abstract void Execute();

        protected void AddCommandParameter(string Name, object Value, DbType Type)
        {
            dbEntity.AddCommandParameter(Name, Value, Type);
        }
        protected void IntAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.Int32);
        }
        protected void StringAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.String);
        }
        protected void BoolAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.Boolean);
        }
        protected void DecimalAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.Decimal);
        }
        protected void DoubleAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.Double);
        }
        protected void DateTimeAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.DateTime);
        }
        protected void DateAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.Date);
        }
        protected void ByteAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.Byte);
        }
        protected void LongAddCommandParameter(string Name, object Value)
        {
            AddCommandParameter(Name, Value, DbType.Int64);
        }

    }
}

In order to maintain the flexibility of changing the names of the stored procedures I will invoke, I decide to keep them as enum:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace DataLayer
{
    public enum StoredProcedure
    {
        CountrySelectAll, 
        LeagueSelectByCountry,
        TeamAllSelect,
        MatchInsert
 //...
    }
}

The Data layer will be constituted of an abstract base class named DataLayerBase and in it there will be a private element that implements the IDatabase interface from CsharpGears.Database. All database entity objects (discussed earlier) implement that interface. There are also wrapper methods over the methods for adding parameters in the database entity objects. The method Execute() is abstract, meaning all classes that will inherit from this class will also have to provide implementation of it. The method AddParameters() is used to hold all the parametrization of the call to the database. Lets now see how the inherited class would look like:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonObjects;
using DevGears.DataBase;

namespace DataLayer
{
    public class TeamAllSelect : DataLayerBase
    {
        private List<Team> resultSet;

        public List<Team> ResultSet
        {
            get { return resultSet; }
            set { resultSet = value; }
        }

        protected override void AddParameters()
        {
            throw new NotImplementedException();
        }

        public override void Execute()
        {
            DbEntity = new GenericDbEntity<Team>(StoredProcedure.TeamAllSelect.ToString(),
                                                    ConnectionString);
            resultSet = (List<Team>)DbEntity.Select();
        }
    }
}
Since we don't need to add parameters to this stored procedure, we decide not to implement the AddParameters() method and leave it like it is. The CommonObject in our case, is the Team class:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CommonObjects
{
    public class Team
    {
        #region Private Members
        private int teamId;
        private string name;
        private string presentationName;
        private string logoUrl;
        private string city;
        private string coordinates;
        private int leagueId;
        private int stadiumId;
        #endregion Private Members

        #region Properties
        public int StadiumId
        {
            get { return stadiumId; }
            set { stadiumId = value; }
        }

        public int LeagueId
        {
            get { return leagueId; }
            set { leagueId = value; }
        }

        public string Coordinates
        {
            get { return coordinates; }
            set { coordinates = value; }
        }

        public string City
        {
            get { return city; }
            set { city = value; }
        }

        public string LogoUrl
        {
            get { return logoUrl; }
            set { logoUrl = value; }
        }

        public string PresentationName
        {
            get { return presentationName; }
            set { presentationName = value; }
        }

        public string Name
        {
            get { return name; }
            set { name = value; }
        }

        public int TeamId
        {
          get { return teamId; }
          set { teamId = value; }
        }
        #endregion Properties
    }
}
Now, going upper to the business tier, we have the following BAL object:


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BusinessLayer
{
    public abstract class BusinessLayerBase
    {
        public abstract void Invoke();
    }
}

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using CommonObjects;
using DataLayer;

namespace BusinessLayer
{
    public class TeamAllSelectProcess : BusinessLayerBase
    {
        private List<Team> resultSet;

        public List<Team> ResultSet
        {
            get { return resultSet; }
            set { resultSet = value; }
        }

        public override void Invoke()
        {
            TeamAllSelect teamAllSelect = new TeamAllSelect();
            teamAllSelect.Execute();
            resultSet = teamAllSelect.ResultSet;
        }
    }
}

Now, the cherry in the cream. In the presentation layer, we have an user control containing dropdown list that we want populated:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using CommonObjects;
using BusinessLayer;
using System.Drawing;

namespace FiksApp.UserControls
{
    public partial class HeadToHead : System.Web.UI.UserControl
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!Page.IsPostBack)
            {
                TeamAllSelectProcess teamAllSelectProcess = new TeamAllSelectProcess();
                teamAllSelectProcess.Invoke();
                ddlTeam1.DataSource = teamAllSelectProcess.ResultSet;
                ddlTeam1.DataBind();
            }
        }
    }
}

That is all practically. I have guided you in a thorough process of designing the DAL and BAL of web application using the benefits of the CsharpGearsFramework. It would be nice to hear your comments, as they encourage me to further extend the framework and go on writing more posts.

5 comments:

Anonymous said...

Hi,
seems to be a nice framework. But is there any documentation? Could fins any...I might be semi-blind though. 8-)

And a question, it seems to be very SP centric? Is it possible to do CRUD without SPs? Is it possible to use Linq interchangeably with the data access (for when you just want a simple select or something).

Thanks!
/R

SeeSharpWriter said...

Yes there is documentation - I should put a link to it, I was making some optimizations in the methods and threw out some needless stuff.

Yes it is possible to do CRUD without SP, although SPs are preferred because of parameter and property mapping caching . Ad hoc queries will not have caching included, but it works.

You would need to pass the whole SQL as string instead of the name of the procedure

SeeSharpWriter said...

Here is an example of ad-hoc SQL query:

Using the DataBaseEntity class for ad-hoc SQL queries

DataBaseEntity dbEntity = new DataBaseEntity("SELECT * FROM Product", CommandType.Text, ConnectionString);
DataTable dt = (DataTable)dbEntity.Select();

Here is the rest of the post: C# Gears Framewok Database Access

Anonymous said...

Hi,

Nice framework, but is there any link from where I can download the complete working solution?

Thanks!

pvahora said...

The article is good to read and worth sharing
Prestashop Designer

Post a Comment