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

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


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

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



Comment and Contribute

 

 

 

 

 


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

 

 

Sitemap