Pages

Wednesday 24 February 2010

Question: When is a non-static class static?

Answer: When it's a Provider.

OK, quite an obtuse question there so let me explain...

In our ASP.NET MVC application we have a custom Membership Provider and a custom Role Provider. Here's a (vastly simplified) example of the membership provider...

public class CustomMembershipProvider : MembershipProvider
{
    private SqlConnection _connection;
    private string _userName;
    private string _password;

    public CustomMembershipProvider()
    {
    }

    public override bool ValidateUser(string username, string password)
    {
        _userName = username;
        _password = password;

        bool success = false;

        try
        {
            // Sets the _cn member...
            OpenConnection();

            success = ValidateUser();
        }
        finally
        {
            CloseConnection();
        }

        return success;
    }
}

So, pretty straight forward. Nothing wrong with that, is there?

Well, actually, yes. The problem is that you may write this class with the expectation that it will be treated as a  POIO (Plain Old Instance Object) but ASP.NET has other ideas. One, and only one, of these objects is instantiated (during the first request to you site). The big issue here is that we now have private fields on a singleton class - not good.

We ran into problems with SqlDataReaders because the runtime complained about multiple readers on the same connection. We could have implemented MARS, but that still didn't deal with the underlying problem. Worse still is the possibility of an unauthorised user actually being authorised! You only need user1's time-slice to end and user2's to begin midway through that ValidateUser method and you've got serious problems.

This is a summarised suggestion of how we solved this problem...

public class CustomMembershipProvider : MembershipProvider
{
    public CustomMembershipProvider()
    {
    }

    public override bool ValidateUser(string username, string password)
    {
        bool success = false;
        
        using (var cn = OpenConnection())
        {
            var validater = new UserValidater(cn, username, password);

            success = validater.IsValid();
        }

        return success;
    }

    private class UserValidater
    {
        private SqlConnection _connection;
        private string _userName;
        private string _password;

        public UserValidater(SqlConnection cn, string userName, string password)
        {
            _connection = connection;
            _userName = username;
            _password = password;
        }

        public bool IsValid()
        {
            // Logic removed for brevity...
        }
    }
}

No comments:

Post a Comment