ASP.NET Providers, reloaded

Note: this entry has moved.

Rob Howard wrote the second part on the provider design pattern they are using in Whidbey, and how to implement it in v1.x. An interesting reading. First of all, I'm really amazed at how open these guys are to good feedback from the community. Rob even dedicated a paragraph named "Extending configuration" that explains why they chose a NameValueCollection for provider initialization over an XmlNode, as I suggested (I'm sure other did too, it's a pretty obvious thing to ask). I really don't share the feeling that a NameValueCollection is *sooo much* easier than an XmlNode and its added flexibility, but at least I understand now why they did so: they expect complex providers to have their own section handler with all the information they need to work, which makes perfect sense!

There's a major performance issue in the implementation he suggests for v1.x, though, that has to do with the actual creation of the specific provider instance. There's a sort of mess in the naming and wording when this feature is explained. The article says:

When calling routines on the Membership class, internally it will always forwards those calls to an instance of the MembershipProvider—first creating an instance of MembershipProvider using a factory method, Instance(), and then calling the appropriate base class method on the retrieved instance.

Well, from the naming point of view, if Instance() is employed as a factory method, it should be called CreateProvider() or something like that, to denote that an instance will be created each time the method is called. And here comes the confusion, because Instance() more closely resembles the Singleton pattern rather than the Factory Method. If you think about it, there's no reason why we should instantiate a new membership object all the time. Not even the dynamic nature of providers justify this, because switching providers in the config file will cause an app restart anyway, loading the appropriate one afterwards.

And this is even more important than a mere wording because instantiating the provider dynamically requires using reflection. The abbreviated method shown in the article is:

