Why I've outgrown Membership and Profile

A couple of years ago, when I was less focused, finished with my book and completely unmotivated to develop anything useful for my own non-day job projects, I struggled trying to shoehorn my apps into the ASP.NET Membership and Profile API's. Probably because of my lack of experience, I became very frustrated at the point where the objects started to relate to the data.

It occurred to me fairly recently that these systems provide a level of abstraction that makes sense in terms of data storage, but you still have to do a fair amount of work in your providers to make sure you aren't pounding the data store. That means caching, of course, but you still might be creating objects in several places (handlers, the page, user controls, etc.), and each time you're going to the well, whether it be by way of the caching mechanism you create or going to the data store.

So why cache at all? Why not get the data once, early in the request cycle, and go back to it when you need it? Oddly enough, it was good old fashioned FormsAuth that made me think of this, where in ASP.NET v1.x we would look up the user and roles, probably in an HttpModule, and access that data from where ever.

Yes, you can do this today with a well-written Membership provider, but it's more complicated than it needs to be. Not only is the interface fairly large, but then you have to create the various objects during the page lifecycle which in turn call the various pieces of the provider. It just seems like a lot of work. It means that in every piece, you have to call something like Membership.GetUser(User.Identity.Name), invoking the provider methods.

What if you did something a little more simple. Heck, use the Membership API if you want to, but do it once. Create an HttpModule that goes something like this:

    public class UserContext : IHttpModule
    {
        public void Init(HttpApplication application)
        {
            application.AuthenticateRequest += new EventHandler(application_AuthenticateRequest);
        }

        private void application_AuthenticateRequest(object sender, EventArgs e)
        {
            HttpContext context = HttpContext.Current;
            MembershipUser user = Membership.GetUser(context.User.Identity.Name);
            context.Items.Add("membershipUser", user);
        }

        public static MembershipUser Current
        {
            get
            {
                return (MembershipUser)HttpContext.Current.Items["membershipUser"];
            }
        }

        public void Dispose()
        {
        }
    }

Then all you need is to call UserContext.Current from your code and you've got access to all that your user object has to offer. I used Membership here, but you could roll your own too. In fact, you could make this an abstract class and leave the implementation of AuthenticateRequest to a derived class, specific to your app.

At first glance, this seems like a very small win, but how much do you use user objects around your page? Furthermore, how much plumbing have you written to implement caching? When you eliminate all of that, suddenly your code gets a lot more simple, and a lot easier to manage. If I had a half-dozen user controls on the page that all had to access my user, and maybe manipulate it, at least it only has to read once from the data store, and I'm free to not write a bunch of caching code for subsequent object creation.

One caveat here is that you do need to keep thread safety in mind. If you don't use asynchronous page or handler methods, you're OK, but keep that in mind.
 

2 Comments

  • Hmm - I think you want to be very careful with the code above. You are retrieving the membership information about the user on each request with this code (since the AuthenticateRequest event fires on each web request to the application).

    There is no need to retrieve the MembershipUser object in a lot of cases (for example: when you call User.Identity.Name it is retrieving the username not from the membership system - but from an encrypted cookie). So I'd recommend not pre-fetching it on each request.

    If you want to use a static instance like above, I'd recommend only retrieving the MembershipUser if it hasn't already been requested and cached. That way you are doing it "on demand" - and only pay the performance hit if you really need it.

    You could actually avoid the need to register a IHttpModule at all to accomplish this - since you can use HttpContext.Current.Items from any class (since it is a static method). So if you want you could encapsulate this logic into a helper class in your project that doesn't need to be registered in the web.config.

    Hope this helps,

    Scott

  • But thing is, the data beyond the user name is used on every request, generally more than once.

    But I do get what you mean about doing lazy initialization on it, so that if the static call is made and nothing is there, get it then. I agree that's probably a better way to do it.

Comments have been disabled for this content.