# Friday, June 06, 2008

Visual Studio Documentation - Have your say

Brad A's just been highlighting the MSDN Survey to get opinions on how Visual Studio and MSDN documentation works (or doesn't) for people.  I added my 2c and it made me think a little about how I access 'help' these days.  Here's what I wrote in the 'other comments' (Q 14 I think).

I generally access MSDN content through google (as it's quicker than accessing the MSDN site, waiting for it to load the TOC, then searching).  It's probably testament to the indexing of the site that a search such as "msdn Path.Combine" will take me straight to the specific page I 'know' I'm looking for.  I guess this means I've got some knowledge of how things are structured and I use that to good effect.  In a simple comparison...

Local Help
Typing Path.Combine into VS.NET code editor - selecting the text and hitting F1 came up with a false start (my current machine doesn't even have the docs installed apart from Enterprise Library 3.1.  It did find some less than useful reference from EntLib!).  I went to Help options and chose 'use online first', and tried again (incidentally I didn't even realise you could pull in your own list of sites to search (Codezone community) - cool).  It chose a different 'Path property' first and took about a minute in total to get to the right 'Path class'.

MSDN Library Site
Opening up the MSDN site (which still feels too heavy in my book - and now curiously like BBC news) and searching for 'Path.Combine' took about the same time (1 min).  This includes opening the browser, loading up the MSDN home page, searching, clicking the first item in the search results, and loading that page.

Google search on MSDN
Opening up Google (admittedly my home page - but I'm looking for speed here) and searching for 'msdn Path.Combine' took 20 seconds.  The first item in the list was what I wanted so got straight to it.  It's also worth noting that Google's become a little fat puppy too with all my iGoogle stuff on it, but it's still way quicker than any of the alternatives.

 

posted on Friday, June 06, 2008 4:44:22 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, June 04, 2008

When is XHTML not XHTML?

Well you may think it's just a matter of rendering some well-formed markup and setting your doctype...

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">

right?  Wrong!

Different browsers render in different ways.  What's common however is that unless your response's content type is "application/xhtml+xml" then you may not pass go and pick up $200.  You can force the browser to recognise the content type in two ways (in addition to the DOCTYPE declaration above):

  1. Name your file with an .xhtml extension (not really a solution as IE says 'what?')
  2. Set Response.ContentType = "application/xhtml+xml" (See here for more info)

The DOCTYPE is really an additional information item and not something modern browsers do much with on its own.  So how different are the browsers'  XHTML implementations?

Internet Explorer for instance takes the 'all comers' approach and will do the best it can given the markup, whilst degrading gracefully if it encounters errors.  great! I hear you say - saves me from actually testing this thing!

Firefox on the other hand will use a completely different parser once it knows you want to serve well-formed markup.  The good thing about this is that you immediately see any errors and the page won't render if there's invalid markup.  IE meanwhile continues to let you believe you're an XHTML master (note: it doesn't recognise the xhtml extension).

Firefox has some good resources about supported features.  A common one that catches people out is Javascript's document.write.  This isn't allowed in XHTML as the string input can't be guaranteed to be valid XML. 

Safari's not quite as advanced yet as Firefox's support, but it too will properly validate your markup and report errors.

If you really want to stick your neck out then place a link on your site to validate against the W3C's standards.  You're likely to get plenty of errors - like this page!

Other things to watch out for are Content Management systems that allow you to enter non-compliant html in text editors, and simply not having JavaScript blocks in CDATA sections).

 

posted on Wednesday, June 04, 2008 10:43:12 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Monday, June 02, 2008

Enabling Javascript GZip Compression in IIS6

Yes - I couldn't be bothered to read through the Microsoft docs on this, so got what I needed from here.

posted on Monday, June 02, 2008 10:47:24 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]

Using RSS.NET to re-write an existing feed XML file

Don't know what happened to RSS.NET.  Looks like it's trying to go commercial, but still very quiet.  Code examples are few and far between (with some simple ones on the site).  Also on InformIT.

