All ASP.NET providers share some characteristics:
: a static class that serves as the client's gateway into the feature's functionality. The class requires no instantiation; it contains a set of static methods that provide this feature's functionality. The code in each of these methods simply calls a corresponding method in an instantiated provider class.
Feature-specific base class: In the previous example, this is the MembershipProvider class—an abstract class that defines the abstract methods that provides this feature's functionality. Typically, the methods of the factory class mimic the methods defined in this base class.
Configuration section: Because 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 stated earlier, there's a pattern for these configuration sections; they vary only in the parent tag name and the extra attributes that define a provider, with the exception of name and type. To write a provider-based feature (as you'll see later), you need a custom configuration section, which in turn means you need configuration classes to read the custom section. Thanks to the new model for reading configuration files that Microsoft introduced back with .NET 2.0, you don't need to write much code to do that. Because the model is already 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 a future article.
Provider implementation class: In the previous example, the SqlMembershipProvider class fulfills this role. This is the class that inherits from the feature-specific base class (MembershipProvider). All the specific implementation goes here.
ProviderBase: One 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 mentioned earlier has a base class that defines its methods and all these base classes have their start in the ProviderBase class. The commonality of 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.
Now that you've taken the membership provider apart and identified its characteristics and behavior, you can apply all this to a custom feature, completely outside the scope of membership, roles, or security.
Build A Provider-Based Feature
You can apply the Provider pattern you've just seen to just about anything. This example uses it for a feature that's familiar to most developers who have been involved in e-commerce credit card processing. There are third-party APIs for this, including 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 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 sometimes checks and wire transfers as well. Despite their differences these payment gateways all share certain features. Injecting the provider model into a feature involves the same thing discussed 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 all the third-party APIs 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 requires some information that any payment gateway will use. In the interest of simplicity I'll keep the list short so you can assume that the common factors include the customer's name, address, credit card information, and of course a dollar amount.
Every payment gateway will also require some kind of authentication credentials. The merchant providers or bank typically assign these to you when you sign up for the service. You then provide your assigned authentication information for each transaction you perform. Whether these credentials take the form 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.
|Author's Note: I've mentioned these two items separately from the other items because there is a key difference between them. The customer's name, credit card, and other pieces of information all differ depending on the customer. That means every transaction will have its own values for these fields, because they belong to the customer. In contrast, the login credentials are consistent for all transactions because they belong to you.
The steps you'll see that create this provider-based feature will directly mimic the provider characteristics shown earlier. I'll develop this feature in a real-world fashion, describing each step and its reasons along the way.
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 should be able to use two different providers from exactly the same code.
The Design & the Abstraction
Microsoft supplies the ProviderBase class from which all provider abstraction classes inherit, start by creating an abstract class called PaymentProcessingProvider. This class will serve as the base from which all the payment processing providers will inherit. It defines protected fields that will contain any information I want to read from the configuration. (These are the fields that appear after the name and type attributes in Listing 1
). A properly written membership provider can access the values of all those attributes, so the payment processing providers should be able to access any configuration attributes you choose to use later. When defining the payment processing provider configuration later, you want to give it the information that will remain static no matter how the provider class gets used. That's why I described fields such as the 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 the configuration section later, and therefore must define them in the PaymentProcessingProvider class as protected fields. I'll use two fields: _Login
, both of type String.
Next, you need to define one or more methods that you want providers to implement later. These are common methods from which all payment gateways will benefit. To keep things simple, I'll define two methods called ProcessPayment
. The arguments for each method will hold the customer-specific information. Remember, any payment gateway-specific providers written later will inherit from this class and provide the specific implementation for each of these methods. Listing 2
(C#) and Listing 3
(VB) show the entire PaymentProcessingProvider class code.
In Listing 2
and Listing 3
, note that both method definitions return a type called TransactionResult. I won't put much emphasis on this class here, because it's not terribly important. It merely implies that processing either a payment or a return would return specific information encompassed by this class rather than a simple Boolean variable indicating success or failure. The TransactionResult class includes fields such as an authorization code (for successful transactions) and an error code (for failures). Basically, the TransactionResult class should any information common to any payment gateway that you might interface with later.
Now that you have the base for all future providers, you can work on the configuration, where you'll define the current provider. The Payment Processing provider in the web.config
uses a custom configuration section called <paymentProcessing>
. To access this from the application, you need to create a configuration section handler that defines the section. Before getting to that, here's what the configuration should look like:
If you compare this custom configuration to that in Listing 1
, you'll definitely see the similarity. In fact, the only difference is the parent tag name and the attributes following the name
. In former .NET versions, you would have had to write a class to implements IConfigurationSectionHandler that could read the contents of this configuration section into your own object model, but the new object model in the System.Configuration namespace lets you both design an object model that mimics the configuration section and map it to the configuration section all in one shot. Not only that, it allows much better code reuse for areas of configuration that are reusable.
|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.|
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 others, such as the membership provider section. Because of thes similarities in the various configuration sections, the .NET Framework contains classes that define the sections beneath my parent tag, so all you need to create is an object to identify the 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. I'll create a class called PaymentProcessingSection that inherits from that class.
Because the paymentProcessing
configuration section has an attribute called defaultProvider
and an inner element called providers, the PaymentProcessingSection class will need a property for each. The first one is easy, because it will be a simple string property called DefaultProvider
. This property will, of course, represent the defaultProvider
attribute that's part of the parent tag. A ConfigurationProperty
attribute decoration serves to connect this property to its XML attribute. The mandatory value sent into its constructor is the name of the configuration attribute this property represents. Optional values include the default value for my configuration attribute and whether or not it is required.
// In C#:
public string DefaultProvider
this["defaultProvider"] = value;
' In VB:
Public Property DefaultProvider() As String
Set(ByVal value As String)
MyBase.Item("defaultProvider") = value
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 identifies it as a property that represents a configuration attribute. To create a property that represents the providers
element under the paymentProcessing
element, you'll need a property whose type 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—including the providers element—shares a common pattern for all providers, .NET includes a class called ProviderSettingsCollection, which inherits from ConfigurationElementCollection. All you need is a property called Providers
of type ProviderSettingsCollection, and an attribute decoration to map it to the providers
// In C#:
public ProviderSettingsCollection Providers
' In VB:
Public ReadOnly Property Providers() As _
Return CType(MyBase.Item("providers"), _
You don't need a property setter because this property represents a new XML element, and only attribute properties are "settable". A future article will discuss the configuration object model in more detail. You can see the entire PaymentProcessingSection class in Listing 4
(C#) and Listing 5
Now, in the web.config
file, add an entry to the configSections
section that informs .NET of the existence of the new section and what class to use as the root of its object model.
|Author's Note: I chose to define the paymentProcessing section as residing inside the system.web section, but that was a choice, not a requirement.