Friday, June 26, 2009

A Look at ASP.NET 2.0's Provider Model

Introduction
Back in 2001 I started work on an ASP.NET online messageboard application called WebForums.NET. The idea was to create an ASP.NET-based messageboard system that could easily be plugged into an existing website. One particular challenge building such an end-to-end application was providing a means for customers to be able to integrate it within their system. For example, clearly an online forum needs some sort of data store to store user information, forums, posts, and so on, but what's important is not to lock the customer into a particular data store. That is, you shouldn't say, "I'm going to have my application use Microsoft SQL Server 2000," because at that point, how are customers who use Oracle or Access going to be able to use your software?

Another issue is with integration into a customer's existing data. All online forum sites have user accounts and a means for creating new accounts; this is typically modeled in the forum's architecture as a Users table in the database. But customers might already have their own user-related database tables that already have thousands of user accounts. Or a customer might want to use the forums in an intranet setting, authenticating and storing user information against Active Directory, rather than some database table. When a forum software system creates a Users database table and says to the customer, "This is how you will store users," they alienate those customers who already have existing infrastructure and user data. Clearly this challenge faces any company or developer that's creating software to be used "in the wild."

This particular challenge arises when you build a system with a rigid API implementation. Rather than providing a means to customize the logic, a rigid API implementation hard codes the implementation details - you will use SQL Server as your backend data store... you will have a Users table in this database where all user information will be stored. This rigidity, however, can be broken quite easily using the provider design pattern. With the provider design pattern the system architect merely defines the API, the programmatic functionality offerred by the system. For an online forum application, this might include a Users class with methods like Authenticate(username, password) and GetUserDetails(username).

The beauty of the provider model is that the customer implementing the solution can specify a custom class that the system should use. This custom class must implement the system's well-defined API, but it allows for any custom implementation to be seamlessly plugged in. That is, once this API is defined, the system implementor can create a default concrete implementation - one that uses SQL Server and a Users table - that most customers can use without modification. Those customers that have a custom need - those that want to use Oracle or have user data stored in some other manner - can create classes that provide the necessary functionality and plug them into the system.

The provider design pattern is used throughout ASP.NET 2.0. There are also guidelines on how to provide this functionality in ASP.NET 1.x applications. In this article we'll examine the provider model and see how it's used in ASP.NET 2.0. Read on to learn more!
Breaking the Rigid API ImplementationFairly early on in my development of WebForums.NET I realized that the rigid API implementation was going to be a problem. Part of my design goal for the software was to make it as flexible and customizable as possible, and boxing users into using SQL Server and my implementation of the users data model seemed restrictive, at best. Unfortunately, I didn't have any insight into this problem, but luckily
Andy Smith did. He suggested (and built!) a system that involved two pieces:
A set of abstract base classes that defined the system's core functionality, and
Code that would, at runtime, dynamically load a specified class that extended the abstract base class and use its functionality. Specifically, the code inspected the Web.config file, which contained a that gave the fully-qualified name for the class to use. With this architecture, I could define the system's functionality through a series of abstract base classes, provide concrete implementations of these classes using SQL Server 2000 and the Users table. Customers that were happy with this configuration could just use the application as-is and everything would work fine without them needing to write a line of code. Those developers who needed to customize the solution for their unique needs, however, could do so by creating their own classes that derived from the appropriate abstract base class(es). They could have this new class used by the system by simply dropping the assembly in the application's /bin directory and updating the Web.config file.
Specifically, WebForums.NET shipped with an abstract base class called DataProvider that spelled out all of the methods in the system, akin to:
public abstract class DataProvider
{
public abstract bool AuthenticateUser(string username, string password);
public abstract User GetUserInfo(string username);
...

public static DataProvider Instance()
{
...
}
}
The AuthenticateUser(username, password) and GetUserInfo(username) methods spelled out two of the many methods defined by the system. The static Instance() method was the workhorse of the DataProvider class. It contained the code that inspected the Web.config file for the WebForums.NET configuration information, which indicated the fully-qualified name of the class the system was to use. The method then used reflection (with caching) to create an instance of that class and return it to the system.
WebForums.NET shipped with a class called SqlDataProvider that extended the DataProvider base class, providing concrete implementations for the assorted methods. For example, all methods in SqlDataProvider worked with data stored in a SQL Server 2000 database; the user-related methods worked with a pre-defined Users database table. A customer who wanted to change the backend functionality could create her own class that derived from DataProvider, indicating in the Web.config file that their custom class should be used. For example, the Web.config in WebForums.NET would include the following content:

