devxlogo

Develop Provider-Based Features for Your Applications

Develop Provider-Based Features for Your Applications

he ASP.NET Provider Model drives many features within the ASP.NET architecture, yet most developers associate it only with security or membership-related functionality. This article takes you deep into the provider model, so you can see how it’s used in a security context. Then it shows you how you to 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 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, others, such as security, were new to the platform. The important aspect is that each of these areas of functionality benefited from something that I find developers often take for granted?the ability to extend and swap functionality when the situation calls for it.

What Is the Provider Model?

In a previous CoDe Magazine article, I wrote about extensibility and how to design applications that you can easily modify and enhance, using plug-in or swappable components. The article described the Strategy Pattern, which allows you to define an abstraction for information going in or out of a component, and then instantiate one of possibly many concrete implementations of that abstraction. In fact, the article showed how to perform such “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 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 only to access data from SQL Server databases. If you needed to access a Sybase server using OLE DB technology for example, you would use the OleDbConnection, OleDbCommand, and OleDbDataReader classes instead.

The Strategy Pattern lets you define an abstraction for information going into or out of a component, and then instantiate one of possibly many concrete implementations.

In either connection object type, 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. That commonality occurs because some interfaces in the System.Data namespace set the framework for these classes. Both the SqlConnection and OleDbConnection classes implement the IDbConnection interface?and that interface defines the ConnectionString property, the Open method, and the CreateCommand method.

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 see later, 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 refer to the concrete classes only after 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, the preceding code uses 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.

Providers make great wrappers for WCF Proxy Classes because they let you hide all your WCF code from the client developer.

While that’s a great programming style, and certainly one that I encourage, the fact that you still have to change code to swap Providers is 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, I’ll shift over to ASP.NET membership.

ASP.NET Membership and Roles

ASP.NET Membership Services is a tremendous time-saver. Microsoft has provided developers (no pun intended) with a great way to set up authentication and authorization quickly. You start by automatically setting up the database using the provided aspnet_regsql utility, and then simply use the Membership and Roles static classes to perform your functionality. For example, to validate a user, given a 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, including user creation, role checking, etc. The question (and really the heart of this article) is: How does this command know how to validate users against your database? And that, my friends, is the beauty of the provider model.

When you call the Membership static class methods, you’re actually performing a check to see which provider is currently installed for this particular feature (in this case, membership). You couldn’t use the ValidateUser statement shown above without placing a section in your web.config file (see Listing 1). This configuration section tells the Membership class which specific membership provider to use. A membership provider needs to adhere to a formal contract, which in this case is solidified by inheriting from a class called MembershipProvider. It is the MembershipProvider class that defines methods such as 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 actually delegate 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 here’s the call to ValidateUser:

   Membership.ValidateUser("miguel", "dotnet");

In the preceding code, Membership is the static factory class. The class has a static constructor. Its job is to access the configuration section shown in Listing 1 to determine the currently-installed provider. Here’s a more detailed examination of the contents of that configuration section 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.

   

The linkage is made using the name attribute of the add element. The type attribute specifies which provider to use, specified in standard .NET type notation?the fully qualified class and the assembly (without the .dll extension), separated by a comma. Listing 1 shows a remove element first, followed by an add element: Both refer to the same name. This is because the machine level config file 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 for this article, I wanted to give you as much detailed information as possible.

The type being installed here is a class called SqlMembershipProvider that inherits from the MembershipProvider base class. The class’s code implements all the methods defined in MembershipProvider, and the implementation is specifically designed to access the tables and stored procedures installed by 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.” (See the sidebar “Config Files” for more background on .NET’s configuration hierarchy.) I chose to “remove” this name and “add” it again so I can provide different values for 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 also starts with a defaultProvider attribute and contains a providers section, which adds providers using one or more add elements. Within the add element, the only consistent attributes are name and type. All other attributes are specific to the feature in question.

