More on sharing a config file

by Matt 27. September 2006 15:36

A couple of people commented on my post about sharing a config file between multiple developers.

Firstly, wow. I've had comments. That's nice.

Secondly, I don't think I articulated the problem too well. The title didn't help - it's not about working with multiple configuration files, or working with a config file across multiple environments, but on how to work with one config file that's being used by multiple developers with different needs. As an example, I want the web.config file to use a Microsoft Access based profile provider, while another developer wants to use a Sql Server profile provider. We both can't be right. We also can't both change the same file in source control, or there'll be trouble. The solution was to store the default config file under a different name and customise it per developer, with the customisations not included in source control. The post has the details.

Another comment was to ask why not use the configSource attribute? This attribute applies to any section element (i.e. xml element) and means that the contents of the xml element come from an external file. This sounds ideal, except it replaces the existing element, rather than overriding if the file is present. Since it replaces, the external file must exist, and we're back where we started.

(This raises questions on how useful this attribute is. It means you can make changes to a file and not have it restart the appdomain, you can set write permissions to a file that contains the appSettings section, so you don't overwrite existing config, and you can encrypt sections of the web.config by encrypting the external file.)

Similarly, there's a "file" attribute on appSettings, which does pretty much the same thing. It is more flexible, though - because you're dealing with a name/value pair collection, you can add to, remove from or clear and replace the web.config's appSettings section. It appears to be a hangover from .net 1.1, and unfortunately only applies to appSettings (which rules it out in my case - I need to change a child of system.web).

Hope this clears things up.

Tags:

Missing Vista Feature #1 - Notifications

by Matt 22. September 2006 18:22

The Windows Vista Team blog recently posted about Preventing Interrupted Presentations with Windows Vista, and it reminded me of something.

Back in the day, Longhorn (as it was known then) was going to have a unified Notification API. It extended XP's balloon notifications with a much more versatile interface, something more like the incredibly flexible TaskDialog API. It would allow you to build a notification window that did pretty much what everyone's custom "toast" solutions do now - provide info, respond to button clicks, timers, you name it.

I've snaffled a few pictures that I could dredge up from the interweb. I did have one that came from the long-gone UI guidelines, but that appears to have vanished somewhere.

Update: Found it!

Here are a couple from a codeproject article, showing the notification itself and the list of all notifications from a very early build:

Then a view of the notification from the 4074 build from the forums on shellrevealed:

And finally, a dodgy yet rather pretty version with glass enabled, which appears to be from aeroxp.org, but I can't find it on the page (here's the pic). It appears to have been taken at PDC 2003:

Unlike the roll-your-own solution, XP's balloon notifications will ensure you only see one at a time and will delay the notification until you're around to see it. Longhorn was going to take that further and queue up notifications in the sidebar when you were busy.

Paul Thurrott wrote about it in a review of Longhorn 4051. You'll need to scroll down to the bit about "Sidebar parts, alerts and notifications".

Longhorn had it. Vista doesn't.

Instead, according to the Vista blog, the best we get is this:

Windows Vista’s presentations settings block 2 types of notifications:  system notifications and Windows Live Messenger notifications.

And Outlook and Office Communicator don't work with this. James Senior has a couple of interesting posts, one looking at presentation mode from the end user experience and the other one on how developers can join in the fun

Not quite as grand as the original Longhorn plan, eh?

Tags:

Working with multiple conflicting config files

by Matt 11. September 2006 18:26

