CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

David Hayden [MVP C#]

         .NET Tutorials, Patterns, and Practices

Data Access Application Block Revealed - Factory Methods and Reflection

The other day I talked about the Data Access Application Block and how you can use it in your applications to assist you with ADO.NET.

In particular, I briefly discussed this piece of code:

 

Data Access Application Block
Database db = DatabaseFactory.CreateDatabase();

string query = "Select * from Customers Where CustomerID = @CustomerID";

DBCommandWrapper command = db.GetSqlStringCommandWrapper(query);

command.AddInParameter("@CustomerID", DbType.String, id);

using (IDataReader reader = db.ExecuteReader(command))
{
    // Do Something...
}

 

One of the statements worth understanding a bit better is

 

Factory Method
Database db = DatabaseFactory.CreateDatabase();
Defers the decision of which class to instantiate.

 

If you take a peek at the DAAB source code, that statement is fairly involved on the surface.  It tosses you around to a number of classes and overloaded methods that could be difficult to figure out if you are new to .NET and object-oriented programming.  There is some pretty cool stuff going on there, so I thought I would mimic this statement with an example that is easier to understand.

In the DAAB, Database is an abstract class.  As any abstract class, you cannot create an instance of it.  You can only create concrete classes that derive from Database.  The DAAB currently has 2 such concrete classes: SqlDatabase and OracleDatabase.

So given this piece of knowledge, we can infer that the DatabaseFactory.CreateDatabase() static method will probably return an object of SqlDatabase or OracleDatabase which will be assigned to db.  The actual concrete class will probably be created on-the-fly based on some logic (in this case the settings in the DAAB configuration file).  This type of method is called a Factory Method as it defers the decision of which class to instantiate.

Let's mimic this concept by building our own classes and factory method using an example that is closely related.  First our database classes.  Although they are on a much simpler scale, the classes below follow the same pattern as in the DAAB.  We have an abstract class, called Database, and two concrete classes, called SqlServer and Oracle, which derive from our base class of Database.  I have one simple abstract method in Database, called GetManufacturer().  As an abstract method, all derived classes are required to implement this method and has been done so accordingly.

 

Database Classes
public abstract class Database
{
    public abstract string GetManufacturer();
}

public class SqlServer : Database
{
    public SqlServer() {}
    
    public override string GetManufacturer()
    {
        return "Microsoft";
    }
}

public class Oracle : Database
{
    public Oracle() {}
    
    public override string GetManufacturer()
    {
        return "Oracle";
    }
}

 

Now we need to create a Factory Method on a class that creates an instance of SqlServer or Oracle on the fly.  Let's do it similar to the DAAB, but with only one class for simplicity.

 

Class Factory
public sealed class DatabaseFactory
{
    private DatabaseFactory () {}
    
    public static Database CreateDatabase()
    {
        return CreateDatabase("SqlServer");
    }
    
    public static Database CreateDatabase(string databaseName)
    {
        if (databaseName.Equals("SqlServer") || databaseName.Equals("Oracle"))
        {
            // Find the class
            Type database = Type.GetType(databaseName);
            
            // Get its constructor
            ConstructorInfo constructor = database.GetConstructor(new Type[] {});
            
            // Invoke its constructor, which returns and instance.
            Object createdObject = constructor.Invoke(null);
            
            // Pass back the instance as a Database
            return createdObject as Database;
        }
        else
            throw new ArgumentException("Never heard of the database " + databaseName);
    }
}

 

The class is sealed so nobody derives from it and has a private constructor so nobody can create an instance of it.  This is pretty common for these types of classes.

We have essentially two static factory methods on this class, CreateDatabase() and CreateDatabase(string databaseName), both of which return an object of type Database. If you don't specify a database, you get the default database, which is "SqlServer".  This is sort of like the DAAB, but the DAAB looks in the configuration file to find the default database.  In this case, I just hardcoded it for simplicity.

Now the cool stuff is in the second method, CreateDatabase(string databaseName).  The databaseName parameter is the name of the class type we want to create on-the-fly.  In our case, it either has to be SqlServer or Oracle, the two concrete classes we created that derive from Database.

The rest is simple reflection similar to how it is done in the DAAB.  The comments above really tell it all.  We find the type requested in databaseName (SqlServer or Oracle), get its default constructor, and then invoke its default constructor which passes back an instance of the type.  We then just cast the object to type Database and return it back to the calling program.

Here is code you can run to test all our classes:

 

Testing Our Classes
using System;
using System.Reflection;

public abstract class Database
{
    public abstract string GetManufacturer();
}

public class SqlServer : Database
{
    public SqlServer() {}
    
    public override string GetManufacturer()
    {
        return "Microsoft";
    }
}

public class Oracle : Database
{
    public Oracle() {}
    
    public override string GetManufacturer()
    {
        return "Oracle";
    }
}

public sealed class DatabaseFactory
{
    private DatabaseFactory () {}
    
    public static Database CreateDatabase()
    {
        return CreateDatabase("SqlServer");
    }
    
    public static Database CreateDatabase(string databaseName)
    {
        if (databaseName.Equals("SqlServer") || databaseName.Equals("Oracle"))
        {
            // Find the class
            Type database = Type.GetType(databaseName);
            
            // Get it's constructor
            ConstructorInfo constructor = database.GetConstructor(new Type[] {});
            
            // Invoke it's constructor, which returns and instance.
            Object createdObject = constructor.Invoke(null);
            
            // Pass back the instance as a Database
            return createdObject as Database;
        }
        else
            throw new ArgumentException("Never heard of the database " + databaseName);
    }
}

public class TestClass
{
    public static void Main()
    {
        // Default is Sql Server
        Database defaultDatabase = DatabaseFactory.CreateDatabase();
        Console.WriteLine(defaultDatabase.GetManufacturer());
        Console.ReadLine();
        
        // Asking explicitly for Oracle
        Database oracle = DatabaseFactory.CreateDatabase("Oracle");
        Console.WriteLine(oracle.GetManufacturer());
        Console.ReadLine();
    }
}

 

Hopefully the above code will illuminate what is going on in the DAAB when you make a call to DatabaseFactory.CreateDatabase().  Although we are doing it on a much simpler scale, it is the same technique and one you can use in your applications.



Comments

TrackBack said:

# March 18, 2005 9:06 AM

David Hayden said:

In my post, Data Access Application Block Revealed - Factory Methods and Reflection, I talked about how...
# April 1, 2005 10:08 AM
Check out Devlicio.us!

Our Sponsors

Free Tech Publications

This Blog

Syndication

News

CodeBetter.Com Home