public static MembershipProvider Instance() { // Use the cache because the reflection used later is expensive ...etc... // Load the configuration settings object[] paramArray = new object[1]; paramArray[0] = membershipProvider.Attributes["connectionString"]; return (MembershipProvider)( ((ConstructorInfo)cache[cacheKey]).Invoke(paramArray) ); }

Note that by caching only the constructor information, you're not earning much anyways. You end up using reflection at each method call anyway. So, if you convert this method from a factory method into a singleton, you directly cache the provider instance you create the first time the method is accessed. Furthermore, I'd directly move this singleton instance creation to the Membership (or similar for other provider-enabled features) altogether:

public class Membership { static MembershipProvider _instance; static Membership() { // Get the names of the providers MembershipConfiguration config = MembershipConfiguration.GetConfig(); // Read the configuration specific information for this provider Provider provider = (Provider) config.Providers[config.DefaultProvider]; // The assembly should be in \bin or GAC try { Type type = Type.GetType( provider.Type ); _instance = (MembershipProvider) Activator.CreateInstance( type ); // Initialize the provider with the attributes. _instance.Initialize( provider.Name, provider.Attributes ); } catch (Exception e) { throw new Exception("Unable to load provider", e); } } public static bool Validate( string username, string password ) { return _instance.Validate( username, password ); } public static void CreateUser( string username, string password ) { return _instance.CreateUser( username, password ); } }

You may have noticed that the original method in the article wrongly calls a special constructor passing a specific hardcoded attribute, "connectionString":

paramArray[0] = membershipProvider.Attributes["connectionString"]; // Special ctor?! return (MembershipProvider)( ((ConstructorInfo)cache[cacheKey]).Invoke(paramArray) );

It's obvious that by doing so the Instance() method is no longer generic, as it's expecting a provider to implement a specific ctor overload and receive a connection string! What if I have an XmlMembershipProvider? Given the base ProvideBase class all providers must implement, and what is explained in the article it makes much more sense to use the ProviderBase.Initialize method for provider initialization, as I did in the static ctor above:

Type type = Type.GetType( provider.Type ); _instance = (MembershipProvider) Activator.CreateInstance( type ); // Initialize the provider with the attributes. _instance.Initialize( provider.Name, provider.Attributes );

Anyway, it's good that we're having these discussions. The ASP.NET community needs these concepts to turn web applications into well architected solutions, leaving the ASP spaghetti programming style behind once and for all.

14 Comments

  • Daniel,



    You did some work here that I can just copy and paste into my own. :P



    One thing though, I don't know if it was for brevity sake or not, ASP.NET 1.1 implementation doesn't seem to implement Provider and Providers properties as mentioned in Part 1 of Rob's article. Thus, it's using the default provider throughout without the ability to switch to a different provider at runtime.



    It also wasn't evident in the article that these properties would be class-based rather than instance-based. So for Membership, there would be a Providers property(implementing ProviderCollection) in either Membership(feature class) itself or in MembershipProvider abstract class. Which would make more sense? Or would it make more sense to have in both?

  • Your singleton suggestion is just about the same thing .Text does.



    -Scott

  • Scott: of course this is nothing new. Singleton and factory method patterns have existed for a long time (GoF is from '95 and I'm sure it wasn't the first one to mention it). So, I don't see what's the relevance of saying .Text does it too. I know tons of software that does.



    Jiho:

    You're damn right! I'd have the Providers property on the feature class, that is Membership, and be a typed wrapper over the loosely typed ProviderCollection.

  • I've been working with the Whidbey version of the Probider Model, and I like it much better. That Instance() stuff is really ugly in my opinion. If I were you, I'd use Reflector 4.0 and start digging into how the Membership provider works in Whidbey. It still has it's problems, but it's a lot more elegant.

  • You bet I'm doing that Robert! I was just discussing the article and Rob suggestions for v1.x.

    What I dislike of Instance() is that it should be a property, not a method, if it's going to be a real Singleton...

  • Correct, it should be a property. It should be the "Provider" property, which gets populated from the default provider instance. The instance should be created from a private "Initialize" routine, which is basically sends the provider information back to a private "ProvidersHelper" method that handles the instantiation.



    But that's not the way Rob explained it in his article. Why? because it takes over 1000 lines of code to accomplish it that way. Rob's article was a sort of lazy way to accomplish the same thing, but is in no way robust enough an implementation IMO.



    Consider the article a Level 200 explanation. I'm pretty sure Rob is building up to discussing the Whidbey implementation. If he won't, I will be soon.

  • Ok, so you think what makes something robust is calling a private routing that calls another one to handle the instantiation, and somehow that would take 1000 lines of code....

    Let me say I'm surprised, to say the least. I've showed the same functionality (I'll leave aside the pseudo-"robustness" you're talking about) in about what, 10 lines of code? The length of the explanation would have been almost the same, just replacing "factory method" for "singleton" and those few lines of code.

    Even the provider construction code differs in a couple lines! At this point, I'm forced into thinking you're just joking....

  • You haven't looked at the Provider Model code in Whidbey then...



    It's too complicated to explain over a comment. If you really need me to explain it, then I will in a later post.

  • You don't seem to realize that how it is specifically implemented in Whidbey is absolutely irrelevant. It seems like you never implemented such a pluggable pattern before. There's nothing too complicated about it. What we're talking about is how to implement the *feature* in v1.x (that was Rob's article about), and for that matter, you don't need to copy every single line of code in Whidbey.

  • I think I see why the constructor invoke was used rather than Activator.CreateInstance - for performance gain of some 300-400 ms. It may not seem all that much, every bit counts in production.



    But that also assumes that a new instance is required for every request. If we employed the Singleton pattern then one-time cost of Activator.CreateInstance may not be a big deal.



    Question: If the provider truly uses the Singleton pattern, then the provider should not store any instance specific information because the same instance will be returned to every request coming in - I just ran into this situation and took me some time to figure out what was happening. Then, does it make sense to have a purely static class? - meaning that a specific provider be implemented as a class with only static methods/properties. Also then, does it make sense to create an instance in the first at all?



    This led me to a series of thoughts. For example, think of ADO connections(or any other DB connection scheme). There is usually a pool of database connection objects lying around so that you don't incur the creation cost every time. The objects are in the same class - exhibit same behavior - but each instance may be different for the duration of its usage. They definitely contain instance specific fields and other stores. But when you are done using it, you "dispose" it and it goes back into the pool in a clean state for reuse.



    I think that the providers should exhibit a similar behavior. Hence, the constructor invoke method makes sense in that case. Even if only, let's say, 20 instances are created in a pool, there'd still be a saving and if 100, obvioulsly more. But then we are assuming that the providers would need to contain instance specific information rather than being service-oriented. In which case, the class would only contain static methods/properties, not unlike the client facing static feature class (i.e. Membership). Then only the type would need to be cached and the feature class would simply cast the type to the provider type and invoke its static methods.



    Am I making sense? What do you think?

  • You're right in a number of points Jiho. Caching the ctor and returning a new instance all the time seems to imply that the provider will need some instance-specific information. However, all providers are (and should be) developed in a service oriented manner, therefore, be stateless.

    The case with ADO.NET is different, IMO, because you can do other things with the instance.

    I don't think it's worth it to have similar functionality on providers.

    About having everything static, there's no such thing as casting a System.Type (the specific provider) to another System.Type (the base provider type) and calling static methods there. First, both are the same .NET type, so there's no cast to do. However, they represent different underlying runtime types. And you can't call static methods on a System.Type (that is, static methods defined in the type represented by the System.Type instance) unless you use reflection, in which case you're in a worse position than if you simply created an instance and used the Singleton pattern in the first place.

    That's why you make it an instance, and not static members. BTW, static members can't be overriden also...

  • Thanks for your clarification.

    I was afraid of that(Type thing). In my case, I can store any request-specific information in HttpContext.Items collection so yes, it seems that duplicating ADO.NET like behavior is unnecessary.

  • Nice work, Daniel, I could not get my head around Rob's cached constructors either and went for a singleton in our app.



    Do you see any advantage of static constructor vs. lazy singleton initialisation? One reason could be that it is shorter to type in the post :-) The problem with static constructor is that if provider initialisation fails, you'd never know what hit you as well as you won't be able to recover. Lazy initialisation would fail just at the right time and in the right place, ie in a client's call thus giving client ability to handle it gracefully.



    <offTopic>

    In our implementation we used provider interfaces. Do you see any disadvantages of using interfaces as opposed to an abstract base class?

    </offTopic>

  • > One reason could be that it is shorter to type in the post :-)



    Don't think so. It takes about the same amount of code. Maybe a couple more lines. And everyone knows about lazy singletons... while not everybody understands why he's using cached ctors...

    I believe using the later is bad, because it implies a new provider instance will be created each time I ask for it. This is completely unnecessary because by definition, a provider is a kind of service, and in this regard, it should be thread-safe and completely stateless. Therefore, I don't see the problem of returning a single instance (singleton) all the time. Too bad RHoward is no longer at MS to explain it :(.



    As for interfaces vs abstract base classes: the later versions better. In a later version of your provider infrastructure, you may need to add methods. If you used an interface, you're lost, as any additions will break existing provider implemtnations. With abstract classes, you can provide default implementation that does nothing or throws and the existing providers will continue to work. This means you'll not end up with IProvider, IProviderEx, IProviderSuperEx and so on... just so you don't break your clients.



    BTW, having a base class for something so specific as a provider, is not a bad thing. After all, you're unlikely going to implement several provider-like functionality in a single class, that require using interfaces instead of base classes, right?

    In general, I tend to think in terms of interfaces when I face some kind of cross-cutting concern, that applies to several unrelated entities, i.e. IAttributeAccessor (for both HTML and WebControls, for example). IXPathNavigable is another good example.

Comments have been disabled for this content.