Provider Model Misconceptions

Paul van Brenk does not understand the Provider Model that is built into .NET 2.0. I don't think a lot of other people really get it either. His argument against using abstract base classes is a common one for anyone that has been doing COM/COM+/C++ development.

For the past 15 or so years, if you wanted to create a pluggable architecture, you used Interfaces. They define a signature that classes must implement in order to conform to that Interface. He says that this is the best way to go, and he is incorrect. I can see where he is coming from though, so let me clear the air a bit. Most of this is taken from my two hour presentation on the Provider Model, which I'll post online sooner or later.

Providers with Interfaces

  • Pros
    • Straightforward
    • No type conversions
  • Cons
    • Impossible to version
    • Hard to maintain
    • Does not scale well
    • No default implementation

I'm not going to hit on the pros at all, cause that much is obvious. But lets look at the cons. Interfaces are immutable, making versioning them impossible. Once an interface is made public, you can't change it without breaking the contract between classes that the Interface creates. Paul talked about the abstract class way being a maintenance issue... how easy do you think that maintaining 4 different interface versions would be? What about 5 or 6? Do you really want to have to worry about whether you should implement ICoolMethod, ICoolMethod1, or ICoolMethodEx? Finally, you can't have any default implementation, or implementation that every provider should share. All you get is the mold by which all implementing classes should conform to.

Abstract base classes solve all those problems. First of all, that common contract between classes is created through an inheritance chain. This is accomplished by implementing the Strategy design pattern. It's a more OOP architecture. For anyone coming from a VB background, it should fit like a warm glove. Using this method, you'll be able to create a base class, like MembershipProvider, which serves as the "face" of the Strategy pattern. This class not only defines what the rest of the providers will look like (because this class is marked either "abstract" or "MustInherit") but it also defines default functionality that every provider must implement.

This makes life easier for plugin developers in many ways. First, it's much easier to version. You can add functionality at any time to any part of the inheritance chain without breaking any existing code. If you add something to the base of the chain, every part of the chain above that has access to it. Therefore, it allows you to scale functionality up (by adding new capabilities) as well as out (by adding new providers).

Now, Paul was upset because of a percieved need to implement every function, which he says can be eliminated by using Interfaces. This is patently false in just about every aspect of his complaint. First of all, you have to implement every method on an interface, or it won't conform to that interface, and therefore, it won't compile. Second, the functionality issue is easily solved by making your provider default to a certain value (if Integer, 0; if String, then String.Empty; If boolean, then false, etc) for its operations. Then, you only override functionality that CAN be accomplished, thereby saving on the amount of code to write.

Paul suggests that you throw a "NotImplementedException" instead of provifing default "null" values. This is an extremely bad idea. Exceptions are expensive to throw, and expensive to catch. The Provider Model architecture was not designed to be a guide as to what should be an exception and what shouldn't/ You may so choose to throw exceptions in your provider based on certain runtime criteria. But you should not throw an exception if a method is not defined. Instead, your calling code should be able to handle and empty or null return value in act in less runtime-costly ways.

There's a lot more to the architecture than this (remember my presentation was two hours long), so this is about all I'm going to tackle for now. Just remember that, if you're coming from a C/C++/C#/Java/COM/COM+ programming background, this is probably a little Greek to you. But if you understand the underlying reasons for the architecture, it makes a whole lot of sense. And since Microsoft had built this functionality into the base of the Framework, you're going to see a lot more of this architecture.

And be careful. All Provider Models are not created equal.

7 Comments

  • Paul is just following the contract MS has put up in the case of the MembershipProvider class when he throws exceptions in those methods that he doesn't support in his provider.



    The MembershipProvider has two properties EnablePasswordReset and EnablePasswordRetrieval that clients of the class should use at runtime to discover whether a specific provider supports either of the two functionalities. If EnablePasswordReset returns false a client should not call ResetPasword on that provider at all.



    In the implementations for SQL and Access by Microsoft ResetPassword will throw a NotSupportedException if ResetPassword is called on a provider who is configured to not support resets of passwords. Throwing an exception in this case is ok, because clients should first check EnablePasswordReset before calling ResetPassword. The NotSupportedException would only be thrown if the code using the provider has a bug.



    Not throwing an excpetion in ResetPassword when that is not supported by the provider but rather returning a default null value (ie an empty string) would break the contract set out by Microsoft for MembershipProvider and lead to unexpected behaviour. In the case of ResetPassword it in fact always returns an empty string (it probably should really have no return value at all).

  • Holy crap. James, I'm gonna need the weekend to respond. I agree on some points, but your response was so detailed that anything less than a detailed response would just not be respectful ;).

  • Why would this be Greek to Java developers? Java has used a pluggable or "Provider" model for quite sometime. Most Java APIs are based on interfaces with some abstract base classes providing minimum functionality. The nice part about using interfaces with an abstract implementation is that you can then choose to extend the abstract base class or if you need you can implement the interfaces directly. Microsoft is taking a step in the right direction by allowing us to extend and enhance their framework (ASP.Net).

  • Jon's exactly right. Java's had provider/factory/plugin type models for YEARS. A lot of their APIs are designed with them in mind because it offers third-parties to supplement the APIs or provide their own implementations. Interfaces have been used extensively in most of those designs.



    And yeah, I use to be on the other side of the fence. That should come as no surprise.

  • Can you explain how late binding is bad in a pure OO world? Or, you refering to the performance concerns with late binding?

  • Obviously not everyboday at Microsoft agrees with on the return types/exceptions issue. In the WSE TokenManager model, an exception is thrown if the tokenmanager is unable to authenticate a user for the service.



    My biggest problem with the MemberShipProvider is: it heavily depends on the membershipuser, which is a much too specialized class, I generally don't need :

    Comment

    CreationDate

    Email

    IsApproved

    IsOnline

    LastActivityDate

    LastLoginDate

    LastPasswordChangedDate

    PasswordQuestion



    And their related methods.



    The other problem is the CreateUserMethod, this takes 7 parameters, of which one out parameter and returns a MemberShipUser... The only 2 parameters I will regularly use are the username and the password.



    Basically I still believe the providermodel is great, but the membershipprovider-stuff needs some work.

  • Oops, forgot to add this, before the last sentence:



    "And why doesn't it accept a membershipuser or derived class as inputparameter, now I have to do a cast and fill my custom properties afterwards."

Comments have been disabled for this content.