I'm writing a quick RSS console app (as I lost the last one I wrote!) that I can use to write an RSS feed from a SubVersion hook.  Most people use a pre-existing tool for Python (can't remember the name), but I thought I'd give RSS.NET another go just for kicks.

The example above works fine assuming you're maintaining state somewhere other than the feed xml file itself.  The example also assumes you're serving this up to the web.  You can override the RssFeed.Write() method to take a filename.

If run the same code again (assuming you've written to a file) it will simply overwrite it with one item (not add to it).  This isn't what I wanted so...

You need to

  1. Read the file back in if it's there - otherwise create
  2. Add your item to the existing channel if it's there - otherwise create
  3. Fix the date behaviour as RSS.NET always assumes UTC dates and appends 'GMT'.  The problem here is that if you're in Australia (like me) reading and rewriting the same items will effectively add several hours on to existing items every time, because you write the date you read back in for existing items (read and parse into region-specific date, then write back as is).  There's two ways to fix this:
    1. Before you add your new item - loop through all items and change the item.PubDate to item.PubDate.ToUniversalTime().  This effectively sets it back to the 'correct' date.
    2. Change the RSSWriter class in RSS.NET to convert ToUniversalTime for the Item.PubDate, Channel.PubDate etc.  This seems like a better option, but it has potentially more knock on effects in RSS.NET.  I'm here to achieve a result, not change the behaviour (possibly adversely) of RSS.NET so I chose option 1

So here's the code.  Not finished yet and rough around the edges, but works as I need.  The intention is to avoid the need for config files and configuring up of feeds specifically.  I just want a library function that's called by a console app.  The web serving will simply be based on the location of the file and pointing to some folder in IIS.

        private static void WriteFeed(string feedFileName, string feedName, string feedDescription, 
        string feedURL, string itemTitle, string itemDescription, 
        DateTime itemPublishDate, 
string itemURL)
        {
            
bool newFeed = false;
            
//Try and first open the feed (to see if it's existing)
            
RssFeed feed = null;
            try
            
{
                feed 
RssFeed.Read(feedFileName);
            
}
            
catch (FileNotFoundException ex)
            {
                feed 
= new RssFeed();
                
newFeed = true;
            
}
            
catch (Exception ex)
            {
                WriteError(ex)
;
                return;
            
}

            RssChannel channel 
= null;

            
//Loop through all channels and if we've got the same title reuse
            
for (int 0i < feed.Channels.Counti++)
            {
                
if (feed.Channels[i].Title == feedName)
                {
                    channel 
feed.Channels[i];
                    break;
                
}
            }

            
if (channel == null)
            {
                channel 
= new RssChannel();
                
feed.Channels.Add(channel)//might blow up if already there?
            
}

            RssItem item 
= new RssItem();

            
item.Title itemTitle;
            
item.Description itemDescription;
            
item.PubDate itemPublishDate.ToUniversalTime();
            
item.Link = new Uri(itemURL);

            
//To ensure we don't screw up existing dates - convert to UTC
            
foreach (RssItem existingItem in channel.Items)
            {
                existingItem.PubDate 
existingItem.PubDate.ToUniversalTime();
            
}

            
//Now add our new item
            
channel.Items.Add(item);

            
channel.Title feedName;
            
channel.Description feedDescription;
            
//channel.LastBuildDate = channel.Items.LatestPubDate();
            
channel.PubDate DateTime.UtcNow;
            
channel.Link = new Uri(feedURL);

            
feed.Write(feedFileName);
        
}

Colorized by: CarlosAg.CodeColorizer

 

 

posted on Monday, June 02, 2008 9:28:25 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Thursday, May 22, 2008

Getting the most out of ASP.NET Web Deployment Projects

In my ongoing love (but mostly) hate relationship with ASP.NET Web 'Site's' I've been using Web Deployment projects to make things more bearable. 

I currently swap in connection strings from 3 files - one for each build configuration (debug, test, release - connectionstrings.debug.config etc ).  This works fine as per the doco on WDP.  I use CruiseControl.NET and NAnt to automate builds, and a few nagging 'automated' pieces were missing from the puzzle. 

  1. Encryption of connectionStrings (or other web.config sections that you want to protect) - without affecting the 'source' file.  I've assumed here that 'internal' people are trusted. 
  2. Changing of other config stuff (like debug=false) in the test and release builds.  (My attempts to get this to work had previously failed as you don't seem to be able to specify system.web as a replaceable section.
  3. Encrypting Forms authentication passwords, using MD5 hash.  This isn't difficult, I just didn't have a tool to generate the hash value.

Encryption of config sections

OK - after re-reading Scott Gu's post on Web Deployment Projects, and K. Scott Allen's post on how to simply encrypt sections of config files, I realised that I could just add a post-build event (manually) in the wdproj file (right-click in solution explorer --> open project file). 

The build events are already in but commented at the bottom of the file.  I ended up with

<Target Name="AfterBuild">
<Exec WorkingDirectory="$(OutputPath)" 
Command="C:\windows\Microsoft.NET\Framework\v2.0.50727\aspnet_regiis -pef connectionStrings ."
/>
<!-- Also remove our 'source' config files using the del command rather than the delete task as you
have to jump through hoops to specify wildcards -->
<Exec WorkingDirectory="$(OutputPath)" Command="del compilation*.config" />
<Exec WorkingDirectory="$(OutputPath)" Command="del connectionstrings*.config" />

</Target>

If I put this anywhere other than 'AfterBuild' it didn't seem to do anything.  I certainly learnt a bit more about aspnet_regiis, as I'd only used it previously to 'install' ASP.NET.  I also had to specify the path to aspnet_regiis, but you could obviously use a property for this (I'm new to MSBuild - only dipping in when I have to, so the framework path may already be a standard property?).

Replacement of system.web sections

The key thing here (which I don't believe was documented very well anywhere) is how to replace system.web elements.  Other typical replacements - e.g. appSettings or connectionStrings are children of the root config element.  You'd therefore assume that you need to replace the whole of system.web (which is a little inconvenient - but still worth it).  This doesn't work and you'll get a 'nice' WDP00002 error saying it can't find the system.web element (a bit like saying 'can't find printer' when it's right next to the computer!). 

You just have to go one level down as follows (in the Deployment --> Web.config file section replacements property page):

system.web/compilation=compilation.release.config

compilation.release.config may be as simple as...

<compilation debug="false"></compilation>

You might have a warning saying 'compilation' isn't a valid element, but this is just the intellisense barking as it validates against the config schema.

Encrypting Forms Authentication Passwords

This is pretty simple and there's lots of docs to support this, but I wanted a simple tool to generate the hash for a given string, and a quick google yielded a nice little command-line tool...

This way you can plug it into your build if you need to, but also replace for different environments using the techniques above.


posted on Thursday, May 22, 2008 9:57:27 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Monday, April 21, 2008

ASP.NET Web Site Project (WSP) vs Web Application Project (WAP)

ASP.NET 2.0 originally came without support for the 'traditional' Web Application project that everyone had become familiar with in ASP.NET 1/1.1.  Some people liked the new approach as it affords more 'on-the-fly' updates, but obviously enough people 'didn't' for Microsoft to release a patch for Visual Studio .NET 2005 to allow creation of WAP's.  The support was then formally re-added in VS 2005 SP1 (making the original patch redundant).

Info on the situation can be found here.

This post (as well as saving the links for me) is also a reminder of the limitations of the Web 'Site' model (hereafter known as WSP) and why I choose not to use it.

  • Namespaces.  WSP does not explicitly add a namespace to any page, class etc.  It uses 'special folders' with some implied names (e.g. App_Code) to determine the namespace hierarchy.  This whole situation can also lead to strange 'circular reference' errors with user controls - especially after converting from VS 2003.  At the very least you'll be pulling your hair out wondering why you can't reference some page or control from somewhere else in your site.
  • Code Reuse.  Only code in the App_Code folder (and below) can actually be referenced by another class in the project.  This forces a structure that you wouldn't otherwise choose.  You can of course create separate assemblies - and should in many cases.
  • Unit Testing support in VS 2005.  WSP does not build to a single assembly when built in VS 2005, and must be 'precompiled' using a Web Deployment Project which in turn uses the asp_merge (publish) utility in order to achieve this.  The standard publish function doesn't support a single assembly, although it's possible to get the App_Code into a single DLL. This all means that because you don't have an output at build time, you can't run unit tests in the WSP - regardless of how much code you're 'reusing' in the App_Code project.  You can jump through some hoops to call the NUnit Console runner, but why bother!?   
  • Included/Excluded files.  Because WSP doesn't have the concept of a project file to say what's 'in' and what's 'out', VS 2005 uses a rather nasty 'rename' method of excluding files - simply suffixing the file with .exclude to denote it should be disregarded.
  • References are actually just copied in.  If you create a reference to an external assembly, VS 2005 will actually just copy the file into your bin folder.  This means you'll end up putting all sorts of binaries in your source tree (under source control), that you maybe otherwise wouldn't
  • Automated Build.  NAnt and other automated build tools can't work out whether a Web Site project is some sort of 'enterprise template' project, or a tub of lard - because it's not really a project.  This means that you can't use the <solution> task with NAnt.  you have to call a custom <exec> task instead, calling the asp_merge.exe tool, then call all other projects separately too.  This all works, but again, why bother?  A cynic might conclude MS was trying to cause issues for NAnt, whilst getting people interested in MSBuild.

For me, any change that introduces new 'non-standard' tools, just so you can have the convenience of 'on-the-fly' updates just isn't worth it.  You'll actually find that this all goes out the window when you deploy to a server anyway, because depending on how you ran asp_merge, you probably won't be able to do any 'real' updates because the assembly names are generated in that process and you'll break the site by changing source files.  In my opinion if you are using this approach then use the Web Deployment project and build to a single assembly to minimse these issues.  If you've got any sort of structure around your production deployments (and you can't just 'copy over') then I see literally no advantage in the WSP model, as it just seems to provide too much pain for no visible gain.

posted on Monday, April 21, 2008 12:09:18 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]
# Wednesday, April 16, 2008

ASP.NET Open Source Content Management Systems - The Winner

Well, it's been a little while since I first talked about Content Management Systems, but I can declare that

mojoPortal is the one for me.

There's a number of reasons, but I'll go through the main pro's than means it wins out over DotNetNuke

  1. It's XHtml compliant out of the box, and has minimal use of tables.  All the people producing skins for MojoPortal are also well evolved along the accessibility curve too.
  2. It uses standard .NET features.  This is due in no small part I'm sure to the fact that it started after DNN and more facilities were around in ASP.NET at the time.  This means that Skins, Themes, Master pages are all present and correct with MojoPortal, rather than the proprietary (but well documented) skins in DNN.
  3. It seems to be quick(er than DNN)
  4. The facilities are stable and work very nicely.  There are less modules, but the developer video tutorials are a really good way to get yourself into developing your own modules and edit pages.
  5. You can get involved and work with the code - after downloading from SVN (I fixed a small bug and felt really good about it!)
  6. Finally - and this is an easy clincher - the support for the product is just fantastic.  Joe Audette is like a man possessed on the forums, and you're likely to get a really well thought out answer to any query within a few hours (depending on the time zone!).  He's sorted me out on a few occasions and set me along the righteous path. 

In summary, mojoPortal is something I'm happy to be using for a client as I'm more keen to use standard .NET features than proprietary skinning.  I also like the philosophy more of the development community, and how Joe Audette's running the project

posted on Wednesday, April 16, 2008 5:10:30 PM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]

Tree Surgeon - Automatically creating a project source tree structure

Found Tree Surgeon on CodePlex.  Its implementation's actually a bit specific for my use, but has some nice ideas (based on Mike Roberts' articles on setting up a .NET Development tree

I'll keep it to hand I think...

posted on Wednesday, April 16, 2008 8:18:36 AM (AUS Eastern Standard Time, UTC+10:00)  #    Comments [0]