One of the luxuries of working in a corporate environment is the uniformity (and I never thought I'd say that). You're in control of just about everything - you can ensure that all developers are using the same configuration. You can all use the same databases, the same web services, the same web sites.

Working on a distributed, open source project is entirely different. You're not in control of the environments being used. Different developers will have things set up differently. And all this is reflected in different web.config files.

There's just one teensy-weensy problem. The web.config file is under source control, which means there is only One True Copy. And if I change it to get my site working, I change it for everyone else, and break theirs.

Of course I'm talking about SingleUserBlog, and of course, this is a problem we've hit. We're all using a different config to each other, and I've already checked in a config file that won't work for everyone else.

So I've had a play around and I've come up with a solution. It's teetering dangerously close to over-engineered, but it also does the job nicely, and I learned a few things to the bargain.

Since SingleUserBlog uses Web Application Projects, it's an msbuild-based project. And the nice thing about msbuild-based projects is that you can edit the project file, add your own tasks in and Visual Studio will still quite happily run with it.

The idea is quite straightforward. Get rid of the web.config file. Create a default.config file that contains the default settings for the project - the one you'd be happy to release. Then, change the .csproj file to copy this file to web.config and use the ReplaceConfigSections task from the Web Deployment Projects add-in to replace any sections in the file with xml from external files.

The implementation is perhaps a little awkward, but it's not exactly horrible either. First, in the main .csproj file, import a MergeConfigFiles.targets file that does the heavy lifting. This new file needs to set up a UsingTask to include the ReplaceConfigSections task. It also needs to set up a few default properties, such as the name of the config files to copy ("default.config" to "web.config"). It then imports another .targets file, if it exists. This one is $(USERNAME)-MergeConfigFiles.targets. If it doesn't exist, the default.config file just gets copied, unchanged to web.config. If it does exist, it should be another msbuild project that simply sets up an ItemGroup containing ConfigReplacementFiles items. Each item points to a file that just contains xml that will replace a specific section in the web.config file. That section name is defined in the Section metadata item in the ConfigReplacementFiles item. The ItemGroup is fed into the ReplaceConfigSections task.

When a build is run, Visual Studio will copy across the default config file, then process this copy, replacing all the sections listed with the contents of all the files listed. If any of the files change, or if the default file changes, it gets rebuilt. It even deletes the web.config file on clean.

There are a couple of issues with this method, but I don't think any are showstoppers. It's perhaps not ideal to have to define this with an msbuild project file, but it's not difficult. Here's an example:

<Projectxmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <ItemGroup>
    <ConfigReplacementFilesInclude="compilation.config">
      <Section>system.web/compilation</Section>
    </ConfigReplacementFiles>
    <ConfigReplacementFilesInclude="authentication.config">
      <Section>system.web/authentication</Section>
    </ConfigReplacementFiles>
  </ItemGroup>
</Project>

Not exactly difficult. Another drawback is that if you change this .targets file, the changes are not reflected until the project is reloaded. Not ideal, but not major, either. Oh, and it will only work for web.config, too - the web deployment project task assumes that. Supporting app.config wouldn't be too tricky either, but would require a custom task.

One drawback that could actually cause problems is ensuring that your overrides don't override something new, or you only add new things to your overrides, but this is really just something you need to keep track of.

Another interesting drawback is that asp.net will automatically recreate the web.config file for you if it gets deleted. Somehow it knows to call the .csproj file and get it created correctly - well, mostly correctly. It did generate some things incorrectly while testing that, and it always did a full build after that anyway, so everything still worked correctly.

The only files that get into source control are default.config and the initial MergeConfigFiles.targets. The username based file doesn't get added, and neither do the local changes.

It might be easier to download an example and actually try it out. Let me know what you think.

Tags:

SingleUserBlog

Mozilla Hires Microsoft Security Exec

by Matt 7. September 2006 16:30

So, in a bid to check out the "Blog This" from RSS Bandit plugin (running in my copy of Sharpreader) - what do you think the Slashdot boys are going to say about this?

Mozilla strengthened its commitment to security with the hire of a former Microsoft executive who was responsible for some of the security functionality within Windows XP SP2 and Windows 2003. Window Snyder will head Mozilla's long-term security strategy planning.

I thought Mozilla had better security than Microsoft? Right?

Source: Mozilla Hires Microsoft Security Exec

Tags:

Find as you type for Internet Explorer!

by Matt 6. September 2006 16:30

At last, someone's gone and done it! Core Services have released anInline Search toolbar for Internet Explorer. It's lovely - just as nice as Firefox's find as you type toolbar.

It replaces the horrible modal find dialog with an explorer bar at the bottom of the screen. Start typing, and it does an incremental search. The text box turns red if it can't find it. Simple. Lovely. It's already changing the way I use IE, and it's only been installed 10 minutes! Lordy.

(Gosh, just noticed there's another one!)

Of course, this is one of those things that's been sat in the mile long list of Things I'd Like to Write If I Ever Get AnyTime - now I don't have to! But it reminded me of something that I had wanted to investigate when I did get round to it. I remember playing around with Browser Helper Objects and finding something interesting. Time for some good old fashioned COM spelunking...

When you open the find dialog in IE, you get a PropertyChanged event raised with the name "__IE_FindDialog". Getting this property reveals an IDispatch pointer. Dumping out the type info for this interface reveals DispHTMLWindow2. Which means that the find dialog is an HTML dialog. With an HTML DOM (you should be able to get it from DispHTMLWindow2->document). Once you've got the DOM, you can get or set the value of the text box.

All highly undocumented and probably not a good idea to use, but very interesting all the same.

And why is this useful? Well, I think this is part of the implementation of the NavigateAndFind method. I'm sure there was a search function somewhere (my memory fails me on this now) perhaps in an old IE version's search bar or the HTML Help viewer, that displayed a list of matched documents. When you selected the document, it was displayed, the search items were highlighted, and the find dialog was automatically populated with the search items, when it was displayed.

It would be nice to leverage this kind of thing in the inline search toolbar. When you search in IE, it stashes away the search terms so that the inline search bar could default to them when selected. Of course, setting them in a find dialog that's no longer used is a bit daft.

So, all in, a bit of esoteric, useless knowledge.

I had fun with this, anyway.

(Via Paul Thurott's Internet Nexus)

Tags:

WebParts on Master Pages

by Matt 6. September 2006 16:16

Last time I spent a very long time griping about how bad WebParts' PersonalizationProvider is, and how it doesn't support web parts on master pages. And I started off the post by saying that SingleUserBlog does just that. What gives?

Out of the box, the PersonalizationProvider doesn't support it, but it's really quite easy to add it. It's just not nice.

This is what Darren had already done in SingleUserBlog; derive from SqlPersonalizationProvider. Override the horrible protected methods I was grumbling about last time, and just call the base method, substituting the proper path with a constant string. Simple.

But what happens when you want to add a new provider? For example, an MS Access based one? When you add this provider, you add a runtime dependency to this provider. As soon as you derive from this protected method, you've added a compile-time dependency, and that's a whole different kettle of fish, and generally not something you take on lightly - after all, what happens if you also want to support SQLite, or just plain old xml? You can't just keep adding dependencies. On top of that, the MS Access providers are all sealed classes - you can't derive from there. (Fortunately, they come with source. Dodged the bullet this time.)

I came up with a slightly different, but only marginally better solution. Look at the docs for PersonalizationProvider and follow along at home. (And read the remarks section. This alone should show you that the class is messed up.)

I created a class that derives from PersonalizationProvider and takes as a parameter the name of the real PersonalizationProvider to use. It uses this name to look up the real provider in PersonalizationAdministration.Providers[]. It then defers nearly all methods to this provider. It allows the base class to handle load and save and overrides the dodgy protected methods. Here's where it gets messy.

When LoadPersonalizationBlobs is called, it uses reflection to call into the real provider and call it's implementation of LoadPersonalizationBlobs. The same happens for SavePersonalizationBlobs.

This works as well as the SqlPersonalizationProvider derived class. It avoids adding dependencies, and copes with sealed classes. But it's fragile - if the provider doesn't implement this hidden, protected API, you're stuffed. If your asp.net host doesn't allow reflection, you're stuffed. And above all, it's using reflection! Another bad smell - if you've had to use reflection to do something, you've probably already gone wrong.

So, what's the proper solution? Well, due to the messy design of the PersonalizationProvider, there isn't one. To support this very useful situation, you need to hack it. It's just a case of deciding how you want to hack it.

Because the PersonalizationProvider is responsible for selecting the path and username to save against, you have to create a new provider, which is a shame. You could make it slightly nicer by having it ask the WebPartManager (well, a derived version) that's sitting on the master page, what the values should be. But you still need a new provider.

I think SingleUserBlog's best choice is to use three methods. First, keep the SqlPersonalizationProvider derived class. You're deriving off a class in System.Web, so you're not adding any dependencies. Secondly, the reflection based method will work with what I suspect will be the majority of custom providers out there.

But my preferred choice would be to create a new provider from scratch for Sub. One of the main reasons I started using Sub was because it didn't use Sql Server for the blog data. So Sql Server shouldn't be the default provider for personalisation (or anything else) in Sub. It would be much nicer to have a simple xml based provider. This kind of provider wouldn't be much good in most other web apps - it won't exactly scale to a large site with loads of pages that loads of users could customise. But we know the usage it will get in Sub - one web page, customised by one user. Xml file should be able to cope with that. Given these requirements, I have no problem creating a Sub-specific provider for this.

Brendan's even got a head start on this (although it's not in the current codebase).

Tags:

Asp.Net API Hall of Shame: PersonalizationProvider

by Matt 4. September 2006 18: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:

SingleUserBlog goes Open Source!

by Matt 2. September 2006 18:16

After the gentlest of nudges, Darren's put SingleUserBlog on CodePlex. This should be fun. I've got a load of little changes that I want to submit, I just have to decide what goes first. A nice position to be in.

Unfortunately, setting up the new version really gives me what should be my first change. And it's not going to be an easy one.

One of the things that got me using SingleUserBlog in the first place was that it uses xml as the blog data store - my web site doesn't come with access to Sql Server, so that suits me down to the ground. But it *does* use Sql Server as the data source for Web Parts and the "remember me" part of the comments.

I've gotten around this by using a set of providers that use a Microsoft Access database, but there's a litle glitch with the Personalisation Provider (which I've got a nice big ranting post about this for later) that makes me really reluctant to submit them. I've got a bit of a hack that gets round it, but I'm sure there has to be a better way. And it's a bit clunky using Access, too. It would be much nicer if we could have a completely xml based install. Brendan created an xml based Personalization Provider that handles the issue, so perhaps this is the answer. It's a very specific provider, in that it will only work the exact way SingleUserBlog currently uses it, and again, that makes me think there should be a better way.

I'll have a think and write that nice ranting blog about rubbish APIs and see what happens.

Tags:

SingleUserBlog

Month List

RecentComments

Comment RSS