So let’s get back to the Membership class. The call to ValidateUser causes the static constructor in Membership to look and see which implementation of MembershipProvider to use. After making that determination, it instantiates the appropriate provider class (SqlMembershipProvider in this case) and stores it 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 that’s 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. This is possible because the instantiated provider?was cast to MembershipProvider and then stored in the private variable, and because MembershipProvider defines the ValidateUser method.

When the call executes, the implementation of ValidateUser that gets executed is the code in the SqlMembershipProvider class defined in the web.config and 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. All these ASP.NET features share similar characteristics.

Editor’s Note: This article was first published in the March/April 2009 issue of CoDe Magazine, and is reprinted here by permission.

Provider Characteristics

All ASP.NET providers share some characteristics:

  • Factory class: 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.

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 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 and _Password, 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 and ProcessReturn. 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.

The Configuration

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 . 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 and type. 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#:   [StringValidator(MinLength=1)]   [ConfigurationProperty("defaultProvider",     DefaultValue="TestProvider")]   public string DefaultProvider   {      get      {         return (string)this["defaultProvider"];      }      set      {         this["defaultProvider"] = value;      }   }      ' In VB:    _       _   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 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 element.

   // In C#:   [ConfigurationProperty("providers")]   public ProviderSettingsCollection Providers   {      get      {         return (ProviderSettingsCollection)         this["providers"];      }   }   ' In VB:    _   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”. 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 (VB).

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.

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:

                       

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 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.

Writing Other Providers

With the provider-based feature designed and written, you can develop additional providers very easily. Without actually developing any payment-gateway-interface details, here’s how you would write a provider to interact with the CyberSource Payment Gateway.

First, create a new project and reference the assembly containing the base class, which in this case is the assembly that contains all the code written so far. Call this new provider class CyberSourcePaymentProcessingProvider. The class code begins by inheriting from PaymentProcessingProvider. The process from here on is identical to the TestPaymentProcessingProvider class so I won’t repeat it. The only definite difference would be in the implementation of the two ProcessPayment and ProcessReturn methods. These methods communicate directly with the CyberSource payment gateway API. How CyberSource implements this functionality is specific to that API and beyond the scope of this article. 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 a WCF service with which you can interface. Every payment gateway is different; it’s up to you familiarize yourself with whichever one you choose.

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 defined in the provider’s base class. If you find that the number and name of possible configuration attributes differs from provider to provider (for this same feature of course), then the Initialize method will check for and act upon more or fewer config values. If you anticipate that this will never be the case for your provider-based feature, then you can place the Initialize method override and all its code in the provider base class itself?PaymentProcessingProvider in this example. Then the actual provider classes need do nothing but provide implementations of all the action methods.

After completing the new provider, compile it and make the assembly available to whatever client will be using it. Referencing the assembly is not necessary. In fact, one of the ideas here is that you can add a provider to your system, install it in the configuration file, and possibly even set it as the default provider?all without having to recompile or redeploy your application. Because provider classes are instantiated in a late-bound fashion (see the sidebar “Late Bound Instantiation“), they simply need to be within the 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

Because the static factory class prepares all the configured providers for use, whether they’re the default provider or not, you can access any of them directly. For example, suppose you have configured three providers in your configuration, called TestProvider, CyberSource, and AuthorizeNet. You may have the defaultProvider attribute set to CyberSource but all the providers are available through 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 non-default 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 methods 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 referred to this model by its common name: the ASP.NET Provider Model. But 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 while it’s used throughout the ASP.NET framework, it can certainly be used from any other type of .NET client as well. That 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 to merit 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 just because you’re not using ASP.NET.

In this article, you’ve seen a very specific real-world 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 help you design applications when you’re uncertain about some of the architectural decisions. You can design some features of your application to be provider-based features, and then write a provider that accesses and acts upon that data in an immediate object-oriented fashion, and later write another provider that acts as a proxy to a service somewhere. You are limited only by your own needs and creativity, but like every other architectural, design, and development technique, learn and use this as just one tool in your arsenal. One thing is for certain. Swappable providers are a great way to accommodate customers who “change their minds” (even though we all know that never, ever happens?right?).

devxblackblue

About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist