Related books
. This column has been quiet for some time-sorry about that I have a really, really good excuse, though: We've been busy working on the next version of the Microsoft® .NET Framework and Microsoft® Visual Studio® .NET, Code-named "whidbey."
Busy is actually an understatement. For Microsoft® ASP.NET, we've roughly doubled the number of class libraries! Is that a good thing? Yes it is, and in the coming months we'll explore some of these new ASP.NET Features. However, Before Focusing All of Our Attertion Towards Whidbey, We'll Spend The First Several Articles ON ASP.NET 1.1 and Relate It to what's to come in whidbey.
Our First Article IS About The New Whidbey Provider-Model Design Pattern and how you can start using it in your asp.net 1.1 Applications Today.
There has been a lot of buzz recently surrounding the announcement of the next version of Microsoft® Visual Studio® .NET and the Microsoft® .NET Framework code-named "Whidbey." No, this is not another Whidbey article, but it is An Article That Discusses An Important New Design Pattern That Is Going To Show Up Whidbey, And More Importantly, a Pattern You CAN Start Using In your Applications Today.
In the next article, we'll look at implementing this pattern in an ASP.NET 1.1 application. In this article, we'll discuss why the pattern is needed, and walk through the "Whidbey" provider specification. Let's look at some of .
Fixed Choices
In published APIs you are usually limited to a fixed number of options. An example I commonly use is the Microsoft® ASP.NET Session State feature. ASP.NET Session State supports an "out-of-process" mode. Using either Microsoft® SQL Server or the ASP.NET State Server, you can store user's Session data in a process separate from the running application There are many benefits:.. reliability, easy Web farm support, and scalability Most people tend to lean towards the database solution, Since Itfits for Replication and Other Benefits Not Offered By The State Server. ONE SMALL PROBLEM: What if you're not a SQL Server Customer? fixed Internal Behaviors
Here's another common situation, again using Session State. Say you want to change some internal behaviors of ASP.NET APIs, for example changing how ASP.NET Session sets or retrieves values from its out-of-process store. Today you're constrained By The Design. It is all or nothing: Either We Fetch All Your Data, or We Fetch None of Your Data. If You're Storing 5 MB of Data And Only NEED 50 KB TO Satisfy The Current Request ... you get Picture.
Fixed Data Schema
The final scenario relevant to this discussion concerns schema. A schema is the logical model of how data is stored. For example, if you ever used Microsoft® Site Server 3.0 Personalization, it forced you to move your data into the schema that it desired. that's all well and good, but typically the people who built that schema do not know a whole lot about your business. In other words, fixed schemas are typically built to be really good for everyone, but excellent for no one.
THESE ARE Common Problems That All Technologies Share: The Inability To Change Core Behavior or FunctionAry Of Published ApisWho Cares, I'll Write My OWN
You definitely can create your own solutions for all these problems, but there are a lot of benefits to using applications with published APIs. One of the main benefits is that many developers know and understand what these APIs are and how they work. If you need to augment your staff, for instance, you can hire a consultant that is already familiar with these well-documented APIs If you write your own, guess what;. you also get to pay that person to learn your proprietary system.
What we really want is a way for everyone to get the benefits of well-documented and familiar programming interfaces, such as Session State, but the ability to completely control the internal Business Logic Layer (BLL) and Data Access Layer (DAL) of these API.
Introducing the Provider Design Pattern
The Provider Design Pattern is a pattern we've used in several of our ASP.NET Starter Kits, formalized in ASP.NET Whidbey, and one that we'll hope you'll start using for your applications, too. The theory of the pattern is that it allows us to define a well-documented, easy-to-understand API, such as our new Whidbey Membership APIs, but at the same time give developers complete control over the internals of what occurs when those APIs are called.
The pattern was officially named in the summer of 2002 when we were designing the new Personalization feature of ASP.NET Whidbey. We were struggling to design the "right" schema that worked for everyone-the Personalization feature of ASP.NET Whidbey adds a new Profile class to which you can add strongly typed properties, for example, FirstName, whose values are stored in a database. The problem was that we needed a flexible design, but at the same time we wanted it to be easily extensible without everyone having to be a DBA; turns out this simple requirement is an incredibly challenging problem.On the one hand, we could have designed the system to be entirely schema driven This would have meant that we would have designed what I call, for lack of a better. name, vertical tables in the database. Rather than using traditional columns, there would be a set of tables used to define the schema and a data table used to map data to a particular type. Each new item in the data table would corr Espond to a type in the schema table identifying what type of material it tas. Below is a Very Simplistic Data Model of this Design:
Figure 1. a Simple Model for Personalization
Using this Data Model, We Well See Something Like The Data Below In The Schema Table:
Figure 2. Proposed Schema Table
And Data in The PersonalizationData Table Similar To The Data Below:
Figure 3. PersonalizationData Table
As You Can See, this Model Maps The Property Type (Such As Firstname) To a property value in the PersonalizationData Table, Such as firstname: Rob.
While being infinitely flexible, this model starts breaking down with a large number of users and properties. For example, while testing this design, we used the data from the ASP.NET Forums. At the time, this was 200,000 users, each with about 15 properties. This resulted in 3,000,000 rows in the data table! An important benefit that SQL can provide, namely normalized data, is lost. We also knew that people would want to use standard SQL syntax for selecting from the tables for reporting and maintenance- for example, try running a report on this type of design for all who live in a certain zip code using the vertical table design-not easy! The obvious solution was to use normal tables, but we did not want users to have to call .
The conundrum was that we could choose either a very flexible schema-based design that had scale limitations, or a super-scalable design that might be too inflexible for some people. Furthermore, beyond the simple data schema problems, we also wanted partitioned data access And We Wanted The Personalization Apis to Be Extensible. We Wanted A Lot!
What did we decide? Well, we decided that each customer scenario would be unique. We knew that we would eventually want to implement it ourselves on www.asp.net, and would want control over the data model. To solve the problem, we Borrowed a pattern we had been experimenting with in the asp.net forums.
THE PATTERN
The pattern itself is exceedingly simple and is given the name "provider" since it provides the functionality for an API. Defined, a provider is simply a contract between an API and the Business Logic / Data Abstraction Layer. The provider is the implementation of the API separate from the API itself For example, the new Whidbey Membership feature has a static method called Membership.ValidateUser () The Membership class itself contains no business logic;... instead it simply forwards this call to the configured provider It is the responsibility of the provider class to contain the implementation for that method, calling whatever Business Logic Layer (BLL) or Data Access Layer (DAL) is necessary.There are some rules for how a provider behaves. A provider implementation must derive from an abstract base class , WHICH IS Used to Define A Contract for a Particular Feature. For Example, To Create a Membership Provider for Oracle, You Create a New Class OracleMembershipProvider, Which Derives F rom MembershipProviderBase. The feature base class, for example, MembershipProviderBase, in turn derives from a common ProviderBase base class. The ProviderBase class is used to mark implementers as a provider and forces the implementation of a required method and property common to all providers. Figure 4 gives an esample of the inheritance chain.
Figure 4. a provider inheritance chain
The Implementer Must Also Define A Configuration Section Entry USED to Load The Provider. Below Is An Example of What this Configuration Entry Looks Like (Borrowed from the asp.net forums):
DEFAULTLANGUAGE = "en-en"> TYPE = "aspnetforums.data.sqldataProvider, Aspnetforums.sqldataProvider " Connectionstring = "[Connection String]" Databaseowner = "dbo" /> Type = "aspnetforums.data.accessDataProvider, Aspnetforums.accessDataProvider " FILELOCATION = "~ / data / accessdataProvider / aspnetforums.mdb" /> providers> forums> forums> configure> We'll see what this all means momentarily. The beauty of this pattern is you can create a new class, for example, OracleForumsProvider, by deriving from the ForumsProvider base class and use Oracle as your data store. Or, if you prefer to continue using SQL Server, but wish to change the behavior of a single method, you can derive from SqlForumsProvider, override the method whose behavior you want to change, and then add that new class to the The Following Information Comes Directly from the provider specification for asp.net whidbey. Common Behaviors / Characteristics Below Is a Listing of Characteristics Common To Providers. Base Class . Threading Providers should be free-threaded / thread safe, with one instance per application domain. Any provider-specific objects created more frequently (for example, one per request) should be created through provider APIs.Factory methods The Abstract Base Class Should Support Factory Methods To Create New Objects Wherever Appropriate, for Example, Createuser (). IF a feature allows a provider to create a framework object, but does not allow the provider to extend the object, the framework class shouth besealed. Complex objects created by a provider may keep track of the provider that created it, and expose it as a Provider property. This allows users of the feature to determine the provider that owns the data for the object. For example, when a new user is Created with the membership API, IT May Be Useful for the developer to be aware of the provider That Data for the object is stored in. Administration THE OBJECT MODEL for ProviderCollection Should Include Apis To Add, Remove, and Clear The Collection and To Set Parameters of Individual Providers in The Collection. Every Feature That Has Providers Should Have A Provider Property That Returns Type ProviderCollection, for Example, MemberShp.Providers. Class Naming and Namespaces The Specific Province Base Classes Should Be Named [Feature] providerbase, for example, membershipproviderbase. Implementations Should Be named [ImplementationType] [Feature] provider, for example, sqlrolemanagerProvider. Server controls that allow selection of a provider should have a property named Provider This property should default to the value of the defaultProvider attribute in the related configuration section for the feature For example, the new.. Figure 5 Below Calls Out Some of The Common Names and casing That Should Be Used for Various Data Stores (Where name is [name] [feature] provider). Figure 5. Suggested prefixes for provider class names For Data Stores That Are Not Named Above, The Name Value Should Be Pascal Cased. Configuring the default provider All features that have providers must have a configuration section and should define a defaultProvider attribute If a default provider is not specified, the first item in the collection is considered the default For example..: TYPE = "System.Web.Security.WindowstokenroleProvider, System.Web, Version = 1.2.3300.0, culture = neutral, PublickeyToken = B03F5F7F11D50A3A " Description = "description here" /> TYPE = "System.Web.Security.sqlroleProvider, System.Web, Version = 1.2.3300.0, culture = neutral, PublickeyToken = B03F5F7F11D50A3A " Description = "Description Here" ConnectionstringName = "[Name in roleManager> configure> Specifying a DefaultProvider Attribute Is Not Required, Although IT IS Highly Recommended. All ASP.NET Providers Will Specify A DefaultProvider Attribute. Provider-friendly name or Dictionary Key When defining a provider within configuration, it is required for the name attribute to be defined. Furthermore, it is recommended that provider names follow a pattern to easily distinguish who owns the providers. The suggested pattern, and the pattern followed by the ASP.NET Team IS: [Provider Creator] [Data Store] provider. For Example, The Membership Provider Capable of Storing Data In SQL Server IS: TYPE = "System.Web.Security.sqlroleProvider, System.Web, Version = 1.2.3300.0, culture = neutral, PublickeyToken = B03F5F7F11D50A3A " Description = "ASP.NET SQL Server Provider" ConnectionstringName = "[Name in It is important to note the friendly name of the provider when specified in configuration is distinct from the class name of the provider. The friendly name does not need to include the feature name, for example, Role, since the friendly name values are only usable within the context of the feature, for example, Roles.Providers [ "AspNetSqlProvider"]. The provider creator detail is added to distinguish between providers that may access similar data stores but pose differing behavior. Provider Type When defining a provider within configuration, it is required for the type attribute to be defined The type value must be a fully qualified type name following the format:. Type = "[namespace.class], [assembly name], Version = [Version], Culture = [Culture], PublickeyToken = [public token] " For example: TYPE = "System.Web.Security.sqlroleProvider, System.Web, Version = 1.2.3300.0, culture = neutral, PublickeyToken = B03F5F7F11D50A3A " The strongly type name is desired, however, it is also legitimate to use it TYPE = "System.Web.Security.sqlroleProvider, System.Web" Expected API Features that derive from the various provider base classes should follow the common pattern of supporting a Provider property and Providers collection property that allow the developer to access the default configured provider as well as the other providers specified in configuration For example.: SQLMEMBERSHIPPROVIDER P = (SqlmembershipProvider) Membership.Provider; AccessmembershipProvider a = (ASPNETACCESSPROVIDER) ["AspnetAccessProvider"; The Provider property and Providers collection allow for runtime access to the underlying Provider class instance. The Provider property returns the currently configured default provider, while the Providers collection returns a collection of all the available providers defined within configuration. Use of All providers that require a connection to a database or require the use of a connection string to locate their data store should use the For all providers shipped by Microsoft, it should not be possible to store a connection string in the provider definition. Rather the An Exception Will Be Thrown WHEN A Provider Attempts To Use a connection string Obtained from Description Attribute Although not an explicit requirement for all providers, any providers created by Microsoft should have a description attribute. The description attribute should contain a brief, friendly description that can be displayed in the Web administration tool or other displays. . String Comparisons . Conceptual Specification The Conceptual Specification Section Addresses Common Classes Shared By All Providers, The Collection Class Used to Manage Providers, and Rules For How Providers Are Managed Withnin A Features Configuration Section. ProviderbaseAll Provider derive from a common providerbase class. By Supporting this Common Abstract Base Class, All Providers Additionally Share A Common Base Type. Namespace system.configuration.provider { Abstract class proviDERBASE { //Methods Public Abstract void Initialize (String Name, NameValueCollection config; // Properties Public Abstract string name {get;} } } When a new instance of a provider is created, internally the Initialize () method is called. It is assumed that all provider instances will add additional functionality to the provider beyond ProviderBase, for example, MembershipProvider. However, all providers should still derive from ProviderBase . Figure 6 Initialize Method for ProviderBase Figure 7. Name Property Of ProviderBase Figure 8. Add method of providercollection Figure 9. Clear Method of ProviderCollection Providercollection The ProviderCollection is a utility collection class used to manage classes that derive from ProviderBase. Features that implement providers can use the ProviderCollection to add, remove, or clear all providers. Internally, instances are cast to the correct type when a typed provider is needed. Namespace system.configuration.provider { Public Class ProviderCollection: ienumerable { //Methods // Public Void Add (Providerbase Provider); Public void clear (); Public void transove (string name); Public void setreadonly (String name); // Properties // Public Providerbase this (string name) {get; set;} Public int count {get; set;} } } Figure 10. Remove Method Of ProviderCollection Figure 11. SetReadonly Property of ProviderCollection Figure 12. Add method of providercollection For Example, When Membership.Providers ["AspnetsqlProvider"] is review, INTERLY THE FOLLOWING CODE IS Assumed: Public MemberShipProvider this [String Name] { Get {return (MembershipProvider) providercollection [name]; ... } CONFIGURATION Features that make use of providers must implement a Providers Are Defined At The Machine Or Application Level. Providers Cannot Be defined in Web.Config Files in Application Director That Are Not Application Roots. Setting the default provider . All features that have providers should have a defaultProvider attribute on the main configuration section If a default provider is not specified, the first item in the collection is considered the default For example.: TYPE = "System.Web.Security.WindowstokenroleProvider, System.Web, Version = 1.2.3300.0, culture = neutral, PublickeyToken = B03F5F7F11D50A3A " Description = "description here" /> TYPE = "System.Web.Security.sqlroleProvider, System.Web, Version = 1.2.3300.0, culture = neutral, PublickeyToken = B03F5F7F11D50A3A " Description = "Description Here" ConnectionstringName = "[Name in providers> roleManager> configuration> Managing Providers in Configuration: The It is not an error to declare an empty Figure 13. Add Element for Configuration NEW Providers Areadded Using The Figure 14. REMOVE Element for Configuration Figure 15. Clear Element for Configuration Conclusion The provider model can help you design flexible and easily evolved APIs for your applications In fact, today it's being used in the ASP.NET Forums (http://forums.asp.net) as well as DotNetNuke (http:. // www .dotNetnuke.com), Both of Which Are ASP.NET 1.1 Applications. In Part 2 of this article, we'll Examine An Implementation of this Specification / Pattern That You Can Begin Using In Your ASP.NET 1.1 Applications.