The ASP.NET Provider Model drives many features within the ASP.NET architecture, yet most people only relate it to security or membership-related functionality. In this article, I’ll take you deep into the provider model, show you how it’s used from a security context, then take it up a notch and teach you how you can use it to turn any feature in your application into an extensible and swappable component. In fact, I’ll even show you why calling it the ‘ASP.NET’ Provider Model may be a misnomer.
Back when Microsoft released ASP.NET 2.0 (remember those days?), Microsoft rewrote several of its areas of functionality using a pattern that has become known as the Provider Model. Some of the features existed back in ASP.NET 1.x and some, like security, were new to the platform. The most important aspect to all this is that each of these areas of functionality now benefited from something that I still find developers take for granted-the ability to be extended and swapped when the situation calls for it.
What Is the Provider Model?
In a previous article for CoDe Magazine, I wrote about extensibility and how to design applications that you can easily modify and enhance using plug-in or swappable components. In that article I described the Strategy Pattern, which allows you to define abstraction for information going in or out of a component, then instantiate one of possibly many concrete implementations of said abstraction. In fact, in that article I show you how to perform the “hot-swapping” declaratively so you never have to change code. Microsoft based the Provider Model on the Strategy Pattern. The nomenclature derives from the essence of what this pattern offers. The components that adhere to it provide something to the consumer.
ADO.NET
Whether you realize it or not, most of you have been using a Provider Model for a long time. It is the core pattern behind the beauty of ADO.NET. You have probably created objects based on the SqlConnection class, commands based on the SqlCommand class, and data readers based on the SqlDataReader class. Of course you know these classes are used to access data from SQL Server databases only. If you need to access a Sybase server, for example, using OLE DB technology, you would use the OleDbConnection, OleDbCommand, and OleDbDataReader classes.
Providers make great wrappers for WCF Proxy Classes allowing you to completely hide all your WCF code from the client developer.
In either of the two connection objects, you can set the ConnectionString property and call the Open method. You can use the CreateCommand method to create the command object, and then call its ExecuteReader method to create your data reader. The point is that the behavior is common. This is due to the fact that there are some interfaces in the System.Data namespace that set the framework for these classes. Both the SqlConnection and OleDbConnection classes implement the IDbConnection interface. It is in this interface that the ConnectionString property, the Open method, and the CreateCommand method are defined.
The beauty of this pattern hasn’t surfaced yet. It comes from the ability to do something called programming to the interfaces, or more accurately, programming to the abstraction. I correct myself here because in the ADO.NET model, the abstractions are a set of interfaces, but in other cases as you will later see, they can be abstract classes. Programming to the abstraction means you use the abstract definitions in as many places as you can to define your objects; and only refer to the concrete classes in the case of an instantiation. Take this example:
In C#:
IDbConnection conn =
new SqlConnection("{conn str}");
conn.Open();
IDbCommand cmd = conn.CreateCommand();
cmd.CommandText = "myStoredProc";
cmd.CommandType = CommandType.StoredProcedure;
IDataReader reader = cmd.ExecuteReader();
In VB:
Dim conn As IDbConnection = _
New SqlConnection("{conn str}")
conn.Open()
Dim cmd As IDbCommand = conn.CreateCommand()
cmd.CommandText = "myStoredProc"
cmd.CommandType = CommandType.StoredProcedure
Dim reader As IDataReader = cmd.ExecuteReader()
As you can see, I’ve used a concrete class only once; when instantiating SqlConnection. If I want to use this same code to access a database using OLE DB (or even ODBC), I only need to change SqlConnection to OleDbConnection.
While this is a great programming style, and certainly one that I encourage, the fact that I still have to change code in order to swap Providers is still a shortcoming. I don’t want to digress too far into ADO.NET so I’ll direct you to look into the ADO.NET DatabaseFactory object to solve this particular problem. With this knowledge of the object design behind ADO.NET fresh in your mind, let’s shift over to ASP.NET membership.
ASP.NET Membership and Roles
ASP.NET Membership Services has become a tremendous time-saver. Microsoft has provided developers (no pun intended) with a great way of quickly setting up authentication and authorization. You start by automatically setting up your database using the provided aspnet_regsql utility, and then simply use the Membership and Roles static classes to perform your functionality. If you want to validate a user, given their user name and password, simply call:
Membership.ValidateUser(username, password)
The returned Boolean value indicates success or failure. The same applies for many other features like user creation, role checking, etc. The question (and really the heart of this article), is how does this command know exactly what to do in order to validate the user against your database? And that, my friends, is the beauty of the provider model.
When you use the Membership static class by calling its methods, you actually make a check to see what provider is currently installed for this particular feature (in this case, membership). You couldn’t use the statement I showed above without placing a <membership> section in your web.config file. You can see this section in Listing 1. This configuration section will tell the Membership class what specific membership provider to use. A membership provider needs to adhere to a specific contract, which in this case is solidified by inheriting from a class called MembershipProvider. It is this class that defines methods like ValidateUser, CreateUser, etc. So even though you see these methods in the Membership static class, or factory class as it is also known, those methods are, in fact, wrappers and are actually delegating the call to the corresponding method in the installed membership provider. I’ll go through the steps that take place from beginning to end to clear things up, and then I’ll tear it apart and show you how you can use the same model for any feature you want.
So, once again I’ll make a call out to the following statement:
Membership.ValidateUser("miguel", "dotnet");
As is obvious by this statement, Membership is a static class, also referred to as a factory class. This class has a static constructor and its job is to access the configuration section shown in Listing 1 to determine what provider is currently installed. Now, let’s look at the contents of that configuration section in detail because, as my old high school teachers used to say, you will see this material again.
The section is called membership and it immediately shows an attribute called defaultProvider. The value of this attribute must correspond to one of the providers listed in the providers section of the configuration section.
<add
name="AspNetSqlMembershipProvider"
type="System.Web.Security.SqlMembershipProvider,
System.Web"
… />
The linkage is made using the name attribute of the add element. The type attribute specifies which provider to use. As you can see, this is specified in standard .NET type notation which consists of the fully qualified class and the assembly (sans DLL) separated by a comma. Listing 1 shows a remove element first, followed by an add element, both referring to the same name. This is because the root web.config file at the machine level already has a membership section and also adds a membership provider of this same name. I could have chosen to simply “add” mine using a different name but I want to leave you with as much detailed information as possible. The type being installed here is a class called SqlMembershipProvider and it is this class that inherits from the MembershipProvider base class I told you about earlier. The code in this class provides the implementation of all the methods defined in MembershipProvider, and that implementation is specifically designed to hit all those great tables and stored procedures that were installed for me when I used the aspnet_regsql utility. If you asked yourself earlier how you would use the Membership factory class against your own database, and even a database of a different type other than SQL server, you should now start to see how to do that. MembershipProvider provides all the methods that the Membership factory class expects to see and wrap with its own factory methods. By writing your own class that inherits from MembershipProvider and installing it into the web.config file, you would be instructing the Membership factory class to use your class instead.
The membership configuration section installed in the machine’s root web.config file uses the SqlMembershipProvider class and the name AspNetSqlMembershipProvider. I chose to “remove” this name and “add” it again so I can provide different values to all the other attributes you see in Listing 1. Among these is the important connectionStringName attribute. The value of this attribute needs to be a connection string defined in the configuration file’s connectionStrings section. This attribute and all the others are specific to the feature of “membership”. If you look at the role subsystem, you’ll see that it is provider-driven as well. Its provider is defined in a configuration section called roleManager. In fact, that configuration section is virtually identical to the membership section in its layout. It too will start with a defaultProvider attribute, contain a providers section, which further adds providers using one or more add elements. Within the add element, the only consistent attributes are name and type. All other attributes a specific to the feature in question.
So let’s get back to the Membership class. When last I left it, I made a call to the ValidateUser method and as I said, the static constructor in Membership looked to see what implementation of MembershipProvider to use. When that determination was made, the appropriate provider class (SqlMembershipProvider in this case) was instantiated and stored in a private variable within the Membership class. This variable is of type MembershipProvider because this is the only concrete information the factory class has and this is where all the methods are defined. The ValidateUser method of the Membership class then made a call to the ValidateUser method of the object stored in the private variable I just mentioned. Remember, this is possible because once the provider was instantiated, it was cast to MembershipProvider then stored, and ValidateUser is defined in MembershipProvider. When this call takes place, the implementation of ValidateUser that is executed is the code in the SqlMembershipProvider class that is defined in the web.config and that is instantiated by the factory class.
This pattern is very consistent with other features within ASP.NET besides membership. These include role management, profiles, web part personalization, and site maps. Each of these features of ASP.NET all share similar characteristics which I indirectly mentioned and described previously, so now let me list them specifically.
Characteristics of a Provider
Factory classThis is the static class that serves as the client’s gateway into the features functionality. This class requires no instantiation; it simply is used and contains all the methods that provide this feature’s functionality by way of static methods. The code in each of these methods simply calls a corresponding method in an instantiated provider class.
The Strategy Pattern allows you to define abstraction for information going in or out of a component, then instantiate one of possibly many concrete implementations.
Feature-specific base classIn the previous example, this would be the MembershipProvider class. It is the abstract class that defines all the abstract methods that provides this feature’s functionality. It’s typically the case that these methods directly mimic those in the factory class-actually, it is the methods of the factory class that mimic the methods defined in this base class.
Configuration sectionSince a provider-based feature’s factory class needs to determine what provider implementation class to use, it needs a configuration section for each feature. As I stated earlier, there is a pattern in place for this configuration section and the only variances are really the parent tag name and the extra attributes that define a provider, with the exception of name and type. Now, if I want to write a provider-based feature (and I will later), I’ll need a custom configuration section. A custom configuration section means I need configuration classes to read all the information in my custom section, but thanks to the new model for reading configuration files that Microsoft introduced back with .NET 2.0, I don’t need to write a lot of this. Since there is a model in place, Microsoft has also provided several configuration-enabled classes that will read the internals of a provider-oriented configuration section. I’ll provide the details on how to use the configuration object model in another article (maybe the next one).
Provider implementation classIn the previous example, this would be the SqlMembershipProvider class, and is the class that inherits from the feature-specific base class (MembershipProvider in this example). All the specific implementation goes here.
ProviderBaseOne other characteristic of the provider model is another base class that serves as the base for all feature-specific base classes. I’ve mentioned the MembershipProvider base class and hinted about the RoleProvider base class. There also is a ProfileProvider class that serves as the base abstraction for any Profile providers you may write. Every feature I mentioned earlier has a base class that defines its methods and all these base classes have their start in the ProviderBase class. The commonality to all provider-based features starts with this class. This commonality includes the Name property (remember the name attribute?) and an Initialize method that you will learn about later.
So now that I’ve taken the membership provider apart and identified its characteristics and its behavior, I’ll apply all this to my own feature, completely outside the scope of membership, roles, or security in general.
A Provider-based Feature
You can apply the pattern I’ve just shown you to just about anything, and I’ll use it for a feature that’s known to most developers that have been involved in e-commerce-credit card processing. I’ve developed several e-commerce sites during my career and all of them needed to process credit cards. Several merchant processors out there offer APIs for this and I’ve had to handle several of them at different points in time. I’ve used Authorize.NET and CyberSource. I’m not here to plug any specific provider and to be honest, they’re all pretty competitive with each other. In fact, the two I just mentioned have now merged, but others also include SkipJack, Verisign, and PayPal.
All these merchant services providers offer what’s known as a Payment Gateway, which is the API used to process credit cards, and in some cases checks and wire transfers as well. However different these payment gateways are, there are certain things they all share in common and that’s what I’m going to concentrate on. Injecting the provider model into a feature involves the same thing I taught in a previous article when describing the Strategy Pattern. It involves identifying commonalities and turning them into coding abstractions. The reason for using a payment gateway is very simple; you need to process credit cards, and they all do that. By processing negative amounts you can issue credits back to a customer, so you can handle returns as well. Handling both of these situations will require some information which any payment gateway will use. In the interest of simplicity I’ll keep the list short so I’ll count on the customer’s name, address, credit card information, and of course a dollar amount.
Another couple of pieces of information that every payment gateway will need will be some kind of authentication credentials. The merchant providers or bank typically provide these to you when you sign up for the service and you need to provider them for each transaction you perform. Whether these credentials take the place of an account number and a pin number, or a user name and a password, they usually all consist of a login and a passcode of sorts. I mention these two items separate from the other items I listed earlier because there is a key difference between them. The customer’s name, credit card, and other pieces of information are all different depending on the customer. This means that every transaction will have its own values for these eventual fields because they belong to the customer; the site user. The login credentials will be consistent for all the transactions because it belongs to you; the site owner.
I think it’s safe to say that no matter what payment gateway you go with, they will share these common characteristics and requirements; so I have the essence of my provider. The steps I’ll take to create this provider-based feature will directly mimic the provider characteristics I laid out earlier. I’ll develop this feature in a real-world fashion, describing each step and its reasons along the way.
The Goal
I want to develop a provider-based credit card processor that can serve as a provider for one payment gateway today and another for a second payment gateway tomorrow. Two different sites can use the two different providers using exactly the same code.
The Design & the Abstraction
I know Microsoft gives me the ProviderBase class from which all provider abstraction classes inherit so I’ll start by creating an abstract class called PaymentProcessingProvider. This class will serve as the base from which all my payment processing providers will inherit. This class will define protected fields that will contain any information I want to read from the configuration. If you recall back to the membership configuration section, after the name and type attributes, there were several others that were specific to this provider type and that defined certain characteristics of a membership provider. A properly written membership provider can access the values of all those attributes so my payment processing providers should be able to access any configuration attributes I choose to use later. When I define my payment processing provider configuration later, I want to give it the information that will remain static no matter how the provider class gets used. This is why I described potential fields like customer’s name and credit card number separately from the login credentials. The merchant provider login credentials will remain static so I’ll provide them in my configuration section later, and thus I’ll define them in my PaymentProcessingProvider class as protected fields. I’ll use two fields: _Login and _Password, both as strings.
Now I need to define one or more methods that I want providers to implement later. These are the common methods from which I feel all payment gateways can benefit. To keep things simple, I will define two methods called ProcessPayment and ProcessReturn. The arguments for each of these methods will hold the per-customer information I described earlier. Remember, any payment gateway-specific providers I write later will inherit from this class and provide the payment gateway-specific implementation for each of these methods. The PaymentProcessingProvider class is listed in its entirety in Listing 2.
If you examine the code, you’ll see that both method definitions return a type called TransactionResult. I won’t put much emphasis on this class for the rest of this article because it’s not too important. Its presence merely implies that both processing a payment and processing a return would return some kind of resulting information, and I felt it was more real-world to provide a class that encompasses this information as opposed to a simple Boolean variable indicating success or failure. The TransactionResult class includes fields such as authorization code (in the event of a successful transaction) and error code (in the event of a failure). One thing I will say about the TransactionResult class is that its contents also consist of information I felt was going to be common to any payment gateway with which I interface later.
The Configuration
Now that I have the base for all my future providers, I’ll jump to the configuration where I’ll define my current provider. I’ll define the Payment Processing provider in the web.config file using a custom configuration section called <paymentProcessing> and in order for this to be accepted in my application, I will need to create a configuration section handler to define the section. Before I get to that, here’s what I want my configuration to look like.
<paymentProcessing defaultProvider="{name}">
<providers>
<add
name="{name}"
type="{class, assembly"
login="11223344" password="dotnet"/>
</providers>
</paymentProcessing>
If you compare this configuration information to the one in Listing 1, you’ll definitely see the similarity. In fact, the only difference is the parent tag name and the attributes following name and type. In the past, I would have to write a class that implements IConfigurationSectionHandler and design it to read the contents of this configuration section into my own object model, but the new object model in the System.Configuration namespace allows me to both design an object model that mimics the configuration section and map it to the configuration section itself all in one shot. Not only that, it allows much better code reuse for areas of configuration that are reusable. In this case, everything within my paymentProcessing parent tag is essentially the same as the membership tag in Listing 1. The new method of designing configuration-based object models also allows for dynamic attributes as is the case here where my login and password attribute are specific to this provider definition only and not another like the membership provider section. Because of these similarities in the two configuration sections, the .NET Framework contains classes that define the sections beneath my parent tag, so all I need to create is an object to identify my paymentProcessing section. Using the model introduced in .NET 2.0, a configuration section parent tag is identified by a class that inherits from System.Configuration.ConfigurationSection, and so I will create a class called PaymentProcessingSection that inherits from just this class.
Since my paymentProcessing configuration section has an attribute called defaultProvider and an inner element called providers, my PaymentProcessingSection class will need a property to identify each of these two. Now, without turning this into a full-featured tutorial on writing custom configuration sections, I will describe the two properties I’m about to create. The first one is easy as it will be a simple string property called DefaultProvider. This property will, of course, represent the defaultProvider attribute that’s part of my parent tag. What will connect this property to its XML attribute is an attribute decoration using the ConfigurationProperty attribute. The mandatory value sent into its constructor is the name of the configuration attribute this property will represent. Optional values include the default value for my configuration attribute and whether or not it is required.
The ASP.NET Provider Model is NOT just for ASP.NET, if you can just get past the psychological hurdle of referencing System.Web.DLL from your non-Web application.
In C#:
[StringValidator(MinLength=1)]
[ConfigurationProperty("defaultProvider",
DefaultValue="TestProvider")]
public string DefaultProvider
{
get
{
return (string)this["defaultProvider"];
}
set
{
this["defaultProvider"] = value;
}
}
In VB:
<StringValidator(MinLength:=1)> _
<ConfigurationProperty("defaultProvider",
DefaultValue:="TestProvider")> _
Public Property DefaultProvider() As String
Get
Return CType(MyBase.Item(
"defaultProvider"), String)
End Get
Set(ByVal value As String)
MyBase.Item("defaultProvider") = value
End Set
End Property
Other possible decorations for this property are validation attributes such as the StringValidator I am using here.
The fact that this is a value-type property and not an object property is what identifies it as a property representing a configuration attribute. To create a property that represents the providers element that is under my paymentProcessing element, I need a property that is of a type that inherits from either ConfigurationElement or ConfigurationElementCollection. The former indicates a configuration element and the latter indicates a collection of configuration elements of the same tag name, as is the case with the add tag used inside my providers element. Because everything inside and including the providers element shares a common pattern for all providers, .NET includes a class called ProviderSettingsCollection, which inherits from ConfigurationElementCollection. All I need is a property called Providers of type ProviderSettingsCollection and an attribute decoration to map it to my providers element.
In C#:
[ConfigurationProperty("providers")]
public ProviderSettingsCollection Providers
{
get
{
return (ProviderSettingsCollection)
this["providers"];
}
}
In VB:
<ConfigurationProperty("providers")> _
Public ReadOnly Property Providers() As _
ProviderSettingsCollection
Get
Return CType(MyBase.Item("providers"), _
ProviderSettingsCollection)
End Get
End Property
You don’t need a property setter because this property represents a new XML element, and only attribute properties are “settable”. I’ll write an article in the future where I’ll discuss the configuration object model in more detail. You can see the entire PaymentProcessingSection class in Listing 3.
Now in the web.config file I’ll add an entry to the configSections section that informs .NET of the existence of my new section and what class to use as the root of its object model.
<configSections>
<sectionGroup name="system.web">
<section name="paymentProcessing"
type="Acme.Web.Configuration.
PaymentProcessingSection,
ProviderFeature"/>
</sectionGroup>
</configSections>
I chose to define my paymentProcessing section as residing inside the system.web section, but this was my choice and not a requirement.
The Test Provider
Now that I have an abstract class from which to start, and a configuration class to determine the configuration information, I’ll write a test provider that implements the PaymentProcessingProvider base class. I’ll call this class TestPaymentProcessingProvider and will set the patterns and standard that I will follow when writing subsequent providers later.
To start with, my new class will inherit from PaymentProcessingProvider. If you recall, PaymentProcessingProvider defines two abstract methods called ProcessPayment and ProcessReturn, which I have to implement in my TestPaymentProcessingProvider class. This is a requirement of course, but what is not a requirement is overriding the Initialize method, which I am going to do as well. If I were to design a provider-based feature that does not count on any configuration information in the provider definition besides the name and type attributes, I would not need this method; but such is not the case here.
Because I designed my configuration to store the login and password used to sign on to a payment gateway and perform a transaction, I must override the Initialize method in my class. This method is defined all the way back in the ProviderBase class, which you recall serves as the base for PaymentProcessingProvider. The two arguments that come into this method are name and config. The name argument corresponds to the name attribute of the provider as defined in the configuration. The config argument is a NameValueCollection type which contains all other attributes in the configuration for that provider (excluding type).
I’ll do a few things in the Initialize method. First of all, I’m going to go through, one by one, the expected attributes by looking for them in the config collection argument. What I do with each as I find it is up to me. I’ll perform specific validation on each one and use the .NET ProviderException class to report validation problems I find, including reporting missing expected values. I’ll also widen the scope of each of the values in the collection so I can access them from the two method implementations later. If you remember back when I wrote the PaymentProcessingProvider abstract class, I defined protected variables that would hold configuration values later; this is that ”later”. The last thing I’m going to do is also optional but I feel is a good practice; and that is to check for configuration attributes that I am not expecting, or do not belong. I can chose to simply ignore these, but I want to provide as much enforcement as possible within this provider class. I make this check by eliminating all expected and valid attributes from the config argument. Then, if there is anything left, I know I have unexpected attributes in the configuration.
The rest of the information that this provider will need is given on a per-customer basis and was defined in the method definitions inside PaymentProcessingProvider earlier. I’m referring to the arguments of ProcessPayment and ProcessReturn. The implementation code in these two methods is what is going to be specific to the payment gateway with which I need to interface. This particular example is a test provider so I’m simply going to create a dummy TransactionResult object and return it in each method. You can see the complete code for TestPaymentProcessingProvider in Listing 4.
So now that I have a test provider written and a configuration in which to define it, I’m going to do just that and describe the instantiation process once again but in the context of the code I’ve just written.
Instantiating the Provider
Following the configuration pattern I explained earlier, defining my test provider would like this:
<paymentProcessing defaultProvider="TestProvider">
<providers>
<add
name="TestProvider"
type=
"Acme.Providers.
TestPaymentProcessingProvider,
ProviderFeature"
login="11223344" password="dotnet"/>
</providers>
</paymentProcessing>
The act of instantiating this provider would involve reading into this configuration section, which I know I can do with the PaymentProcessingSection class, grab the value of the defaultProvider attribute and use it to access that provider in the Providers collection (remember Providers is a property in my configuration class). Once I’ve determined which <add> item in the configuration corresponds to the provider I want, I use the type attribute to instantiate that class. The act of instantiating a class when given its class notation in a string involves using the Activator class like this:
Activator.CreateInstance(
Type.GetType("class, assembly"))
This seems like a lot of work and also work I don’t want to do each and every time I use my provider. For this reason, my final step in completing my provider model is to write the factory class that will perform all this for me. This class will be analogous to the Membership static class I showed you earlier and will be called PaymentProcessing.
The Factory Class
The PaymentProcessing class will take care of all the plumbing code that reads into my configuration information, grab the provider list, identify the default provider, instantiate it, and use the appropriate method. All this will take place by simply accessing the PaymentProcessing class and calling one of its methods. Like its counterpart, the Membership class, the PaymentProcessing class will have static methods that mimic those that I defined in my provider base class, the PaymentProcessingProvider class. From a usage perspective, if I want to process a credit card payment, this is all I should have to do:
PaymentProcessing.ProcessPayment(
credit card, exp month, exp year, amount)
When I write another provider later and install it in the configuration, the code statement above will NOT change whatsoever-and that is the whole point of everything I’ve done so far.
I’ll take the code statement above apart and show you how each step works. The first time I access the PaymentProcessing static class, I’ll hit the static constructor-no different than instantiating a class and hitting the instance constructor. The static constructor will read from the configuration file and set me up for using the correct provider; and here is where the process will differ a little from a conventional strategy pattern, such as the one I described in a previous article (see sidebar).
A provider-based feature modeled by the ASP.NET Provider Model is expected to have in-memory, instances of all the providers listed in its corresponding configuration section, and the default provider directly accessible in its own variable; the others, of course, go into some sort of collection. The reason for this is so you can request any one of the installed providers, regardless of the one identified as the default provider.
To read the configuration section I use conventional code which looks like this:
In C#:
PaymentProcessingSection section =
(PaymentProcessingSection)
WebConfigurationManager.GetSection(
"system.web/paymentProcessing");
In VB:
Dim section As PaymentProcessingSection = _
CType(WebConfigurationManager.GetSection( _
"system.web/paymentProcessing"), _
PaymentProcessingSection)
The resulting variable, section, contains the object model that corresponds to my configuration section. From this point on, you have a lot of help from a .NET class called ProvidersHelper. Before I talk about this class, let me first follow up on my original statement differentiating the ASP.NET Provider Model from a convention Strategy Pattern. My PaymentProcessing class will have two properties, one called Provider, of type PaymentProcessingProvider. This property will represent the default provider. The other is called Providers and will be of type ProviderCollection. The ProviderCollection class is a .NET class which will contain instances of all the providers defined in my configuration section. Both these properties will expose a member variable of the same type and will be read-only, since I do not want the outside world to change them. So with that out of the way, I’ll get back the ProvidersHelper class.
As I mentioned earlier, part of the job of the PaymentProcessing static class is to read the configuration file, instantiate the providers installed, and make the default provider available to the method I call. The ProvidersHelper class does all of this work for me. The InstantiateProviders method in this class receives as arguments my ProviderCollection-type property and the Providers property from my configuration section, which if you recall I read in earlier.
In C#:
ProvidersHelper.InstantiateProviders(
section.Providers, _Providers,
typeof(PaymentProcessingProvider));
In VB:
ProvidersHelper.InstantiateProviders( _
section.Providers, _Providers, _
GetType(PaymentProcessingProvider))
Once this is executed, the _Providers variable contains instances of all the providers defined in my configuration section. The _Providers variable is the member variable behind the Providers static property, which is of type ProviderCollection.
The rest of the initialization I need to perform from my static constructor is to check the DefaultProvider property of my configuration section object and if not empty, set the _Provider variable to the item in the _Providers collection, keyed by the DefaultProvider value.
In C#:
_Provider = (PaymentProcessingProvider)
_Providers[section.DefaultProvider];
In VB:
_Provider = DirectCast(_Providers.Item( _
section.DefaultProvider), _
PaymentProcessingProvider)
The _Provider variable is the member variable of type PaymentProcessingProvider (my provider base class) that is exposed by the Provider property.
At this point, the Provider property of my static class exposes the current default provider casted to what it knows as the point of commonality, or abstraction; PaymentProcessingProvider. The Providers property exposes all the providers listed in the configuration section in case I wanted to directly access any of them (see sidebar).
Once all of this initialization takes place, the method I called when I originally accessed the PaymentProcessing class is executed. My code snippet showed a call to ProcessPayment and all the code in that method will do is make a call to the ProcessPayment method of the Providers property, which holds the instance to the current default provider. You can see the complete code for the PaymentProcessing factory class in Listing 5.
All the code I’ve written so far resides in one assembly, and this assembly will be referenced by any additional payment processing provider(s) I write.
Writing Other Providers
Now that I have my provider-based feature designed and written, I can develop additional providers very simply. Without actually developing the payment-gateway-interface details, I will demonstrate how I would write a provider to interact with the CyberSource Payment Gateway.
To do this, I’ll create a new project and reference the assembly containing the base class, which in this case is the assembly that contains all the code I’ve written so far. I’ll call my new provider class CyberSourcePaymentProcessingProvider and will start by inheriting from PaymentProcessingProvider. The process from here on is 100% identical to the one I followed in the TestPaymentProcessingProvider class so I won’t repeat it. The only definite difference would be the implementation of the two methods. The code in the ProcessPayment and ProcessReturn methods is what will directly communicate with the CyberSource payment gateway API. How it does this is going to very specific to that API and beyond the scope of this article. I will say that some payment gateway APIs involve referencing a .NET component that they provide while others are completely service-oriented and offer either a Web service or in the specific case of CyberSource, a WCF service with which to interface. Every payment gateway is different and it is up to you to get familiar with the one you chose.
Now, I just used the words “definite difference” because there is also a “possible difference” in the way I would code this provider as opposed to the test provider earlier. This difference lies in the Initialize method. Remember, the goal of the code in this method is to read the config argument and set the variables I defined in the provider’s base class. If you find that the number and name of possible configuration attributes will differ from provider to provider (for this same feature of course), then the Initialize method will check for and act upon more or less config values. If you anticipate for your provider-based feature that this will never be the case, then you can certainly place the Initialize method override and all its code in the provider base class itself-PaymentProcessingProvider in my case. Then the actual provider classes will have nothing to do but provide the implementation for all the action methods.
Once the new provider is done, I need to compile it and make the assembly available to whatever client will be using it. What I mean by this is that referencing the assembly is not necessary. In fact, one of the ideas here is that you add a provider to your system, install it in the configuration file, and possibly set it as the default provider, all without having to recompile or redeploy your application. Since the provider classes are being instantiated in a late-bound fashion, they simply need to be within scope of your application. However, your application does need to reference the core assembly-the one with the provider base class, the configuration class, and the static factory class.
Accessing Any Provider
Since the static factory class prepares all the configured providers for use, regardless if they are the default provider or not, you can access any of them directly. Say you configured three providers in your configuration called TestProvider, CyberSource, and AuthorizeNet. You may have the defaultProvider attribute set to CyberSource but all of them are available in the Providers property of the PaymentProcessing factory class.
The client application may not, and should not, know of the existence of the concrete provider classes, but it does know and has access to the base class from which they all inherit. Therefore, to access the AuthorizeNet provider directly, you can do this:
In C#:
PaymentProcessingProvider provider =
PaymentProcessing.Providers["AuthorizeNet"] as
PaymentProcessingProvider;
In VB:
Dim provider As PaymentProcessingProvider = _
TryCast(PaymentProcessing.Providers. _
Item("AuthorizeNet"), PaymentProcessingProvider)
Now you can use the provider variable to call the ProcessPayment and ProcessReturn respectively.
This is the technique that the ASP.NET Security Controls use internally when you set their MembershipProvider property to target a specific provider; otherwise they use whatever is the default provider.
Non-ASP.NET
During the course of this article, I’ve been referring to this model by what it is known as, the ASP.NET Provider Model. The truth is that the ASP.NET Provider Model is not restricted to ASP.NET applications only. The story that I heard is that it was developed by members of the ASP.NET team and it is a fact that it is used throughout the ASP.NET framework, but it can certainly be used from any other type of client. The other client must reference System.Web.dll, which is where all the code resides. In my humble opinion, placing all the required classes for this model in this assembly was a mistake. It’s an important enough model where it merits its own assembly. Unfortunately, I’ve encountered many developers that refuse to reference System.Web.dll from a Windows Forms application or from a business layer. Those developers say it’s a UI DLL and does not belong in a business layer, or that it’s a web DLL and does not belong in a Windows Forms application. I look at it this way: it’s a framework DLL and I treat it just like I do System.DLL; the model is too powerful to give up on it if you’re not using ASP.NET-so endeth the sermon.
Conclusion
I’ve shown you a very specific implementation of the Strategy Design Pattern known as the ASP.NET Provider Model. Used correctly, this model can add some great extensibility to your applications. It can also allow you to design applications if you’re uncertain on some of the architectural decisions right away. You can design some features of your application to be provider-based features, and then write a provider that accesses and acts upon data in an immediate object-oriented fashion, and later write another provider that acts as a proxy to a service somewhere. You are limited by your own needs and creativity, but like every other architectural, design, and development technique, learn it and use it as a tool in your arsenal. If you read my extensibility article in this magazine a few months back, (Design for Extensibility, Jan/Feb 2008) you’ve heard me say this exact same thing again. One thing is for certain. Swappable providers are a great way to account for a customer that “changes their mind” later; but we all know that never, ever happens… right?