Examining ASP.NET's Membership, Roles, and Profile - Part 7
By Scott Mitchell
A Multipart Series on ASP.NET's Membership, Roles, and Profile |
---|
This article is one in a series of articles on ASP.NET's membership, roles, and profile functionality.
SqlMembershipProvider and the security Web controls.aspnet_regsql.exe ).SqlProfileProvider . |
Introduction
One of the main challenges of building a programming framework is balancing the desires to create a standardized, straightforward API for accomplishing common tasks and providing flexibility and customizability so that developers using the framework can mold the framework to fit their applications rather than having to bend their applications to fit the framework. With the .NET Framework 2.0, Microsoft implemented this delicate balance with the provider model. The provider model allows for a standardized API to be defined within the framework, but enables developers to design their own concrete implementation and plug that implementation into their systems at runtime. (See A Look at ASP.NET 2.0's Provider Model for more details on the provider model, its implementation in ASP.NET 2.0, and the concepts behind it.)
The Membership, Roles, and Provider pieces examined throughout this article series all utilize the provider model. Throughout the past
six installments of this article series we've examined some of the providers that ship with the .NET Framework (such as
SqlMembershipProvider
, SqlRoleProvider
, and SqlProfileProvider
). In fact, you can
download the source code for these built-in providers from Microsoft as well as a Provider Toolkit for creating your own
custom providers [link]. And Scott
Guthrie lists a number of custom Membership, Roles, and Profile providers in his ASP.NET
2.0 Membership, Roles, Forms Authentication, and Security Resources page.
If the none of the built-in providers meet your needs, you can always create your own custom providers. Custom providers are
useful when integrating legacy systems (perhaps you have an old Users
database table that differs from the
database tables used by SqlMembershipProvider
) or when needing to persist membership, role, or provider information
in a backing store not natively supported by one of the built-in providers.
In this article, we'll discuss the concepts behind creating and using a custom provider and then implement a simple custom profile provider that serializes profile information to XML files. Read on to learn more!
The Responsibility of the Profile System
As discussed in Part 6 of this article series, the Profile system is responsible for storing additional user-specific information. The Profile system allows the page developer to define a set of "profile properties" in the
Web.config
file, and then needs to save and read those property values from
some backing store upon request. The .NET Framework 2.0 ships with a SqlProfileProvider
, which persists these user-specific
properties to a denormalized database table (namely, it squishes all of the profile property names and values into single columns in
a database table). Microsoft also offers the custom
Table Profile Provider, which uses a normalized data model to store the Profile properties, using a separate column in a
database table for each property.
If neither of these Profile providers meets your needs, you can always create your own custom provider. Creating and using a custom provider - whether it be for Profile, Membership, Roles, the ASP.NET site map, or some other subsystem - entails the following steps:
- Create the custom provider - this involves creating a class that derives from the appropriate base class.
To extend the Profile system, the provider must derive from the
ProfileProvider
class. - Configure the application to use the custom provider - we need to configure the application's
Web.config
file to specify that the Profile system should use our custom provider in lieu of the default one (SqlProfileProvider
).
SqlProfileProvider
in ASP.NET 2.0 is implemented.
Recently I was discussing the default Profile provider (SqlProfileProvider
) with a colleague and he inquired as to
whether there was a custom Profile provider that persisted data in an XML file format. I subsequently Googled for any results,
but didn't find anything and therefore decided to try my hand at creating such a custom provider. The remainder of this article
examines my implementation of XmlProfileProvider
, a custom Profile provider that persists user-specific settings
in XML files in the App_Data
folder. The complete code (along with a simple test web application) can be downloaded
at the end of this article.
Designing the XmlProfileProvider
The default SQL-based providers for the Membership, Roles, and Profile systems are all designed to store information for multiple applications. This is evidenced by the
aspnet_Applications
table, which has a record for each application
instance; each user in the system is associated with an application via the ApplicationId
foreign key in
the aspnet_Users
table. I decided to replicate this functionality for my profile provider (since, conceivably, the
XmlProfileProvider
might be used in conjunction with the SQL-based Membership and Roles providers). The Profile
data is serialized (by default) to files in the App_Data
folder so as to prevent users from directly requesting these files
through a browser. (If a user attempts to visit http://www.yoursite.com/App_Data/anyFile
through their web
browser, ASP.NET will return an HTTP 404 message.) You can, however, customize the base folder the XML files are saved under via
the custom provider's profileFolder
setting.
Specifically, the Profile data is stored in the following format: ~/App_Data/XmlProfiles/applicationName/username.xml
.
Each XML file starts with a <profileData>
root element, but its children elements are determined by the
Profile properties spelled out in Web.config
. As discussed in Part 6
of this article series, the Profile properties can be serialized as strings, XML, or binary data. For string-serialized
properties, the XML file saves the result as text content. For XML-serialized properties, the raw XML is saved, and for
binary-serialized content, the base-64 encoded binary content is persisted.
To illustrate the file naming and XML content persistence, imagine that our web application used six Profile properties
defined using the following <properties>
definition in Web.config
:
|
Here, the type Address
is defined as a simple class in the App_Code
folder, and has scalar properties
like Address1
, Address2
, City
, and so on. Note that the BillingAddress
Profile property serializes the Address
type as XML, while ShippingAddress
serializes it as
binary data.
Given the above Profile property definition, if a user with username "Jisun" were to interact with the Profile system when visiting
a page on our website whose application name was configured to "MyApp", a file named ~/App_Data/XmlProfiles/MyApp/Jisun.xml
would be created, and might contain the following markup:
<?xml version="1.0" encoding="utf-8"?>
|
As you can see, each Profile property is persisted as an XML element as a child element of the <profileData>
root element. For string-serialized properties - like IncludeSignatureInPosts
, Signature
,
HomepageUrl
, and Birthdate
- the property value is persisted as the text value for the XML element.
For the XML-serialized property (BillingAddress
), the XML serialized content is stored in raw format as content
within the <BillingAddress>
element. The binary-serialized content of the
ShippingAddress
property is base-64 encoded.
Programming the XmlProfileProvider
Class
When building a custom Profile provider, have it extend the
System.Web.Profile.ProfileProvider
class. At minimum,
we need to provide the functionality for the GetPropertyValues(context, collection)
and
SetPropertyValues(context, collection)
methods. Both methods accept two input parameters -
context and collection. The context provides two bits of vital information - the username of the
person whose Profile to access/save and whether the user is authenticated. (ASP.NET supports the notion of anonymous profiles.
This is a topic beyond the scope of this article, however. See the Profiles
section on the ASP.NET Quickstarts for more information.) The collection provides the set of Profile properties.
The GetPropertyValues
method is responsible for returning the values for the specifies set of Profile properties,
whereas the SetPropertyValues
's sole task is to write the property values specified in collection to the
backing store (XML files, in our case). There are additional methods in the ProfileProvider
class, but we can
create a fully working custom profile provider by implementing just these two methods.
One of the challenges with creating a custom Profile provider is correctly handling the serialization and deserialization
issues. The collection passed into the GetPropertyValues
method indicates how each property is configured
to be serialized. We can use this information to correct deserialize the data when reading it back from the XML file.
For example, when working with a Profile property that has been configured to use binary serialization, we need to
decode the base-64 value from a string back into the binary format (a byte array). This is done using the Convert.FromBase64String()
method
in the .NET Framework. However, if we are dealing with a Profile property that's serialized as XML or a string, we want to just
read the XML content as text. On the SetPropertyValues
side, we need to know how to serialize the data back to
the XML file. For binary-serialized properties, we need to base64-encode it. XML-serialized data includes the <?xml version="1.0" encoding="utf-8"?>
pre-processing statement, which needs to be stripped out before writing the XML data as child content for the Profile property
element.
Plugging the XmlProfileProvider
Custom Provider Into Your Website
In order to use the
XmlProfileProvider
custom provider in your web application, you first need to download
the code at the end of this article. Drop the CustomProviders.dll
assembly into your web application's
/bin
directory and then update your Web.config
file to specify the custom provider for the Profiles
subsystem:
|
The profileFolder
attribute is optional. If omitted, it defaults to ~/App_Data/XmlProfiles/
.
The download available at the end of this article includes a test web application that illustrates how to use the
custom XmlProfileProvider
class in conjunction with the default SqlMembershipProvider
class.
Conclusion
The provider model allows for Microsoft's .NET Framework 2.0 to offer a consistent, standardized API against which Web controls, the Framework itself, and third-party libraries can be created against, but that still enables the flexibility to customize the implementation details. In this article we saw how to utilize the provider model to create a custom Profile provider, one that persists user-specific settings to XML files rather than a database.
Happy Programming!
Attachments
A Multipart Series on ASP.NET's Membership, Roles, and Profile |
---|
This article is one in a series of articles on ASP.NET's membership, roles, and profile functionality.
SqlMembershipProvider and the security Web controls.aspnet_regsql.exe ).SqlProfileProvider . |