This setting information, by default, referenced the SqlDataProvider class that shipped with WebForums.NET. However, if a customer created their own implementation of the API, they could provide their class's details here, and the system would automatically start using their implementation in lieu of the default implementation.
With this architecture in place, the page developer working with the WebForums.NET could use code like the following to authenticate a user:
if (DataProvider.Instance().AuthenticateUser(username, password))
// User is authenticated
else
// Username/password invalid!
When the DataProvider.Instance() method is called, the configuration file is examined and an instance of the appropriate class is returned. This would be the default SqlDataProvider class if the customer has not created their own implementation; it would be their own class if they did. Once the DataProvider.Instance() method returns an instance of the appropriate provider, we can simply invoke the members of the API, AuthenticateUser() in this example.

The Benefits of the Provider ModelThe provider model offers a number of benefits. First, note that there's a clean separation between the code and the backend implementation. Regardless if whether or not the code to authenticate a user is done against a SQL Server 2000 database's Users table, or if it's done against an Active Directory store, the code from the page developer's perspective is the same: DataProvider.Instance().AuthenticateUser(username, password). The backend implementation changes are transparent.
Since system architects are strongly encouraged to create a default concrete implementation, the provider model offers the best of both worlds: to those who are content with the default implementation, the system just works as expected; for those that need to customize the system, they can do so without upsetting the existing code or programmatic logic. This design pattern also makes prototyping and agile development a lot easier. For example, in the early iterations of working with the system, it might be easier to just use the default implementation. However, later you suspect you'll need to cusomize certain aspects in order to integrate the work with your company's existing systems. When that time comes, you can achieve the needed customization through the provider model, meaning your earlier work need not be changed to reflect the backend implementation changes.
Like many good design patterns, the provider model also affords separation of duties among developers. One set of developers can be tasked with mastering the system's API, while others can be tasked with focusing on the backend implementation and customization. These two groups can work on the system without stepping on one another's toes. Furthermore, if the system being worked on is an industry standard - like ASP.NET 2.0 - skills from both tasks can be easily carried over into future jobs.
The Provider Model in ASP.NET 2.0ASP.NET 2.0 utilizes the provider model throughout its architecture. Many of its subsystems - Membership, Site Navigation, Personalization, and so on - utilize the provider model. Each of these subsystems provide a default implementation, but enable customers to tweak the functionality to their own needs. For example, the Site Navigation piece of ASP.NET 2.0 allows a page developer to define the navigational structure of their website. This data can then be used by a variety of Web controls, to display site maps, breadcrumbs, treeviews, or menus that highlight the site's navigation and/or show the user's location in the site. In addition to navigation-related Web controls, the site navigation API provides a bevy of methods for interacting with the website's navigation information
By default the site's navigational information must be encoded in a properly-formatted XML file. This is the data store that the default site navigation is hard-coded to use. However, ASP.NET 2.0's provider model makes it easy for you to use your own data store for site navigation. For example, in a project I'm currently working on a database contains information on what pages exist in the site and what permissions various users have for those pages. Rather than redefining this information in an XML file and having to try to keep two copies of this information up to date, in order to utilize the site navigation in ASP.NET 2.0 I will be able to simply create a provider class that works directly with the database information. Once this class is created and specified in the website's configuration, the navigation Web controls will be working against the application's custom navigation information stored in the database. (Note: as of the time of this writing, this project is still in ASP.NET 1.x. However, this example hopefully illustrates the benefit of the provider model.)
Personally I think that the provider model is one of ASP.NET 2.0's greatest features for migration. ASP.NET 2.0 offers a lot of new features that developers had to custom bake in 1.x. If these new features in 2.0 used rigid implementations, it would dissuade migration from 'living' 1.x applications that used custom solutions since many of the new ASP.NET 2.0 Web controls use these new subsystems. With the provider model in place, however, we can upgrade our 1.x apps to 2.0 and create a provider to have 2.0's new subsystems integrate with our custom baked solutions. That means when moving to 2.0 we can use the new Web controls and have them seamlessly use our existing systems thanks to the provider model.


Source: http://aspnet.4guysfromrolla.com/articles/101905-1.aspx

No comments: