Blue Theme Orange Theme Green Theme Red Theme
 
MindFusion's Components
Home | Forums | Videos | Photos | Downloads | Blogs | Interviews | Jobs | Beginners | Training
 | Consulting  
Submit an Article Submit a Blog 
 Login Close
User Id:
Password:
 
Forgot Password
Forgot Username
Why Register
 Jump to
Skip Navigation Links
TechnologyExpand Technology
WebsiteExpand Website
 Resources  
Close
 Our Network  
Close
Search :       Advanced Search »
Home » C# Language » An Elegant C# Data Access Layer using the Template Pattern and Generics

An Elegant C# Data Access Layer using the Template Pattern and Generics

The GOF Template pattern coupled with .NET 2.0 Framework generics provides an awesome synergistic alliance. This article demonstrates how to drastically reduce the amount of code required in building a data access layer. Less code to debug... less code to break... less code to maintain... what could be better?

Author Rank:
Total page views :  115042
Total downloads :  3263
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
TemplateArticle.zip
 
Become a Sponsor

The GOF Template pattern coupled with .NET 2.0 Framework generics provides an awesome synergistic alliance.  This article demonstrates how to drastically reduce the amount of code required  in building a data access layer.  Less code to debug... less code to break... less code to maintain... what could be better?

Part I: Introduction to the Template Pattern

To get started, let's talk about the GOF Template pattern.  As with all GOF patterns, its primary purpose is to separate out what changes in your code from what does not change. The Template pattern deals with repetitive coding within a class.  If you find yourself coding the same thing over-and-over (and over), you can get rid of the repetition of code using the Template pattern.

Here is an example.  A perfect opportunity is if you have a ton of classes where you are doing similar logic as in GetTheFancyNumber() below

public class DoSomething

{

    private int m_number;

    public int GetTheFancyNumber()

    {

        int theNumber = m_number;

        // DO LOTS OF CRAZY STUFF TO theNumber HERE.

        return theNumber;

    }

}

public class DoSomethingElse

{

    private int m_number;

    public int GetTheFancyNumber()

    {

        int theNumber = m_number;

        // DO LOTS OF CRAZY STUFF TO theNumber HERE.

        return theNumber;

    }

}

If the logic is similar in the classes and you can identify what changes, you can encapsulate it.  You take the code shared by the classes and put it in a base class and make the parent class responsible for what changes.  In this example we would pub the "GetTheFancyNumber()" method in a base class and encapsulate setting the "theNumber" variable (the first line of the method), forcing the parent classes to take care of it.

This would be our Template:

public abstract class DoSometingBase

{

    protected abstract int GetTheNumber();

    public int GetTheFancyNumber()

    {

        int theNumber = GetTheNumber();

        // LOTS LOTS OF CRAZY STUFF TO theNumber HERE.

        return theNumber;

    }

}

Then when we create the parent classes, we use the logic encapsulated by the template and implement the things that change (getting the number):

public class DoSomethingElse : DoSometingBase

{

    private int m_number;

    protected override int GetTheNumber()

    {

        return m_number;

    }

}

 

public class DoSomething : DoSometingBase

{

    private int m_number;

    protected override int GetTheNumber()

    {

        return m_number;

    }

}


So, that's the Template pattern in a nut-shell.  Where it really shines is when we couple it with generics.

Part II. Template Pattern + Generics = Mapping Synergy

Where is the one place where we all do the same thing over-and-over (and over) again?  You guess it! – accessing a database and getting objects built from the data.  This is where we'll implement the Template pattern to create an elegant DAL (Data Access Layer).

First, let's build a simple table to use in this example that will hold some data for a person.

CREATE TABLE [tblPerson] (
 [PersonID] [int] IDENTITY (1, 1) NOT NULL ,
 [FirstName] [nvarchar] (50),
 [LastName] [nvarchar] (50),
 [Email] [nvarchar] (50) ,
 CONSTRAINT [PK_tblPerson] PRIMARY KEY  CLUSTERED
 (
  [PersonID]
 )  ON [PRIMARY]
) ON [PRIMARY]

So now we have this exciting table to hold our data. :

 

