Login | Register   
Twitter
RSS Feed
Download our iPhone app
TODAY'S HEADLINES  |   ARTICLE ARCHIVE  |   FORUMS  |   TIP BANK
Browse DevX
Sign up for e-mail newsletters from DevX


advertisement
 

Develop Provider-Based Features for Your Applications : Page 3

The ASP.NET Provider Model is a powerful feature that extends far beyond ASP.NET.


advertisement

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 and ProcessReturn, 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 and type 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 and config. 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 and ProcessReturn. 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:

<paymentProcessing defaultProvider="TestProvider"> <providers> <add name="TestProvider" type="Acme.Providers.TestPaymentProcessingProvider, ProviderFeature" login="11223344" password="dotnet"/> </providers> </paymentProcessing>

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:

Activator.CreateInstance( Type.GetType("class, assembly"))

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:



PaymentProcessing.ProcessPayment( 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 = (PaymentProcessingSection) WebConfigurationManager.GetSection( "system.web/paymentProcessing"); ' In VB: Dim section As PaymentProcessingSection = _ CType(WebConfigurationManager.GetSection( _ "system.web/paymentProcessing"), _ 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 C#: ProvidersHelper.InstantiateProviders( section.Providers, _Providers, typeof(PaymentProcessingProvider)); ' In VB: ProvidersHelper.InstantiateProviders( _ section.Providers, _Providers, _ GetType(PaymentProcessingProvider))

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) _Providers[section.DefaultProvider]; ' In VB: _Provider = DirectCast(_Providers.Item( _ section.DefaultProvider), _ PaymentProcessingProvider)

The _Provider variable is the member variable of type PaymentProcessingProvider (the provider base class) exposed by the Provider property.

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.



Comment and Contribute

 

 

 

 

 


(Maximum characters: 1200). You have 1200 characters left.

 

 

Sitemap