The Test Provider
With an abstract class in place, and a configuration class to read the configuration information, you can write a test provider called TestPaymentProcessingProvider that implements the PaymentProcessingProvider base class. This class will set the patterns and standard for subsequent providers.
The new class inherits from PaymentProcessingProvider, which defines two abstract methods called ProcessPayment
, which the TestPaymentProcessingProvider class must implement.
In addition the class overrides the Initialize
method. If you were to design a provider-based feature that does not rely on any configuration information in the provider definition besides the name
attributes, you wouldn't need this method. In this case, though, because the configuration stores the login and password used to sign on to a payment gateway and perform a transaction, you must override the Initialize
method defined in the ProviderBase class, which serves as the base for PaymentProcessingProvider. The Initialize method arguments are name
. The name
argument corresponds to the name
attribute of the provider as defined in the configuration, while the config
argument is a NameValueCollection type that contains all other attributes in the configuration for that provider (except type
Here, the Initialize
method performs several tasks. First, it loops through, the expected attributes by looking for them in the config
collection argument and performs specific validation on each. It uses the .NET ProviderException class to report validation problems, including missing expected values. It also widens the scope of each value in the collection so you can access them from the two method implementations later. Remember, the PaymentProcessingProvider abstract class defines protected variables that hold configuration values.
Finally, the Initialize method checks for unexpected configuration attributes. This is not required, but is good practice. You can opt to simply ignore such values if you like. Here, the code first eliminates all expected and valid attributes from the config
argument. Then, if the collection contains anything else, those values are due to unexpected attributes in the configuration.
The rest of the information that this provider needs is customer-specific: the arguments of ProcessPayment
. These two method implementations are specific to each payment gateway. Because this particular provider is simply a test, I'm going to create a dummy TransactionResult object and return it from each method. See Listing 6
(C#) and Listing 7
(VB) for the complete code.
At this point, you have a test provider written and a custom configuration section in which to define it. Here's a more complete explanation of the instantiation process in the context of the code I've just written.
Instantiating the Provider
Using the configuration pattern explained earlier, you can define the test provider like this:
Instantiating this provider causes the PaymentProcessingSection class to read this configuration section. That grabs the value of the defaultProvider
attribute and uses it to access that provider in the Providers
collection (remember, Providers
is a property in the configuration class). After determining which <add>
item in the configuration corresponds to the provider I want, the code uses the type
attribute to instantiate that class, using the Activator class as shown below:
That seems like a lot of work. It's also work I don't want to do each and every time I use my provider. For this reason, the final step to complete the provider model is to write a factory class that will perform all this work for me. This class will be analogous to the Membership static class I showed you earlier, but will be called PaymentProcessing.
The Factory Class
The PaymentProcessing class will take care of all the plumbing code that reads the configuration information, grabs the provider list, identifies the default provider, instantiates it, and calls the appropriate method. All this will take place whenever you access the PaymentProcessing class and call one of its methods. Like its counterpart, the Membership class, the PaymentProcessing class will have static methods that mimic those defined in the provider base class, the PaymentProcessingProvider class. From a usage perspective, when you want to process a credit card payment, you should have to write only code such as this:
credit card, exp month, exp year, amount)
If you later write another provider and install it in the configuration, the preceding code would NOT change whatsoever—and that's the whole point of all the work you've done so far.
I'll take the code statement above apart to show you how each step works. The first time you access the PaymentProcessing static class, you'll hit the static constructor. The constructor will read the configuration and set up the correct provider. Here's where the process differs slightly from a conventional strategy pattern, such as the one I described in a previous article (see the sidebar "Design for Extensibility
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
must be directly accessible in its own variable; the others, of course, go into some sort of collection. This is so you can request any one of the installed providers, regardless of which one is identified as the default provider.
Reading the configuration section uses conventional code like this:
// In C#:
PaymentProcessingSection section =
' In VB:
Dim section As PaymentProcessingSection = _
The resulting section
variable contains the object model that corresponds to the configuration section. From this point on, you have a lot of help from a .NET class called ProvidersHelper. Before discussing that class, I want to follow up on differentiating the ASP.NET Provider Model from a conventional Strategy Pattern.
The PaymentProcessing class will have two properties, one called Provider
, of type PaymentProcessingProvider, which represents the default provider. The other is called Providers
and is of type ProviderCollection. ProviderCollection class is a .NET class that 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, because the outside world should not be able to change them. With that out of the way, let's get back to the ProvidersHelper class.
As mentioned earlier, part of the PaymentProcessing static class's job is to read the configuration file, instantiate the providers installed, and make the default provider available to whichever method I call. The ProvidersHelper class does all of this work for me. The InstantiateProviders
method in this class receives two arguments: a ProviderCollection type, and the Providers
property from the custom configuration section.
' In VB:
section.Providers, _Providers, _
After this code executes, the _Providers
variable contains instances of all the providers defined in the configuration section. The _Providers
variable is the member variable behind the Providers
static property, which is of type ProviderCollection.
The rest of the initialization involves checking the DefaultProvider
property of my configuration section object and if it's not empty, setting the _Provider
variable to the item in the _Providers
collection, specified by the DefaultProvider value.
// In C#:
_Provider = (PaymentProcessingProvider)
' In VB:
_Provider = DirectCast(_Providers.Item( _
variable is the member variable of type PaymentProcessingProvider (the provider base class) exposed by the Provider
At this point, the Provider
property of the static class exposes the current default provider cast to the abstract type PaymentProcessingProvider. The Providers
property exposes all the providers listed in the configuration section if you wanted to access any of them directly.
After all this initialization complets, the method called when you originally accessed the PaymentProcessing class gets executed—in this case, ProcessPayment. All the code in that method does is make a call to the ProcessPayment
method of the Providers property, which holds the current default provider instance. Listing 8
(C#) and Listing 9
(VB) show the complete code for the PaymentProcessing factory class.
All the code written so far resides in one assembly that will be referenced by any additional payment processing provider(s) I might write.