asp tutorials, asp.net tutorials, sample code, and Microsoft news from 15Seconds
Data Access  |   Troubleshooting  |   Security  |   Performance  |   ADSI  |   Upload  |   Email  |   Control Building  |   Component Building  |   Forms  |   XML  |   Web Services  |   ASP.NET  |   .NET Features  |   .NET 2.0  |   App Development  |   App Architecture  |   IIS  |   Wireless
 
Pioneering Active Server
 Power Search





Active News
15 Seconds Weekly Newsletter
• Complete Coverage
• Site Updates
• Upcoming Features

More Free Newsletters
Reference
News
Articles
Archive
Writers
Code Samples
Components
Tools
FAQ
Feedback
Books
Links
DL Archives
Community
Messageboard
List Servers
Mailing List
WebHosts
Consultants
Tech Jobs
15 Seconds
Home
Site Map
Press
Legal
Privacy Policy
internet.commerce














internet.com
IT
Developer
Internet News
Small Business
Personal Technology
International

Search internet.com
Advertise
Corporate Info
Newsletters
Tech Jobs
E-mail Offers

HardwareCentral
Compare products, prices, and stores at Hardware Central!

Strategy for Building a Pluggable Data Access Layer in .NET
By Adam Sills
Rating: 3.8 out of 5
Rate this article


  • email this article to a colleague
  • suggest an article

    Introduction

    Nearly every commercial (and non-commercial) application uses some sort of data access technique. Usually this is some sort of database, be it SQL Server, MySQL, Access, an XML-based database, or simply flat text files. No matter what data access techniques are used, the application can easily become tied to one specific technique and make porting the application to use another data access layer cumbersome at best.

    ADO (OLEDB/ODBC) and ADO.NET are Microsoft's solution to an independent data access architecture. While these technologies are incredibly powerful and very effective when used correctly, they still only enable an application developer to tie his or her application to one specific data store. SQL Server and the System.Data.SqlClient namespaces in .NET tie an application to SQL Server. This can be remedied by using the System.Data.OleDb namespaces; however, using stored procedures will limit your application to SQL Server.

    Many application developers write their applications for the lowest common denominator. If the developer is targeting a Windows machine for the application, then the developer typically uses only what is common between Access and SQL Server. This means stored procedures are not used. SQL Statements that were once simple in SQL Server are ported and made convoluted to work in Access (things like SELECT TOP, SELECT @@Identity, etc). If this developer were targeting the application to run on MySQL, FoxPro, SQL Server, or Access, there would be a lot of additional work and testing on each database to make sure that the features in one database work correctly in another.

    While this solution can be modularized, be based on application-level settings, specify a particular database, and execute the appropriate code, very infrequently is the architecture of this data access technique truly "pluggable". These applications are typically targeted at a number of data stores and at the code written for them. Creating code to access new data stores can often be a heroic task, tracking down all the places where data access is used and adding yet another possible line of code to execute based on the new data store. Frequently application developers maintain multiple code bases for the different data access layers, causing a maintenance nightmare if not done correctly.

    The Solution

    The end goal of every developer who is creating an application that targets multiple data stores is to make it as easy as possible to switch seamlessly from one data store to another. While this is often possible, it is not always possible to have a data access architecture that can be completely customized without needing to modify the application itself.

    The solution is to create a simple abstract factory pattern for data access. Provide a set of standard "in and out" interfaces to someone developing a data access layer for a new data store. As long as the person developing the data access layer understands exactly what comes into the layer and exactly what should come back out of it, the theory is that you should be able to "plug in" a new data layer without the application ever knowing that it's now pointing to a new source for its data.

    An abstract factory is simply a class that delegates object creation to a concrete factory. The application only knows about the abstract interfaces defined ahead of time that it needs to use to access its data store. The "pluggable data layer" provides the concrete classes and exposes them as interfaces, so the application never needs to maintain references to the actual classes that are being used.

    Simple User Management Application

    From this point on, I will refer to a fictitious user management application called UserStore. UserStore has some pretty simple goals: be written in C#.NET; store pertinent user information such as name and birth date; store phone numbers and addresses.

    UserStore's object hierarchy is relatively simple:

    As you can see, UserStore's classes aren't incredibly complicated. Were this a typical real-world application, classes would be much more involved than this; however, this is all that is needed for this article. From this diagram, we can definitely tell a number of points of entry into the database:

    • Getting a User class's data
    • Getting the data for the Addresses in a User
    • Getting the data for the PhoneNumbers in a User
    • Adding and Removing PhoneNumbers
    • Adding and Removing Addresses
    • Persisting data to the database for every class

    The Abstract Factory Interface

    The goal of the pluggable data layer is (as the name explicitly states) to be able to plug one set of data code in at any time without changing the code. This is accomplished by creating a set of interfaces and one class that contains nothing but a static method. The concept is relatively simple; create a set of predefined interfaces (these can also be abstract classes, but I prefer interfaces for this specific instance) that your business classes will call to, but without knowledge of the exact type of class that it is creating. How is this done? Take a look at the following code:

    
    public interface IUserStoreFactory {}
    
    public class UserStoreDataFactory
    {			
    	public static IUserStoreFactory ConcreteDataClass()
    	{
    		Type type = Type.GetType(
    			Configuration.AppSettings["ClassType"]
    			);
    		return (IUserStoreFactory)
    			Activator.CreateInstance(type, null);
    	}
    }
    
    
    There are a couple of things going on that need explaining. First there is an interface, IuserStoreFactory, which represents the interfaces necessary to create concrete data classes. This will be described later. Second, there is a class, UserStoreDataFactory, which has a single static method, ConcreteDataClass, that returns an IuserStoreFactory. Without looking at the details of the code, you should be able to understand that this method will somehow magically create an instance of the concrete data factory class and return it as an IuserStoreFactory.

    The details of the ConcreteDataClass method should be relatively simple to understand. First off, a System.Type object is retrieved using an appSettings entry which is defined in an application configuration file. Simply create a configuration file and indicate which class to use:

    
    <configuration>
    	<appSettings>
         <add key="ClassType"                
          value="YourNamespace.YourClass,YourAssembly" />
    	</appSettings>
    </configuration>
    
    
    From that, a System.Type object is created and passed to Activator.CreateInstance to create an instance of your class to be used as an IuserStoreFactory.

    Some people like to create a separate method for every specific type of concrete factory being created. That is to have a "CreateSqlDataFactory()" and "CreateAccessDataFactory()". While these are valid approaches, it limits the developer to only [x] number of predefined sets of classes they can instantiate. Retrieving the type of class from a configuration file means having your code automatically understand a brand new class factory without explicitly calling one of the more specific methods.

    Now that the UserStoreDataFactory class is created, and we understand how it returns an instance of the concrete classes, let's take a look at some more interfaces that our application will use:

    
    public interface IUserStoreFactory 
    {
    	IUserData GetUserData();
    	IAddressesData GetAddressesData();
    	IPhoneNumbersData GetPhoneNumbersData();
    }
    public interface IUserData
    {
    void RetrieveUserData(int uniqueID, out string lname,
       out string fname, out DateTime birthDate);
    void SaveUserData(int uniqueID, string lname,
       string fname, DateTime birthDate);
    }
    public interface IAddressesData
    {
    DataSet RetrieveAddresses(int userid);
    SaveAddress(...);
    }
    public interface IPhoneNumbersData
    {
    DataSet RetrievePhoneNumbers(int userid);
    SavePhoneNumber(...);
    }
    
    
    First, notice that IuserStoreFactory has been expanded to contain methods for retrieving specific data classes. Each method returns an instance of a class that implements a given interface that is used to retrieve and set data in an implementation-independent manner. For instance, IUserData.RetrieveUserData requests that a uniqueID be passed to it, then sends back (in the form of out parameters) the data for the given user. You should also notice the methods on IaddressesData and IphoneNumbersData that return DataSets. Since a DataSet is a database-independent data structure, it's useful as a generic store for data, provided that the developer of the business objects and the developer of the data classes understand what needs to come out of the DataSet. For addresses, the DataSet needs a single table with zero or more rows and 5 columns (Street1, Street2, City, State and PostalCode). For phone numbers, the DataSet needs a single table with zero or more rows and 4 columns (CountryCode, AreaCode, Prefix and Number). As long as those assumptions are made and documented and the data classes follow the rules, using a DataSet is a perfect way to transfer data in an implementation-independent manner.

    Creating Concrete Factory and Data Classes

    All of this is good in theory; however, it's only as good as my word until you can see how it is implemented. Imagine that we wanted to use SQL Server 2000 to power UserStore application. What we would do is create a set of classes (4 in particular) that implemented our interfaces defined above. One class (SqlUserStoreFactory) would implement IUserStoreFactory, and then we would have 3 other classes that implemented the other 3 interfaces (SqlUserData, SqlAddressesData and SqlPhoneNumbersData). The SqlUserStoreFactory class would implement the 3 methods on the IUserStoreFactory interface and return the appropriate SQL data class. Here is some sample code:

    
    namespace squishyWARE.UserStore
    {
    public class SqlUserStoreFactory : IUserStoreFactory
    {
    		public IUserData GetUserData()
    		{
    			return new SqlUserData();
    		}
    		public IAddressesData GetAddressesData()
    		{
    			return new SqlAddressesData();
    		}
    		public IPhoneNumbersData GetPhoneNumbersData()
    		{
    			return new SqlPhoneNumbersData();
    		}
    }
    }
    
    
    You can see here how the factory class is responsible in creating the appropriate data class when it is requested. Your business object would simply call UserStoreDataFactory. ConcreteDataClass() to get an instance of your SqlUserStoreFactory, then call the appropriate method to get an instance of one of the data-manipulating classes (SqlUserData, SqlAddressesData, SqlPhoneNumbersData) and call methods on those classes (which are defined in the interfaces) to get and set data.

    Now imagine that you wanted to add a new data provider that your application can use, be it Access, MySQL, XML, .NET MyServices, or anything else you can think of. All you would need to do is create a new set of data classes that implemented your defined interfaces and then "register" the factory class in your configuration file. Once you do that, the UserStore application is using a brand new data source, and it never knew the difference. Because you defined a set of interfaces for all of your data manipulation and only ever reference the interfaces themselves and not the implementations of your interfaces, you can truly plug in a brand new data source.

    Conclusion

    What you have seen here is a way to create a data access layer that is abstracted to a level where the application and business objects never realize how the data access is implemented. All data access is handled through a set of defined interfaces that your business objects can work with, and you can be free to implement any number of data access layers without ever needing to modify your application code.

    FAQ

    Q: What about speed? Won't dynamically creating these objects at runtime impact performance?
    A: It will, but not significantly. The .NET runtime has in it what I like to call "magic". Well not really, but it does have speed on its side. Activator.CreateInstance is definitely what I would call a slow way to construct an object. After the object is constructed, the speed of using the interfaces is just like any other interface.

    Q: If I create my classes and compile them, where do I need to place the assembly?
    A: Your concrete data class assembly needs to be somewhere that the .NET runtime can find it. For private deployment it will need to be in the bin folder of your application, or in a subfolder if you have set up probing in your configuration file. You can also optionally install your assembly into the GAC (Global Assembly Cache) provided you have given it a strong name.

    Q: What if I already have my own set of data classes, such as the Microsoft Data Access Application Block?
    A: That's perfectly fine. One of the best things about this technique is that it integrates seamlessly with other data access techniques. Accessing your data provider can occur any way you want it to; this technique simply abstracts the data access to a level where your application itself never needs to know how you implemented it. Implement this technique, then on your SQL Server or Access data layer you can use your other data access code to actually access the data store.

    Q: Are there any applications out there that use this technique? If so, where can I find one to see some examples?
    A: Many companies actually use this technique; however the actual implementation can vary from application to application. One application that you can find this specific implementation is squishyFORUMS. squishyFORUMS can be downloaded at http://www.surrealization.com. The download package contains the PluggableDataAccess code and a SQL Server 2000 implementation of the data access interfaces.

    About the Author

    Adam Sills is a Software Architect working at GreatLand Insurance, a subsidiary of Kemper Insurance. Adam specializes in Microsoft .NET applications, and has five years of professional experience working with Microsoft technologies.

    In addition to professional work, Adam created and maintains squishyFORUMS, a product under the company name squishyWARE ( http://www.squishyweb.com). squishyFORUMS is a multi-use forum object library targeted at developers. squishyFORUMS powers a small number of high-traffic sites, in addition to his personal weblog (http://www.surrealization.com). Techniques used in squishyFORUMS was the driving force behind this article.

    Adam can be reached at asills@squishyweb.com

  • Rate This Article
    Not HelpfulMost Helpful
    1 2 3 4 5
    Supporting Products/Tools
    Proposion N2N
    Proposion N2N connects Microsoft .NET applications to Lotus Notes and Lotus Domino databases. This ADO.NET managed data provider allows you to perform blindingly fast queries and updates of Notes data from ASP.NET pages, .NET web services, Windows, or Mobile applications. An innovative SQL-like query language leverages the unique features of Notes and makes collaborative software accessible to relational database programmers.
    [Top]
    Other Articles
    Sep 15, 2005 - Building an Image Keyword System
    Unlike text-based file formats image files aren't made up of words, which makes searching for an image file by keyword difficult. Instead of being able to simply open the file to see what it contains, we're stuck looking at the text around it and other metadata to determine the image's meaning. In this article, Ziran Sun shows you how to build a simple database-based image keyword system that allows you to associate keywords with images and use these keywords to make finding images easier.
    [Read This Article]  [Top]
    Apr 7, 2005 - A Step-by-Step Guide To Using MySQL with ASP.NET - Part 2
    In the second part of of his article on using MySQL with ASP.NET, Ziran Sun covers how to add a new MySQL user to the database server, assign the user the appropriate permissions, connect to the database, and build a simple ASP.NET page to perform a query.
    [Read This Article]  [Top]
    Feb 10, 2005 - A Step-by-Step Guide To Using MySQL with ASP.NET - Part 1
    Back in the days of classic ASP, if you were building a database-driven web site, your choice was either to invest a lot of money to get a copy of Microsoft SQL Server (or some other enterprise-ready database) or invest a lot of time finding a way to deal with the performance and scalability limitations of Microsoft Access. Luckily these days there's another viable alternative: MySQL.
    [Read This Article]  [Top]
    Jan 27, 2005 - Moving a Database from SQL Server 7.0 to SQL Server 2000
    Moving or copying a SQL Server database from one machine to another requires a lot of preparation in order to ensure a smooth transfer. In this article, Dina Fleet Berry examines the different methods and highlights the different issues associated with each of them.
    [Read This Article]  [Top]
    Jan 6, 2005 - Debugging a SQL Stored Procedure from inside SQL Server 2000 Query Analyzer
    There are many times when using SQL Server 2000 Query Analyzer to debug SQL statements is a better choice than debugging in Visual Studio .NET. In this article, Dina Fleet Berry explains why and walks you through the debugging process step-by step.
    [Read This Article]  [Top]
    Nov 24, 2004 - Persisting .NET Objects to SQL Server Using SQLXML and Serialization
    As a follow up to his article on retrieving objects from SQL Server using SQLXML and serialization, Gianluca Nuzzo discusses saving objects back to SQL Server using a schema definition file and updategrams.
    [Read This Article]  [Top]
    Sep 14, 2004 - Transaction Processing in ADO.NET 2.0
    One area that stands out when comparing ADO.NET 1.x to ADO.NET 2.0 is transaction processing. Bill Ryan shows just how easy transaction processing has become with the TransactionScope object in ADO.NET 2.0.
    [Read This Article]  [Top]
    Sep 8, 2004 - Custom Object Data Binding with .NET
    Developers often use brute force coding to marshal data between the GUI and application objects. In this article, Luther Stanton explains how to use .NET's out-of-the box data-binding functionality to make this job much easier.
    [Read This Article]  [Top]
    Sep 2, 2004 - Queue MSMQ Messages from SQL Server
    Learn how to create a console application to queue a message in Microsoft Message Queuing (MSMQ) and then use an extended stored procedure to call the console application from a SQL Server trigger.
    [Read This Article]  [Top]
    Aug 30, 2004 - Tuning Up ADO.NET Connection Pooling in ASP.NET Applications
    Connection pooling increases the performance of Web applications by reusing active database connections instead of creating a new connection with every request. This article shows how to monitor the connection pool, diagnose a potential problem, and apply the appropriate fix.
    [Read This Article]  [Top]
    Mailing List
    Want to receive email when the next article is published? Just Click Here to sign up.

    Support the Active Server Industry



    JupiterOnlineMedia

    internet.comearthweb.comDevx.commediabistro.comGraphics.com

    Search:

    Jupitermedia Corporation has two divisions: Jupiterimages and JupiterOnlineMedia

    Jupitermedia Corporate Info


    Legal Notices, Licensing, Reprints, & Permissions, Privacy Policy.

    Advertise | Newsletters | Tech Jobs | Shopping | E-mail Offers