Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / web / ASP.NET

Four ways of passing data between layers

4.81/5 (106 votes)
8 May 2013CPOL10 min read 309.5K   2.7K  
In this article we will see four different ways of passing data between layers.

Table of contents

What’s the goal?

Layered architecture is the most common way of creating software projects because they bring in SOC (Separation of Concern), decoupling, easy understanding of code, etc. In software industry people are pretty clear about the common layers and their responsibility (UI for look and feel, middle layer for business logic, and data access layer for data).

But the biggest confusion or I will say where developers have not come to common standards is the way of passing data between these layers. This article will try to flesh out the various ways of passing data between layers.

Image 1

Layers and Tiers are different things

I repeat again layers and not tiers. In case you are confused about layers and tiers, the below diagram explains it visually. Layers are more of a logical separation for your code while tiers mean that those layers are deployed in different physical machines. So for now we will keep the scope of this article limited and restricted, or else we need to get into web service, remoting, etc., which will lead to more confusion.

Image 2

Data incoming and data outgoing

We will be analyzing the passing of data between layers from two perspectives: how the data will come towards the data access layer, and how the data will pass to UI. As you read the article you would understand better why I want two perspectives.

Image 3

Method 1: Non-uniform way of passing

This is the first way (probably the most common way) of passing data from one layer to another, the non-uniform way. So let’s start from UI to DAL.

Data from the UI to middle tier is passed using getter and setter, below is a sample code for the same.

Customer objCust = new Customer();
objCust.CustomerCode = "c001";
objCust.CustomerName = "Shivprasad"; 

Further down from the middle tier to the data access layer, data is passed by using comma separation, arrays or some other non-uniform way which makes a developer personally comfortable. In the below code snippet look at the Add method and how it’s making a call to the data access layer with input parameters.

C#
public class Customer
{
    private string _CustomerName = "";
    private string _CustomerCode = "";
    public string CustomerCode
    {
        get { return _CustomerCode; }
        set { _CustomerCode = value; }
    }
    public string CustomerName
    {
        get { return _CustomerName; }
        set { _CustomerName = value; }
    }
    public void Add()
    {
        CustomerDal obj = new CustomerDal();
        obj.Add(_CustomerName,_CustomerCode);
    }
} 

Below is the data access layer code with two inputs: CustomerName and CustomerCode.

C#
public class CustomerDal
{
    public bool Add(string CustomerName,string CustomerCode)
    {
        // Insert data in to DB
    }
} 

So summarizing for non-uniform way data is passed in the following ways:

  • UI to middle tier (setter and getter).
  • Middle tier to data access by using comma, inputs, arrays etc.

Image 4

Now let’s analyze how data will be passed from DAL to UI in the non-uniform method. So let’s start from the UI first.

From UI, data will be pulled in using the middle tier object as shown below.

C#
Customer obj = new Customer();
List<Customer> oCustomers = obj.getCustomers(); 

From middle tier the data will be pulled from the data access layer using dataset/datatable/XML etc. The most important point to note is the middle tier loops through the dataset and converts it into a strong type object, in this case it’s Customer. You can see the getCustomers function (code is shown below), how it loops through the dataset and fills in a strongly typed list of customers. This is necessary because the UI is referencing strongly typed classes (watch the UI code snippet just above).

C#
public class Customer
{
    private string _CustomerName = "";
    private string _CustomerCode = "";
    public string CustomerCode
    {
        get { return _CustomerCode; }
        set { _CustomerCode = value; }
    }
    public string CustomerName
    {
        get { return _CustomerName; }
        set { _CustomerName = value; }
    }
    public List<Customer> getCustomers()
    {
        CustomerDal obj = new CustomerDal();
        DataSet ds = obj.getCustomers();
        List<Customer> oCustomers = new List<Customer>();
        foreach (DataRow orow in ds.Tables[0].Rows)
        {
            // Fill the list
        }
        return oCustomers;
    }

This conversion also ensures that we maintain the golden rule of tiered layer: “UI should avoid directly referencing data access components like ADO.NET, OLEDB etc. With this if we change our data access methodology later, it’s not replicated on the UI.” You can watch the project of my console UI, it has no references to System.Data, very clean, isn’t it?

Image 5

The final thing is the CustomerDal class which just returns the dataset using ADO.NET to pass the same to the middle layer.

C#
public class CustomerDal
{
    public DataSet getCustomers()
    {
        // fetch customer records
        return new DataSet();
    }
} 

Summarizing in a non-uniform way the data is moved to UI in the following ways:

