07 October, 2011

Fix: Bug in .NET Roles / RoleProvider / MembershipProvider initialize

Due to a bug in the System.Web.Security.Roles class in .NET 2.0, which is caused by a singleton instance of the last exception that occurred during the Initialize of the RoleProvider, any exception's that occur during Initialize method of the RoleProvider, will be caught and the only way is to recycle the app pool in IIS.

I stumbled upon this bug due to a connectivity issue with our database, that caused a small connection lost to the database, the resulting error on the front end of our website was a database error (which was stored in the static exception inside the Roles class).

It took a little time to track this down and fix it, but the fix is to do a try catch block inside the Initialize method, preferable only around initialize DB code, and your own static object to monitor if the initialize went perfect, then in all the overridden methods do a check if the initialization went fine, and else initialize the DB again.

Update - 2012-08-13:
I sadly stumbled upon the bug in the MembershipProvider also, so to every who stumbled upon the same problem here is a example draft code that can fix the error.

public sealed class MyCustomMembershipProvider : MembershipProvider
{
 private static object _syncLock = new object();
 private static bool _isDBInitializedSuccessfully = false;

 public override void Initialize(string name, NameValueCollection config)
 {
  if (config == null)
  {
   throw new ArgumentNullException("config");
  }
  
  // .... Add your code to validate the config settings, if needed
  
  // Initialize the abstract base class.
  base.Initialize(name, config);

  // .... Add your code to initialize the class, if needed

  // Initialize the DB
  InitializeDB();
 }

 public override bool ChangePassword(string email, string oldPassword, string newPassword)
 {
  // Call InitializeDB() in all methods that require DB access in the class
  InitializeDB();
  // .... Add your code that will change the password
 }

 private static void InitializeDB()
 {
  if (_isDBInitializedSuccessfully) { return; }
  lock (_syncLock)
  {
   if (_isDBInitializedSuccessfully) { return; }
   try
   {
    // .... Add your code to check if the DB is loaded
    _isDBInitializedSuccessfully = true;
   }
   catch (Exception e)
   {
    // .... Add your code to log the Exception's
    e = null;
   }
  }
 }
}

And the code that generates the error:
namespace System.Web.Security
{
 //...
 public static class Membership
 {
  //...
  private static void Initialize()
  {
   if (s_Initialized)
   {
    if (s_InitializeException != null) // static Exception, so only a app-pool reset will fix it
     throw s_InitializeException;
    return;
   }
   if (s_InitializeException != null) throw s_InitializeException;
   //...
  }
  //...
  private static Exception s_InitializeException = null;
  //...
 }
}

Hope this helps you :-)

No comments:

Post a Comment