Tuesday, 16 March 2010

C# var recommendations

With a recent move to .NET 3.5 we’re beginning to review our coding standard and consider some of the things C# 3.0 makes available to new and experienced developers alike. One of these things is the contentious var keyword.

‘var’ seems to instil fear into some developers with others viewing it as total awesomeness. Like all things, var has its place for storing anonymously typed objects returned from the likes of a LINQ query but can it really be misused?

While var does at first glance appear to be a merger of JavaScript and VB with C#, it’s important to note the use of the var keyword must comply with the following:

  • A variable declared using the var keyword must be initialised within the same statement
  • An object with a different type cannot be assigned in a subsequent statement
  • Variables declared with a type of ‘var’ are strongly-typed and compile-time checked; instead of having to determine the type yourself, the compiler does it for you
  • Intellisense will continue to work wherever the variable is used
  • The var keyword can only be used for local variables within a method

Commenters responding to a blog post by Steven Wellens on the use of the var keyword also cited an improved refactoring experience; I partially agree with this point however Visual Studio’s refactoring tools largely take care of those problems.

The main reasoning by commenters was for (and against) readability and I agree strongly that using var reduces “noise” words in the code. In my view, this improves the ability to easily scan the code.

The example statement Steven supplied was claimed to be unreadable—but primarily for the poor name chosen for the GetData method:

var data = GetData();

If the method is renamed, this does remain a gray area unless it also includes type information in the name, which is yucky. Removing type information does allow intent to shine through of course—not a bad thing.

With this preface, here are the draft recommendations for use of the var keyword in our shop:

  • Do declare the type if it's useful otherwise use 'var' to avoid "noise" where type information is unimportant or can be read in the object's initialisation. Eg: var cat = new Cat ();
  • Do use 'var' to highlight intent over implementation detail
  • Do use 'var' as required for anonymous types
  • Consider readability; avoid examples like var data = GetData(); where the type returned by GetData() is not obvious

References

Use EnableValidator to Manage an ASP.NET Validator in Client Script

I’m a big fan of the ASP.NET validator controls—not because they’re complete and wonderful but because they’re convenient when they work as expected and otherwise keep me on my toes when something out of the ordinary is required.

Today brought forth a requirement to hide the State field on a form when the country wasn’t set to Australia. Because the State field also has a RequiredFieldValidator attached, I would have to disable the validator server-side in some cases during the initial page load (depending on the data being pre-filled) and disable it client-side as the user interacts with the form. Failing to do so would prevent the form from posting back when the State field was hidden.

One of the main reasons I like these validation controls so much is because of the way they just work client-side and server side; in the client arena, everything occurs as Javascript and that was enough for me to assume, in this case, I could manage the validator through Javascript. I wasn’t certain this would be possible (or at least simple) but fortunately I won’t lose any sleep over this one tonight ;) As I found out, changes to a validation control in one context are even reflected in the other—nice!

The magic all happens with the mysterious ValidatorEnable(validator, bool) function. I have no idea where this function comes from and don’t really care but it allows you to enable or disable a validator in Javascript code by supplying the validator object and a boolean value indicating whether it should be enabled (true) or disabled (false).

It’s important to remember the first parameter is the validator object itself, not its ID. You’ll need to locate the validator object by ID (or using some other means) of course, but that’s where the ClientID property comes in handy:

var stateRequiredFieldValidatorId = "<%=rqdState.ClientID %>";

For additional information check out the ancient (circa 2002) “ASP.NET Validation in Depth” article on MSDN. Jonas Bush also has a concise example.

Monday, 15 March 2010

Retrieve just a URI’s path

Need to get the path component of a URI without the domain, port, or query string? Use Request.Url.AbsolutePath. Despite its name it returns a relative URL:

So http://blog.mediawhole.com/somepage.aspx?a=b becomes /somepage.aspx.

How to return an anonymous type from a method

Current .NET lore would have you believe you can’t return an anonymous type from a method but Tomas confirms it is possible by casting and cites the C# 3.0 spec:

Within the same program, two anonymous object initializers that specify a sequence of properties of the same names and types in the same order will produce instances of the same anonymous type.

Tomas also qualifies his interesting post with a request to let him know if a good reason can be found for using this technique!

In my own work I recently wanted to call a method that returns what I would ultimately implement as a data transfer object: a strongly-typed collection of structured, related data. I considered out parameters and collections but neither option really struck my fancy; a DTO would likely be the preferred option at the expense of introducing another class, maintenance overheads, etc.

In the hunt for a better, modern solution, the concept of returning an anonymous type appealed—apart from the problems anonymity introduces! While I understand how .NET constructs anonymous types and reuses those that identical, the one major problem I see with Tomas’ solution is the requirement that both “sender” and “receiver” not only know about the so-called anonymous type but actually define it in two places. This just seems like more overhead to me so I abandoned this approach before long.

Friday, 5 March 2010

Enable testing of web services

I’m bound to forever forget how to switch on (or off) the ASP.NET web service test screen and documentation options. For future reference:

