Today I began the adventure of converting one of my oldest .NET 1.1 apps to 2.0 with the intent of doing it the "new" way (and not just the way I normally do things). As expected, the first task is to get the membership registration/login working. I must say the whole new model feels very strange. Potentially cool, but very strange.
Take for example the idea of adding custom attributes to a user. Normally this would involve the common User class, making a public property, blah, blah, database, whatever. Instead, you add what properties are applicable to the Profile and via magic they are loaded and stored for you. Here's a sample of my web.config that is using this:
<
profile>
<properties>
<add name="DateCreated" type="date"/>
<add name="NotifyEmail" type="bool"/>
<add name="EmailActiviationCode" type="string"/>
</properties>
</profile>
ASPNET automagically makes a class for you to use, so anytime you want to access these properties its a simple matter of:
Profile.EmailActiviationCode =
"123";
Okay, weird but I'll give it a go for now. But I really, really want to revert to the comfort of my own custom objects and factories!!!
Not that I'll be able to comment on that further, as I finally overcame a major issue just getting database access to work correctly. Anyone who has been to a Microsoft presentation of late has seen their Application Settings web applet that does a lot for you, including provider selection, user and role management, etc. Pretty nice. But say you want to do something that isn't covered in the script, such as not require "question and answer" password retrieval. This is where the fun begins.
First off, you must specify a custom provider. Relax, you don't need to learn the whole new provider class model and crank code until midnight tonight, you need to define a new provider [it would be nice if this distinction was made a bit clearer in the docs]. Specifying a custom provider is a relatively simple matter of defining the following section in your web.config:
<
membership
userIsOnlineTimeWindow="20"
defaultProvider="TipsMembershipProvider">
<providers>
<clear/>
<add name="TipsMembershipProvider"
type="System.Web.Security.SqlMembershipProvider"
connectionStringName="LocalSqlServer"
enablePasswordRetrieval="false"
enablePasswordReset="true"
requiresQuestionAndAnswer="false"
applicationName="Tips"
requiresUniqueEmail="true"
passwordFormat="Hashed"
maxInvalidPasswordAttempts="5"
minRequiredPasswordLength="7"
minRequiredNonalphanumericCharacters="1"
passwordAttemptWindow="10"
passwordStrengthRegularExpression="" />
</providers>
</membership>
In your machine.config all the default providers are set up to use a local SqlExpress database and make everything easy for you. That's fine for vanilla apps, but not where customization is necessary (e.g. the real world). My calling <clear/> you are removing from the AppDomain any prior provider definitions so only yours are visible. This is really important, especially for online shared hosts. And you see on the bolded line that I said I don't want to require question and answer as part of my user creation process.
That's it, right? Simple! No. Enter frustration.
There is a nice overload to the Membership.Create user call that sets a user to unauthorized and returns an enum of what error occurred. Two of the paramters is for question and answer, which, as you see below, I left to empty strings:
MembershipUser
user = Membership.CreateUser( txtUserName.Text, txtPassword.Text, txtEmail.Text, "", "", false, out status );
The gotcha is that you cannot use this overload if you intend NOT to use question and answer parameters. Instead, you are forced to use exception handling to catch for illegal user creations, and respond accordingly.
try
{
MembershipUser user = Membership.CreateUser( txtUserName.Text, txtPassword.Text, txtEmail.Text );
...
}
catch ( MembershipCreateUserException ex )
{
lblSignupError.Text = ex.Message;
}
I suppose it could have been worse (not being able to change behavior vs. scrapping the entire membership model), but it could have been better and more intuitive.