  • From DAL to middle tier it uses dataset/XML/datareader (personal choices of developers).
  • From middle tier it uses strongly typed classes. 

Image 6

Problems and benefits with Non-uniform way

Some of the benefits of non-uniform way:

  • It’s simple and easy to implement. In case your data storage and access methods will not change I think this should suffice.

But it has some serious disadvantages.

  • Because we do not have a uniform structure, we need to always convert from one format to another as we pass through each layer, which can lead to performance issues for a large number of rows.
  • If developers start using their custom way of passing data (like XML), it will lead to increase in complexity.
  • If tomorrow you need to change your data access methodology from ADO.NET to OLEDB, you need to change across tiers.

That’s where we can use the uniform way of passing data, i.e., “Entities”.

Circular dependency problem

In case you are wondering why we cannot use the middle tier classes across layers, do not forget, the middle tier is already using DAL. If DAL tries to use the middle tier it will lead to circular dependency.

Image 7

Method 2: Uniform way of passing data (Entity classes)

The other way of passing data is using the uniform approach using entity classes. Entity classes are nothing but simple classes with SET and GET. It does not have logic. For instance you can see CustomerEntity class with customer name and customer code. Now this class is in a separate project called as Entity so that it can be referenced by all layers.

Image 8

C#
public class CustomerEntity
{
    protected string _CustomerName = "";
    protected string _CustomerCode = "";
    public string CustomerCode
    {
        get { return _CustomerCode; }
        set { _CustomerCode = value; }
    }
    public string CustomerName
    {
        get { return _CustomerName; }
        set { _CustomerName = value; }
    }
} 

So let’s start with CustomerDal first. The DAL returns back a CustomerEntity collection and also takes in a CustomerEntity object to add to the database. Please note the data access layer is responsible to convert the output to the CustomerEntity type object.

C#
public class CustomerDal
{
    public List<CustomerEntity> getCustomers()
    {
        // fetch customer records
        return new List<CustomerEntity>();
    }
    public bool Add(CustomerEntity obj)
    {
        // Insert in to DB
        return true;
    }
} 

The middle tier inherits from the CustomerEntity class and adds behavior to the entity class. This class will have all the business rules for the entity. Also note that data is passed to the data access layer in an Entity class format (see the Add method) and also the data is returned back in entity format (see the getCustomers method).

C#
public class Customer : CustomerEntity
{
   
    public List<CustomerEntity> getCustomers()
    {
        CustomerDal obj = new CustomerDal();
        
        return obj.getCustomers();
    }
    public void Add()
    {
        CustomerDal obj = new CustomerDal();
        obj.Add(this);
    }
} 

Note: Watch the type cast made in “Add” method. I personally love the elegancy and simplicity the way data is passed to the DAL.

Finally the UI is also no different even he talks with the same protocol of entity class structure.

Image 9

Summarizing, in the uniform method we pass data from one layer to other layer using “Entity” class structure.  So either the data is moving in or out it uses entity structure.

Image 10

Problems and benefits with uniform way

First let’s talk about good things.

  • Due to uniform structure, irrespective of which layer you are on, it’s strongly typed and you get the benefit of Visual Studio intellisense. For instance you can see in the below figure, even though I am in the data access layer, I still know which object and properties I am dealing with. So strong types support across layers.

    Image 11

