Robert McLaws: FunWithCoding.NET

Public Shared Function BrainDump(ByVal dotNet As String) As [Value]

News

<script type="text/javascript"><!-- google_ad_client = "pub-4330602465258980"; google_hints = "ASP.NET, VB.NET, C#, C#.NET, WindowsForms, .NET Framework, VS2005, Visual Studio, XAML, WinFX, Windows Workflow, WPF, WCF, Atlas, NetFX3, Visual Studio Orcas"; google_ad_width = 120; google_ad_height = 240; google_ad_format = "120x240_as"; google_ad_type = "text_image"; google_ad_channel ="4997399242"; google_color_border = "B6C9E7"; google_color_bg = "EFEFEF"; google_color_link = "0000FF"; google_color_text = "000000"; google_color_url = "002C99"; //--></script> <script type="text/javascript" src="http://pagead2.googlesyndication.com/pagead/show_ads.js"> </script>
<!--
-->

You should feel free to challenge me, disagree with me, or tell me I'm completely nuts in the comments section of each blog entry, but I reserve the right to delete any comment for any reason whatsoever. That said, I will most likely only delete abusive, profane, rude, or annonymous comments, so keep it polite, please.

Blogroll

Cool .NET Articles

My .NET Tools

My Builder.com Articles

My MSKB Articles

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.

Comments

David said:

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).
# August 7, 2004 9:48 AM

Michael Teper said:

Yes, exactly what David said.

Robert, you really should be careful about how you say things. What you are expressing is an opinion and not Microsoft gospel. Nor does having presented on a topic makes you the absolute source. If someone disagrees with you, it doesn't mean they "don't get it". Finally, if you have a specific reference to Microsoft supporting your opinion, please cite it.

You can also take a look at my response to the comment you left on my blog here: http://michaelteper.com/archive/2004/08/06/195.aspx.
# August 7, 2004 12:16 PM

Robert W. McLaws said:

David, gotcha. I forgot to mention that part... those properties are vital in any PM implementation.

Michael, I'm not really expressing too much of my own opinion. If you think providers should be done with Interfaces, then you don't understand why abstract base classes are infinitely more powerful. That's not a bad thing. But the concept was not understood. i don't feel that sugar coating it is gonna help anyone.
# August 7, 2004 3:40 PM

James Arendt said:

"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."

If you're making a published interface (think contract or standard), you shouldn't be going around and changing them without given due consideration. They really should be set in stone. When I look at electrical outlet in a wall, the interface has hardly changed in years. The pros for this capability is that just about everything that needs power can plugin easily. When viewed in this way, interfaces are ideal for plugin architectures.

The other problem I see with this argument is that abstract classes do not avoid it. If by contract, we assume it means the methods one has to implement, abstract classes have the exact same problem. If you add an abstract method to an abstract class, you cause the exact same problems for those who inherit from that class as you would have if you had added a new method to the interface.

The solution for interfaces is to create new interfaces which has its share of versioning issues. Abstract classes are in some ways less flexible in that you have a rigid inheritance heirarchy to contend with. On the other hand, you can add new methods that are either non-virtual or virtual without breaking derived classes. I think this what you're refering to when you say you can just add a new method to the base class. In that case, it could be argued that abstract classes have the potential to be more flexible. But, I personally wouldn't jump to that conclusion without a big disclaimer.

"All you get is the mold by which all implementing classes should conform to."

That's the whole point.

"Abstract base classes solve all those problems. First of all, that common contract between classes is created through an inheritance chain."

Inheritance chains are fragile. There's a whole host of papers in the OO community that tackle all the various issues with them. Different languages and platforms, hence, deal with it in different ways and introduce their own set of problems.

"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."

The main benefit of this approach is a somewhat simplified model. It could be accomplished using a combination of interfaces with default implementations of those interfaces using either concrete or abstract classes for implementations. You're gaining one less class/interface by going the approach you described above.

"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."

Don't fool yourself that they're easier to version. The whole reason C# and other .NET languages have the distinction between non-virtual and virtual methods and methods that can be sealed is to deal with the host of problems with versioning implementation of behaviour in a class. Even with those features, it is still up to the designer of the library to design the classes with inheritance and versioning in mind. It is also up to the consumer of the library to understand the expectations -- both implicit and explicit -- for deriving from those classes.