Now, we will build a class to hold the data (the code is in the project files – I won't bore you with it here).

 

There are two things we do repeatedly when accessing data.  First we have to access the database, issue a command, and get the results.  Second, we have to map those results to our objects.  Both of these steps are candidates for templatizing (templatizing(?)... that's a real word, right?).

Let's look at the mapping part first because it's the easier one of the two.  (The mapper pattern is an awesome enterprise pattern introduced in a book by Fowler.)

In this example we'll be coding to the IDataReader and IDataRecord interfaces in order to map our data to objects (IDataReader basically inherits from and iterates through IDataRecords).

I would always recommend coding to existing framework interfaces wherever possible so your code is more flexible (check out my article on interface based development here:

http://www.c-sharpcorner.com/UploadFile/rmcochran/csharp_interrfaces03052006095933AM/csharp_interrfaces.aspx?ArticleID=cd6a6952-530a-4250-a6d7-54717ef3b345 ). 

By coding to these interfaces, we are only tightly coupled to the database where we actually create the connection.  This makes our code easily portable to any database so when someone says "Hey, this is great! Let's move it to MySQL!" you don't have to pull your hair out.

So anyways... back to the subject. Here is our mapper base object.  The parent class will take care of the mapping specifics.  The base class will take care of taking each mapped object and putting it in a collection.  Because generics allow us to specify logic for any type the template can be used for all of our objects pulled from the DAL, not just the Person.

abstract class MapperBase<T>

{

    protected abstract T Map(IDataRecord record);

 

    public Collection<T> MapAll(IDataReader reader)

    {

        Collection<T> collection = new Collection<T>();

 

        while (reader.Read())

        {

            try

            {

                collection.Add(Map(reader));

            }

            catch

            {

                throw;

 

                // NOTE:

                // consider handling exeption here instead of re-throwing

                // if graceful recovery can be accomplished

            }

        }

 

        return collection;

    }

}

When we inherit the MapperBase to actually map to a Person object we only have to implement the specifics of creating an object and mapping the data from the IDataRecord to the object's properties. 

class PersonMapper: MapperBase<Person>

{

    protected override Person Map(IDataRecord record)

    {

        try

        {

            Person p = new Person();

 

            p.Id = (DBNull.Value == record["PersonID"]) ?

                0 : (int)record["PersonID"];

 

            p.FirstName = (DBNull.Value == record["FirstName"]) ?

                string.Empty : (string)record["FirstName"];

 

            p.LastName = (DBNull.Value == record["LastName"]) ?

                string.Empty : (string)record["LastName"];

 

            p.Email = (DBNull.Value == record["Email"]) ?

                string.Empty : (string)record["Email"];

 

            return p;

        }

        catch

        {

            throw;

 

            // NOTE:

            // consider handling exeption here instead of re-throwing

            // if graceful recovery can be accomplished

        }

    }

}

Can you see how easy it would be to create a new mapper for any class we have defined that pulls data from a table?  If you see the possibilities, I imagine you are getting a bit excited right about now.  Hold on though, we aren't even to the good part yet.

Part III. Template Pattern + Generics = DataAccess Synergy

The other thing we have to do is actually hit the database with a request and get the IDataReader back. 

Here is what changes for each table we are hitting and each object we are creating:

  1. Getting the connection.
  2. Getting the Sql Command
  3. Getting the Sql Command Type
  4. Getting the mapper (from part II)

IDbConnection GetConnection();

string CommandText { get; }

CommandType CommandType { get; }

Collection<IDataParameter> GetParameters(IDbCommand command);

MapperBase<T> GetMapper();

Here is what stays the same which we'll encapsulate in an Execute() method that will return a collection of our objects.

public Collection<T> Execute()

{

    Collection<T> collection = new Collection<T>();

 

    using (IDbConnection connection = GetConnection())

    {

        IDbCommand command = connection.CreateCommand();

        command.Connection = connection;

        command.CommandText = this.CommandText;

        command.CommandType = this.CommandType;

 

        foreach(IDataParameter param in this.GetParameters(command))

            command.Parameters.Add(param);

 

        try

        {

            connection.Open();

 

            using (IDataReader reader = command.ExecuteReader())

            {

                try

                {

                    MapperBase<T> mapper = GetMapper();

                    collection = mapper.MapAll(reader);

                    return collection;

                }

                catch

                {

                    throw;

 

                    // NOTE:

                    // consider handling exeption here

                    // instead of re-throwing

                    // if graceful recovery can be accomplished

                }

                finally

                {

                    reader.Close();

                }

            }

        }

        catch

        {

            throw;

 

            // NOTE:

            // consider handling exeption here instead of re-throwing

            // if graceful recovery can be accomplished

        }

        finally

        {

            connection.Close();

        }

    }

}

So, here is the class we end up with:


 

One of the things that will be the same for all of the objects inheriting from ObjectReaderBase is getting the connection: "GetConnection()". We'll put this in an abstract object implementing the ObjectReaderBase<T>.

abstract class ObjectReaderWithConnection<T> : ObjectReaderBase<T>

{

    private static string m_connectionString =
         @"Data Source=DATA_SOURCE_NAME;Initial Catalog=Test;Integrated Security=True";

   

        protected override System.Data.IDbConnection GetConnection()

    {

        // update to get your connection here

 

        IDbConnection connection = new SqlConnection(m_connectionString);

        return connection;

    }

}

So we have:

 

Here is the implementation of a PersonReader which handles things specific to reading a person from the database and building a collection of Person objects.

class PersonReader: ObjectReaderWithConnection<Person>

{

    protected override string CommandText

    {

        get { return "SELECT PersonID, FirstName, LastName, Email FROM tblPerson"; }

    }

 

    protected override CommandType CommandType

    {

        get { return System.Data.CommandType.Text; }

    }

 

    protected override Collection<IDataParameter> GetParameters(IDbCommand command)

    {

        Collection<IDataParameter> collection = new Collection<IDataParameter>();

        return collection;

 

        //// USE THIS IF YOU ACTUALLY HAVE PARAMETERS

        //IDataParameter param1 = command.CreateParameter();

        //param1.ParameterName = "paramName 1"; // put parameter name here

        //param1.Value = 5; // put value here;

 

        //collection.Add(param1);

 

        //return collection;  

    }

 

    protected override MapperBase<Person> GetMapper()

    {

        MapperBase<Person> mapper = new PersonMapper();

        return mapper;

    }

}

So now we have:

Once you have the general concept, you will probably have many ideas on how you can tweak this base reader object for improvements or to fit your specific projects needs. 

Also, you will have to create a different base object for each "type" of data access you'll be performing.  For example you may need other abstract base classes for when you will be executing IDbCommand.ExecuteNonQuery() or IDbCommand.ExecuteScalar() requests against the database.

Part IV. Mapping Synergy + DataAccess Synergy = Elegant Code

The important thing to realize is that we have completely separated out normally repetitive code which will make for much easier maintenance because of a smaller code base.  For each new table-class relationship you have in your project you only have to specify the things that are different.  So, while this approach may look like more classes and complexity upfront, once you understand and implement this approach you'll actually save a lot of time as the number of  "data holding" classes in your project grows and you'll have fewer places to look when debugging because of all the shared-code-synergy.

This is how we'll use our framework to retrieve everyone from the database and print them to the console:

static void Main(string[] args)

{

    PersonReader reader = new PersonReader();

    Collection<Person> people = reader.Execute();

 

    foreach (Person p in people)

        Console.WriteLine(string.Format("{0}, {1}: {2}",
            p.LastName, p.FirstName, p.Email));

 

    Console.ReadLine();

 

}

Part V. Conclusion.

So now you have the general idea you can use it as a base to build a DAL. There are many places to improve upon this approach such as using an identity map (yet another awesome Fowler pattern) to avoid unnecessary database hits or a better way to get the IDbConnection, such as using the strategy pattern and maybe using a factory,  or how about a facade pattern to expose all of this functionality through one object....  anyways the possibilities are (almost) endless depending on your project.

You will most likely have to make changes to have this approach work for any specific project, but as you can see, it is possible to have less code to debug... less code to break... less code to maintain... what could be better?  We can synergize our code by combining the Template Pattern with generics.

Until next time,

Happy coding


Login to add your contents and source code to this article
 About the author
 
Matthew Cochran
Looking for C# Consulting?
C# Consulting is founded in 2002 by the founders of C# Corner. Unlike a traditional consulting company, our consultants are well-known experts in .NET and many of them are MVPs, authors, and trainers. We specialize in Microsoft .NET development and utilize Agile Development and Extreme Programming practices to provide fast pace quick turnaround results. Our software development model is a mix of Agile Development, traditional SDLC, and Waterfall models.
Click here to learn more about C# Consulting.
 
Introducing MaxV - one click. infinite control. Hyper-V Hosting from MaximumASP.
Finally – a virtual platform that delivers next-generation Windows Server 2008 Hyper-V virtualization technology from a managed hosting partner you can truly depend on. Visit www.maximumasp.com/max for a FREE 30 day trial. Hurry offer ends soon. Climb aboard the MaxV platform and take advantage of High Availability, Intelligent Monitoring, Recurrent Backups, and Scalability – with no hassle or hidden fees. As a managed hosting partner focused solely on Microsoft technologies since 2000, MaximumASP is uniquely qualified to provide the superior support that our business is built on. Unparalleled expertise with Microsoft technologies lead to working directly with Microsoft as first to offer IIS 7 and SQL 2008 betas in a hosted environment; partnering in the Go Live Program for Hyper-V; and product co-launches built on WS 2008 with Hyper-V technology.
Dynamic PDF
ceTE software specializes in components for dynamic PDF generation and manipulation. The DynamicPDF™ product line allows you to dynamically generate PDF documents, merge PDF documents and new content to existing PDF documents from within your applications.
Go.NET
Build custom interactive diagrams, network, workflow editors, flowcharts, or software design tools. Includes many predefined kinds of nodes, links, and basic shapes. Supports layers, scrolling, zooming, selection, drag-and-drop, clipboard, in-place editing, tooltips, grids, printing, overview window, palette. 100% implemented in C# as a managed .NET Control. Document/View/Tool architecture with many properties&events. Optional automatic layout.
Dundas Software
Dundas Chart for .NET is the most advanced .NET charting package available today.  With an extremely complete feature set, elegant architecture and easy implementation, Dundas Chart can quickly add advanced Charting functionality to enhance and transform ASP.NET and Windows Forms applications.  Whether you are implementing charting into internal projects, or building applications for clients, Dundas Chart offers advanced technology and advanced results to get the most out of data.
Clickatell's SMS Gateway
Clickatell's Developer Solutions allow you to SMS enable any website or application via a range of API's. Learn More about our API connections.
Free access to .NET Memory Management video
Everything you need to know about Garbage Collection, Temporary Objects, Fragmentation, Finalization and common causes of memory leaks in .NET. Watch the video here.
Microsoft Visual Studio 2010 Professional
Microsoft Visual Studio 2010 Professional will launch on April 12, but you can beat the rush and secure your copy today by pre-ordering at the affordable estimated retail price of $549 (US). Pre-order now.
Nevron Chart for .NET 2010.1 Now Available
The leading .NET charting control now features PDF, Flash and Silverlight export, visualization of large datasets and more. Deliver true charting functionality to your BI, Scorecard, Presentation or Scientific apps. Download evaluation now.
Developer-Ready ASP.NET 2.0 Web Hosting with 3 MONTHS FREE
Now supporting .NET 3.0 Framework with Windows Workflow Foundation, Windows Communication Foundation (WCF), Windows Presentation Foundation (WPF), windows CardSpace (WCS)! Providing more flexibility for Developers with Web Services Support and a User/Permission Manger. Also supporting MS SQL 2005/2000 with Real-Time Backups, FREE Automated Attach .MDF Tool, FREE SQL Restore and Shrink SQL DB Tools, and SQL
 
   Print Read/Post comments Post a comment  Similar Articles  
   Email to a friend  Bookmark  Author's other articles  
Download Files:
TemplateArticle.zip
 
 Post a Feedback, Comment, or Question about this article
Subject:  
Comment:  
Become a Sponsor
 Comments
MySQL by psycho_tx On May 29, 2006

Hi there,

First I have to say, great article !!

could you give me an example how to let this example communicate with MySQL

And what would you have to do if you want to use stored procedures.

thanks already

Greets

Reply | Email | Delete | Modify | 
Good article... thoughts about updating and inserting? by Christoffer On July 26, 2006

I have implemented your approach for reading data, and it works like a charm.

But I have som thougts about updating and inserting (where you don't need generics)

Does this look like a reasonable approach?:

---------- CODE -----------

class CustomerUpdater : DataUpdaterBase

{

private Customer _customer;

private string _commandText;

public CustomerUpdater(Customer customer)

{

_customer = customer;

}

protected override string CommandText

{

set

{

_commandText = value;

}

get

{

return _commandText;

}

}

protected override System.Data.CommandType CommandType

{

get

{

return System.Data.CommandType.StoredProcedure;

}

}

protected override Collection<IDataParameter> GetParameters(IDbCommand command)

{

Collection<IDataParameter> collection = new Collection<IDataParameter>();

IDataParameter custGuid = command.CreateParameter();

custGuid.ParameterName = "GUID";

custGuid.DbType = DbType.Guid;

custGuid.Direction = ParameterDirection.Input;

custGuid.Value = _customer.GUID;

collection.Add(custGuid);

return collection;

}

}

Reply | Email | Delete | Modify | 
Re: Good article... thoughts about updating and inserting? by Matthew On August 1, 2006

Yeah,  it seems like a reasonable approach to me. 

Sometimes I like using an IPersisted interface approach to keep things more abstract/flexible.

this way you can pass any of your objects into a utility method (implementing the the IPersister interface below) and it will know whether to insert, update, delete, or ignore the object. 

Here is a rough example (very rough -- hopefully it makes sense)...

IPersisted
{

  bool IsSaved { get; set; }
  bool IsDeleted { get; set; }
  bool IsDirty { get; set; }
  bool IsNew { get; set; }

}

IPersister
{
  bool Persist(IPersisted item);
}

public class Book: IPersisted
{

  // define book here
}

public class Person: IPersisted
{
   // define person here
}


public static class  Persister: IPersister
{

  public bool Persist(IPersisted item)
  {
    if(item is Person)
    {

        // call save method here and update/persist person data

   }
    else if (item is Book)
    {

        // call save method here and update/persist book object data
    }

 }
}

Reply | Email | Delete | Modify | 
Re: Re: Good article... thoughts about updating and inserting? by Christoffer On August 3, 2006

Thanks for the reply

I kind a see what your getting at, but a static class cannot implement an interface or inherit from an abstract class as far as I know.

What are your thoughts about this article:

http://www.codeproject.com/useritems/BusinessObjectHelper.asp

 

Kind regards

 

Christoffer

Reply | Email | Delete | Modify | 
Re: Re: Re: Good article... thoughts about updating and inserting? by Matthew On August 3, 2006

Nice article.  I could see that approach being perfect for some situations, maybe overkill for others. 

You may want to look into combining either approach with code generation and see what you can come up with.  Check out CodeSmith, it is a nice tool: http://codesmithtools.com/.  Once you have your basic code base down, code generation can really expedite the creation of the data access layer and it makes it easy to quickly adapt your core classes to minor data structure changes.

and.. you are 100% correct.  We can't implement interfaces with static classes (my fault for madly typing away without thinking).  You can achive this result with a singleton (or just abandon all interest in playing nice with the memory and have a ton of instances lying about).

 

Reply | Email | Delete | Modify | 
Re: Re: Re: Re: Good article... thoughts about updating and inserting? by Christoffer On August 3, 2006

Yes a singleton would of course solve it :).

Anyways, great article, you left me greatly inspired. Keep up the good flow of articles

Kind regards

Christoffer

Reply | Email | Delete | Modify | 
BindingList? by Stephen On August 7, 2006
Wouldn't it make sense to use a BindingList collection so that you can throw the events for when items are added/deleted/changed?
Reply | Email | Delete | Modify | 
Re: BindingList? by Matthew On August 7, 2006
Yeah, especially if you need to monitor the list.  I guess it would depend on how you are monitoring the state of the objects.  If they are self-monitoring, you may not want the extra overhead, if they are being monitored by another state-management object, the BindingList would certainly make life easier. 
Reply | Email | Delete | Modify | 
MapperBase by csharpcorner On November 13, 2006

If the names of your data class properties are the same as the data table column names, you can use the following implementation of the Map method in the MapperBase class to avoid having to write manual mapping code for each data class.

using System.Reflection;
public abstract class MapperBase<T> where T : new()

protected T Map(IDataRecord record)
{
 T instance = new T();

 string fieldName;
 PropertyInfo[] properties = typeof(T).GetProperties();

 for (int i = 0; i < record.FieldCount; i++)
 {
  fieldName = record.GetName(i);

  foreach (PropertyInfo property in properties)
  {
   if (property.Name == fieldName)
   {
    property.SetValue(instance, record[i], null);
   }
  }
 }

 return instance;
}

Reply | Email | Delete | Modify | 
Re: MapperBase by Matthew On November 13, 2006
Nice.  Thanks.
Reply | Email | Delete | Modify | 
Re: MapperBase by Rodrigo On March 17, 2007

Hi,

when I use "where T : new" in my code I've not gotten compile it.

the compiler error focus is Line: "protected abstract MapperBase<T> GetMapper();" on class ObjectReaderBase.

the error message is: "The type 'T' must have a public parameterless constructor in order to use it as parameter 'T' in the generic type or method"

I dont know what's my class need.

Thanks.

Reply | Email | Delete | Modify | 
Re: Re: MapperBase by Dennis On May 23, 2007

Hi rodrigod,

Check that your class has a parameterless contructor:

new() constraint - The type argument must expose a public, parameterless (default) constructor

It works fine for me.

D

Reply | Email | Delete | Modify | 
Re: Re: MapperBase by Ashok On June 27, 2007

To get compile your code please add constructor constriant ( where T: new()) to other base classes also.

to ObjectReaderWithConnection and to ObjectReaderBase

like

class ObjectReaderBase<T> where T : new()

class ObjectReaderWithConnection<T> : ObjectReaderBase<T> where T : new()

 

hope this helps

Regards

Ashok

 

Reply | Email | Delete | Modify | 
how about parameters? by Tom On August 7, 2007
Hello, I find this piece of code indeed an elegant but it lacks of usability or am I missing something? I need to parametrise Readers. Lets take PersonReader. I need to make method like: Person person = personReaderInstance.getPerson(personId); And I wish to have couple of methods like this per reader with different parameters. What then? I'm trying to walk around this issue but my conceptions violate the elegancy :/
Reply | Email | Delete | Modify | 
Re: how about parameters? by shirkdaddy On March 15, 2008
Did anyone ever find a solution for parametrized readers? I'm running into the same issue. Every path I go down seems to contradict the elegance of this solution. I thought about providing another Execute method like: public T Execute(int objectId) { ... map here ...} but that doesn't seem right either (CommandText and parameters are set in the PersonReader). I suppose some flag could be set in ObjectReaderBase and some logic added to PersonReader to handle this, but bleck! Makes the elegance in the rest of the code seem pointless. Not to mention we'd have to create Execute methods for every combination of parameters needed to return a single object ref of any possible type...
Reply | Email | Delete | Modify | 
Example to Insert,Update... by Nivitha On January 15, 2008
Hi, This article has been very useful to me. Can you please give an example to perform Insert, Update, Delete operations?
Reply | Email | Delete | Modify | 
Prashant by Prashant On August 20, 2008
Hi, I like this article. Taking basic ideas from the DataAccess, I have created a simple framework. I might be posting the framework soon. Pras
Reply | Email | Delete | Modify | 
Prashant by Prashant On August 20, 2008
Hi, I like this article. Taking basic ideas from the DataAccess, I have created a simple framework. I might be posting the framework soon. Pras
Reply | Email | Delete | Modify | 
Layers structure by Giorgio On October 13, 2008
Hi there, Nice article! Suppose I'm separating Dal and business logic layers on two different assemblies and let both depend on on a third assembly containing interfaces for abstracting dependencies. Where would the PersonMapper class be? It seems to me that the business logic layer would be the correct place but how would the class PersonMapper be instantiated by the dal class without breaking separation of the layers?
Reply | Email | Delete | Modify | 

 Hosted by MaximumASP  |  Found a broken link?  |  Contact Us  |  Terms & conditions  |  Privacy Policy  |  Site Map  |  Suggest an Idea  |  Media Kit
Current Version: 5.2009.6.2
 © 2010  contents copyright of their authors. Rest everything copyright Mindcracker. All rights reserved.