  • Due to common structure you need to convert the data only once, i.e., in DAL. So once the DAL converts the data into Entity format structure it can be used without conversion in the below layers, i.e., UI and middle tier.

The only disadvantage which I see is the double loop problem. When we talk about entity classes they normally represent real world object structure.

Now the database is technically optimized, so the structure of the database world is different from that of the real world. For instance it’s very much possible that you have customer name and address are stored in the same table (denormalized tables) so that you can fetch faster. But as soon as these objects come in the real world it's possible we would like to have one to many relationships. Do have a look at the below image.

So in a simple world we would need to write some kind of logic for this transformation.

Image 12

Below is a simple code extract from a middle tier where you can see there is a double loop, one loop which fills the customer collection and the other which adds the address object as an aggregated object to the customer object.

C#
foreach (DataRow o1 in oCustomers.Tables[0].Rows)
{
    obj.Add(new CustomerEntyityAddress()); // Fills customer
    foreach (DataRow o in oAddress.Tables[0].Rows)
    {
        obj[0].Add(new AddressEntity()); // Fills address
    }
} 

Method 3: Data transfer objects (DTO)

So to overcome the same we can create classes which are more of technical classes only to pass data efficiently across layers.

And because these are technical classes to just pass data around, we do not need to worry if they represent real world objects or not. In other words, we can flatten the class structure to avoid the double loop problem which we talked about previously.

You can see the below code snippet which shows how the CustomerDTO class inherits from CustomerEntity and aggregates the Address class to create a de-normalized class which is optimized for quick loading and viewing.

C#
public class CustomerDTO : CustomerEntity 
{
    public AddressEntity _Address = new AddressEntity();
}

You can see in the below code snippet how just one loop fills the de-normalized class.

C#
foreach (DataRow o1 in oCustomers.Tables[0].Rows)
{
	CustomerDTO o = new CustomerDTO();
	o.CustomerCode = o1[0].ToString();
	o.CustomerName = o1[1].ToString();
	o._Address.Address1 =  o1[2].ToString();
	o._Address.Address2 = o1[3].ToString();
	obj.Add(o);
} 

And as usual, the UI can always call the DTO to get data into the UI.

C#
// From Data out
Customer obj = new Customer();
List<CustomerDTO> oCustomers = obj.getCustomers(); 

Problems and benefits with DTO

The biggest advantage is quick and fast loading due to denormalized structure.

The biggest disadvantage is as these are technical classes which are optimized from a performance perspective, they can violate all rules of OOP and real world representation. So it is best to have a hybrid approach as discussed in the next section.

Method 4: Hybrid approach (Entity + DTO)

On one side Entity classes represent real world objects which follow OOP principles and on the other side DTO represents flattened classes to gain performance. So the best approach would be to take goodies from both worlds.

When data manipulation happens in the system (it can be insert, update, or delete form) we would like to ensure that we have data integrity and objects exposed on the user interface should mimic the real world. So for those scenarios a full blown normalized class structure (Entity) is the way to go.

But if you are looking to pull huge data to be displayed, like some lengthy report, then a denormalized structure /  DTO is more suitable. I still remember in of our old projects these classes where termed as “Reporting classes”. The class structure for these classes never depicted real world objects.

Image 13

When to use what?

  • Non-uniform approach: When you think about it, your data access methods will not change too much. In other words if you think your project tomorrow will not have different databases (like Oracle, SQL Server, etc.), this method is great.
  • Uniform approach (Entity, DTO, or hybrid): If you think your project will deal with different flavors of databases, this approach becomes mandatory. In other words your DAL does the conversion once but inside the project, you always refer to a common structure without worrying about the database structure.

Ready made solutions: Entity Framework and LINQ

In good old days we used to write these classes manually. I loved how carefully and tenderly I used to create those classes. World has moved ahead and we have awesome frameworks like EF (Entity Framework) and LINQ which can do magic in minutes. This article will not go beyond its scope to teach Entity Framework or LINQ.

But yes, if you understand the flavors of these classes, you can easily create better class structures by using EF or LINQ.

Source code

You can download the source code from here.

For further reading do watch the below interview preparation videos and step by step video series.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)