A chief criticism from those who develop for non-.NET languages/platforms is that these "features" increase language and design complexity without necessarily removing the burden on the designers of class libraries or developers who use them. It could be argued that they actually increase the burden. But, I'm digressing.

The point is that you made a blanket statement that in reality does not hold water.

"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)."

Once others become dependent on that new functionality, you can't change it without introducing problems. This is the whole problem with fragile base classes.

http://c2.com/cgi/wiki?FragileBaseClassProblem

I would argue that makes it harder to maintain.

"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."

That's if you're implementing the interface directly. If you use a base class that provides appropriate defaults for the interface, this is a non-issue. Secondly, the interface still gives you the option that if you're concerned about the problems of depending on a base class, you can still go ahead and implement it completely yourself.

"But if you understand the underlying reasons for the architecture, it makes a whole lot of sense."

I understand some of the reasoning, but as I've begun to show it doesn't make the most sense. It has its share of flaws in general design, let alone arguments of whether something should return a value or throw an exeption.

"And since Microsoft had built this functionality into the base of the Framework, you're going to see a lot more of this architecture."

Microsoft's teams have made a lot of mistakes over the years with their frameworks and class libraries. An architecure that is heavily reliant on a base class assumes that those designing the base class really know what they're doing. It also assumes those deriving from the base class know what they're doing. That's too many assumptions for me to feel confident.

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

I agree.
# August 7, 2004 4:49 PM

Robert W. McLaws said:

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 ;).
# August 7, 2004 4:55 PM

David said:

Great post, James! I agree with all the points, especially since it makes it very clear that this issue is complicated and doesn't have a simple right/wrong answer.

Obviously you tackled the really important points here, while my point on how a client can find out what functionality a specific provider implements is a lot less central to the debate (although I believe it was the question Paul discussed in his blog post that triggered Robert's post). Nevertheless I have to say that I find the design chosen by Microsoft a lot less elegant than a solution based on interfaces. Having properties signal whether a method is implemented (and can be called) or not just seems awkward to me. Grouping functionality sets that don't need to be implemented into interfaces seems so much more straightforward. There is also the point that one has almost no chance to understand the construction Microsoft chose without looking at the documenation first, while an approach based on interface would be a lot easier to grasp.
# August 7, 2004 7:43 PM

Jon said:

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).
# August 9, 2004 12:33 PM

Michael Teper said:

More exception-related thoughts can be found here: http://michaelteper.com/archive/2004/08/09/201.aspx.
# August 9, 2004 3:23 PM

James Arendt said:

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.
# August 10, 2004 8:51 AM

Jeff Atwood said:

Right, the fragile base class problem, as pointed out ad nauseam above. You're trading one set of problems for another.

Here's the thing: pure OO "everything inherits" design is great for writing a language, but guess what? I'm not writing a language. A handful of people in the world are writing languages. The rest of us are writing shitty little business logic apps.

I've been through enough project cycles to see what a pure OO methodology does to these kinds of business apps: it makes them a lot more complicated than they need to be.
# August 11, 2004 12:56 AM

Robert W. McLaws said:

Well, in a "pure OO" world, late binding is bad too. But, it's one of the many things that makes .NET so powerful.

So what's the big deal?

Jeff: You're not thinking fourth dimentionally. What you don't realize is that you ARE writing a language... one that others will need to understamd at some point.

And I don't know about you, but I'm not in this business to write shitty anything. If you are, you might consider a different profession.
# August 11, 2004 3:08 AM

James Arendt said:

Can you explain how late binding is bad in a pure OO world? Or, you refering to the performance concerns with late binding?
# August 11, 2004 11:33 AM

paulb said:

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.
# August 24, 2004 7:18 PM

paulb said:

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."
# August 24, 2004 7:27 PM

Web Dillies said:

From Robert McLaws Blog: Provider Model Misconceptions From Michael Tepers Blog: More on MembershipProvider

# May 28, 2007 11:13 AM