http://msdn.microsoft.com/en-us/library/b2c0ew36(VS.85).aspx

or add this to the system.web section of the service’s web.config file:

<webServices>
        <protocols>
            <add name="HttpSoap12"/>
            <add name="HttpSoap"/>
            <add name="HttpGet"/>
            <add name="HttpPost"/>
        </protocols>
</webServices>

Wednesday, 3 March 2010

Fiddler redirects localhost. to IP address

Debugging local HTTP traffic using Fiddler, while not intuitive, is easily accomplished by adding a dot to your localhost address. In other words, http://localhost:1234 becomes http://localhost.:1234 and traffic is captured like it always is.

This wasn’t quite working for me this morning: localhost was resolving to the IP address of the host machine, without the port or directory bits. I’d earlier disabled capturing by clicking the “Capturing” button in the status bar and that seemed to be causing this behaviour; enabling capture once again and the old dot trick worked again.

Incidentally, after closing Fiddler, I noticed IE8 does the same thing so this is likely a browser behaviour rather than a Fiddler oddity.

Friday, 12 February 2010

Best Practice List Development in SharePoint

I had the honour of speaking on the subject of best practice SharePoint list development at last weekend’s SharePoint Saturday event held in Perth, Western Australia but I want to take this discussion one step further. I’m therefore creating this post to point to my slide deck from the event and as an index for follow-on, detailed posts about some of the items I had to gloss over. There’s precious little out there, in a single location, on the subject of list development guidelines and I’m hoping to change that. If you want to contribute what you feel is a best practice I’ve missed or challenge one of the practices I do have listed, please comment and join the discussion.

Slides are available here: Best Practices for Developing SharePoint Lists (PowerPoint | PDF)

Here are the two key resources I cite in the presentation:

Wednesday, 10 February 2010

YSlow Configuration “Use a Content Delivery Network (CDN)”

The westernaustralia.com site is served to users all over the world from the most remote city in the world: Perth, Western Australia. In other words, we target users in core markets across Australasia, Europe, and North America and while we expect a consistent homepage load time below ten seconds in all cases, Perth to New York, Perth to London, and, really, Perth to anywhere equates to high latency, congestion, and poor response times. Couple that with SharePoint, a rich, interactive site design, and an excessive number of page requests and you'll begin to appreciate why we chose to serve the site across Akamai's content delivery network.

I'll describe the setup in detail someday but for now, I've finally managed to convince YSlow the site is being served using a CDN; while we use better tools for global performance measurement (Gomez), seeing a low grade—in part because the hundred-odd requests were perceived to be served without a CDN—was starting to get me down ;) YSlow is a great plugin for analysing your site in Firebug but it's grading system can be a bit tough.

The YSlow FAQ will tell you how to configure the plugin to acknowledge the use of a CDN by setting a extensions.yslow.cdnHostnames preference. I won't repeat those details here but will expand on how the string value might be configured to tell YSlow a CDN is in use—whether you’re telling the truth or not. It’s surprisingly simple.

In our particular case, www.westernaustralia.com is a CNAME to an Akamai URL so telling YSlow to use akamai.com as a CDN doesn’t make sense and YSlow isn’t smart enough to figure things out on its own. Since www.westernaustralia.com is implicitly its own CDN, configuring the the extensions.yslow.cdnHostnames value as westernaustralia.com sorts everything out. I had to restart Firefox (3.5.x) and reload the page for things to take effect.

If you’re not using a CDN and want to turn off this particular component of your grade (or at least make it an “A”), simply add your own URL; regardless of whether you’re actually using a CDN, YSlow will think you are.

SharePoint 404: The resource cannot be found

If a page element is missing—either a user control hasn’t been deployed or something else that needs to be in place for the page to compile dynamically is awol—you might be the proud owner of a 404 even though the page itself does exist.

HTTP 404. The resource you are looking for (or one of its dependencies) could have been removed, had its name changed, or is temporarily unavailable.  Please review the following URL and make sure that it is spelled correctly.

After setting the SafeMode element’s CallStack element true in web.config, turning off CustomErrors, and setting the debug=”true” attribute on the compilation element, you’re possibly looking at a beautiful ASP.NET error message with no stack trace and not a lot to go on.

Well here’s a trick: view source on the error page and and scroll down below the closing HTML tag. Behold the stack trace! It should tell you exactly where to look for the problem.

Tuesday, 9 February 2010

westernaustralia.com Relaunch and Other Website Recent Activities

If you’ve been following the progress of the westernaustralia.com website over the years, you’ll already know the site was one of the first in Australia to be launched on the MOSS 2007 platform and has been consistently voted as one of the top fully branded SharePoint sites in the world ever since. Having worked on the site since the get-go and as the current technical lead for the project, I’m the proud owner of the site’s programmatic aspects; today I’m tickled pink to tell you, the SharePoint community, about yesterday’s launch of the new Tourism Western Australia brand, Experience Extraordinary, and, more specifically, the brand’s impact on the westernaustralia.com website.

