Asp.Net API Hall of Shame: PersonalizationProvider

by matt 4. September 2006 23:16

This is a post I've wanted to write for a while. And I wrote it last night, and got it all wrong. Long winded, rambling and not really saying what I wanted to say. So I've got rid of that one, and let's try again.

SingleUserBlog uses WebParts on it's master page. This is a brilliantly simple idea that allows every single page on the site to share a common, personalisable layout.

The only problem is that WebParts don't support this.

Or more accurately, the PersonalizationProvider doesn't support it.

Allow me to introduce you to a big offender in the API hall of shame - PersonalizationProvider. This is the base class for the provider model used in the persistence of web parts. It saves layout and content changes made to a specific page, for a specific user (if logged on).

It's first offence is a poor separation of responsibility. Considering that the WebPartManager is in charge of, well, managing web parts, you'd be forgiven in expecting it to tell the personalisation provider what data to persist, what page it was for, and who the user was. Similarly, to get the data, it would ask the personalisation provider for what it had saved against a specific page with the current user. Master pages would be dead easy, then. Simply have the WebPartManager pass a global key for the page. Job done.

Since we're talking Hall of Shame here, you know that isn't what happens. The PersonalizationProvider is responsible not only for persisting the data (and reading it back in) but also for deciding what the unique keys are! (And it uses internal System.Web methods to do that, too.) Reading data back into the WebPartManager now occurs via telepathy - the PersonalizationProvider just knows what the WebPartManager wants.

Second offence? Too much has been pushed up into the base class. This is supposed to be the base class for all providers that deal with web part data persistence. And yet it has two API's. The first is the public API that the client (WebPartManager) calls to load and save the data, the one we'd like to see the path and user name passed into (Tell Don't Ask). This is the only API you should need to override in your provider implementation.

The second API is a couple of abstract methods that are expected to be used by provider implementations; they load and save the data formatted as a binary stream. These methods are what are actually overridden for the provider implementation. Interestingly enough, these abstract methods pass the page and username about. This should have caused some alarm bells to go off that the proper (public) API wasn't right.

What if you want to format the data differently? E.g. split it up and save it as plain text so you can search across it? Well, you can't. The base class, despite it's public API using the PersonalizationState class all over the place actually casts that to an instance of BlobPersonalizationState (which is internal and sealed so you can't override or even new it up) to do the formatting and pass the data on to the derived class to persist. Shocking.

Not to worry, You can just override the virtual public API and implement everything yourself, including finding the path and username. Except now you also have to implement that abstract protected API - even though you're not using it at all!

If the base provider class had been defined as an interface, or more derived classes had been implemented I'm sure this design wouldn't have happened. Better separation of responsibility and composition of helper classes would have sorted this right out.

A much cleaner design would have the base class as an abstract class. The SqlPersonalizationProvider (or any other) would get told the path and username. It could then call out to a helper class to get a binary stream to persist (being a helper class, other providers that only wanted to change the persistence store could be easily created). If you wanted to format the data differently, you couldn't use the same store, so you'd have to implement a new provider and you'd just use a different helper class (or format it directly in your new provider).

Is there a third offence? Yes, but this one has accomplices. Fire up Reflector and try and figure out how web parts data gets saved and loaded. It's an absolute mess of spaghetti. Just one example is that the WebPartManager creates a WebPartPersonalization item that uses BlobPersonalizationState that's the internal sealed class that handles the formatting that the PersonalizationProvider is using. It looks like a very confusing class structure that doesn't really know who has what responsibility and knows too much about the implementation details of the other classes, and assumes that the data will be formatted in such a way and used with the SqlPersonalizationProvider.

And the big problem? I want to use web parts on master pages in SingleUserBlog. Next time - how we can solve this.

Tags:

Comments

6/10/2009 11:50:44 AM #

Franchises for sale

It's interesting, the blog engine platform seems very variable in form.  My design skills are not so good as my C coding though, I would be interested in seeing what additional skins you can get for it.  Nice blog btw, best wishes for it and keep up the posts. Smile  Kind regards,  Peter sims.

Franchises for sale United States | Reply

Add comment


(Will show your Gravatar icon)

biuquote
  • Comment
  • Preview
Loading



About the author

Something about the author

Calendar

<<September 2010>>
MoTuWeThFrSaSu
303112345
6789101112
13141516171819
20212223242526
27282930123
45678910

View posts in large calendar

RecentComments

Comment RSS

License

Creative Commons License
Except where otherwise noted, content on this site is by Matt Ellis and is licensed under a Creative Commons Attribution-Share Alike 3.0 Unported License.

©2010 Matt Ellis