At the moment we’re working with the Digital Marketing team on a two (or three phase) approach (depending on how you look at it) to implement the new brand. The first phase unfolded in August last year and involved the neutralisation of the preceding brand’s elements (The Real Thing) while the latest creative design agency, Host, was appointed. Phase 1 of the new brand implementation kicked off in the dev team a little over three weeks back and involved making the Photoshop mockups provided by Host into a living website. This work focused largely on the page banners (vibrant imagery, the tab, and extra height), the navigation, the background colour (trust me—everything is more complicated than you might think at first glance with wa.com!), what we call the ePostcards element, and the footer elements.

Here’s a picture of what I think is a visually stunning website and a vast improvement over its predecessor:

wacom homepage 4 Experience Extraordinary Phase 1

Phase 2 holds still more secrets but you can bet we’ll be making all the middle bits fit with the rest of the changes. One of the challenges we’ll be facing is getting the home page weight down from a whopping 1750KB and 100 requests. Stunning, certainly, but our CDN of choice (Akamai) allows us to get away with this sort of monkey business.

In addition to all the glam, we also took the opportunity to overhaul the heading infrastructure. This subsystem is designed to give content editors the ability to upload new banner images and all of the corresponding display data required to render the Flash banner. The Flash banner itself was built by Host from our previous banner and it takes as a parameter an XML file providing all the information it needs to display the banner images, text, links, and colours.

Obviously this data is stored in a list to which the FlashBanner.cs web part communicates at render time. Like I say, nothing in wa.com is as it first appears and one of the more complex requirements was allowing a subsite to override the banners displayed for for it and its children (in effect to support the major subsections of the site such as the destinations). Apart from simply walking up the site hierarchy recursively until a banner list is found, we then needed interrogate the list and emit an XML file acceptable to the banner; the URL to the file is finally cached with a dependency on the file itself.

Interestingly, this was one of the first times we started using event receivers on the banner lists to invalidate the cache when a banner is added, edited, or deleted; this keeps the content editors happy and productive and is one less of those annoying “is my banner cached in the browser cache | Akamai | reverse proxy| application cache | output cache | blob cache?” questions ;)

I’ll also point out we’re using a simple custom list for this instead of a picture library or custom list derived from a picture library. The previous version of this list was in fact a custom picture library but it suffered from broken thumbnails (which we now know how to fix) and proved rather cumbersome to use in practice (no reordering, a difficult view, etc). The move away from the picture library wasn’t, in retrospect, necessarily a great decision but it was probably the best decision at the time and came with considerable deliberation. The one major problem with the current approach is banner images must be uploaded separately to a real picture library and then referenced from the list; this is a bit of a double-up and disconnects the image itself from its metadata. As our understanding of list development and the peculiarities of these other list types increases, we may revisit this approach.

Also at a technical level, we recently move the site from our old IBM BladeCenter kit running Windows Server 2003 x86, MOSS 2007 SP1 (not even the Infrastructure Updates—gasp!), .NET 3.0, and a non-clustered SQL Server 2005 x86 database server. Everything was virtualised on ESX 4.0. We actually had three of these farms: one in prod, one in DR, and one for authoring; as content deployment never worked for the site, this meant a daily backup and restore of the content database from authoring to production and corresponding switch to or from DR (with DR actually being treated as a standby prod environment). That must have really sucked for our admin who had did that essentially manual task every business day since launch in May 2007. What. A. Drag.

From that setup, we moved to what was briefly the bees knees: Windows Server 2008 x64, MOSS 2007 SP2 + June 2009 CU, .NET 3.5 SP1, and a clustered SQL Server 2008 x64 database environment. Everything is still virtualised on ESX 4.0 and the entire setup is mirrored in DR. We’ve finally done away with the authoring farm so content editors are editing content in the production farm (we’re working towards a tiered security design and approval workflows, by the way, but our content editors have been working with the environment for years now and are mature in their understanding of how not to break the site ;). So no more daily content deployments and those of us in the dev team can finally start working with .NET 3.5 and LINQ.

For more on how we run the site, check out my Perth SharePoint User Group presentations and videos (things have changed but not that much):

http://blog.mediawhole.com/2009/02/how-we-do-wcm-at-tourism-wa-im-speaking.html

http://blog.mediawhole.com/2009/03/presenting-at-next-sharepoint-user.html

If you’ve read this far, you deserve the chance to win a prize. Want the opportunity to take an extraordinary taxi ride around the state of Western Australia? If so, check out the “brand activation” microsite: http://www.extraordinarytaxiride.com.au/

Note: I am employed by Tourism WA as a contractor working for Diversus. Mediawhole, mediawhole.com, and this blog are not associated directly with Tourism Western Australia or the westernaustralia.com website. The information provided above  is published independently as a member of the public and does not reflect the views of Tourism Western Australia or Diversus. Please consult a Tourism WA representative for more information about its brand, campaigns, and websites.