Every spring in my town, they send out a flyer asking residents to avoid standing water, as it's a breeding place for mosquitos. Watertown is close to the city and yards are small, so it's easy to comply. And it works! We can sit on the back patio on summer evenings and not get eaten alive, while friends in rural towns retreat to their screened-in porches.
Wouldn't it be good to drain the stagnant water in software and prevent pests from hatching later on? Well you can. This article classifies the most common species of SharePoint coding bugs, and how you can stamp them out before they sprout wings. I've seen dozens of bugs that would have never made it into production if only for these simple guidelines!
At last fall's SharePoint conference in Anaheim, fpweb.net gave away tee shirts that said, "It works on my machine!" I get comments and laughs every time I wear mine. The first three bugs are just that kind of infection: they appear in production, even after testing in staging and development.
1. Data Inconstans (The May or May Not Fly)
This vermin appears only in the presence of certain content, which may be found in production, but not on testing servers. The trick to prevent this is to sync content from production back into development and staging machines. That way, the problem is lured out during the testing process, before it can bite end-users. Perhaps you can't sync everything, but at least take representative content. And certainly sync content from any custom applications you've built!
Maybe the pest only shows up if you have a certain character in a list item field, or if you've changed the value in a lookup list. Maybe there's a 50 millisecond delay in a loop through a bunch of list items, and you don't notice it until there were so many items in the list that users go batty waiting for the screen to paint. (Do you really need to iterate through those items anyway?) I didn't make these up: they're all real bugs I found. Not in my code ... well, hardly ever! If you ask for my help troubleshooting, a copy of the production content will be my first request.
A site collection backup works well for copying the content; attaching a copy of a content DB works too. Repliweb has built a product to address this. However you do it, make it part of your everyday process. It's a minor amount of work compared with the time you'll save trying to hunt it down in production, not to mention the buzz from end-users!
2. Locus Incertus Suffragium (The Uncertain Resource Locator)
This might be the most abundant arthropod I've seen over the years. I've got some gray hairs from it; I've seen it in downloaded samples and toolkits; and before I learned my lesson, I posted code on MSDN Code gallery with this little beast inside. It's an infestation!
Here's how it happens. Suppose your code needs the URL of an image you deployed with a web part. If it's in a site collection feature, that image is under the top-level site in the collection. If the code looks for the image in SPContext.Current.Web it will work ... until you use it in a child site! Maybe you have the page URL, and need to navigate to a list in the site. The thing is, just looking at the URL, how can you distinguish between the managed path, the site collection root, and child sites, lists and folders? They're all different containers in SharePoint, yet they all look the same in the URL!
The object model can help here. SPContext.Current is a big help. Even if you create a new SPSite object, you can pass it a URL anywhere within the site collection and it will work. But then it's fickle, returning a ServerRelativeUrl of "/" for the root site collection, yet removing the trailing "/" when the site collection is below a wildcard managed path. This makes it impossible to simply concatinate a path on the end, and leads to code that looks like this:
SPSite currentSite = SPContext.Current.Site;
imageUrl = ((currentSite.ServerRelativeUrl == "/") ?
"/" : currentSite.ServerRelativeUrl + "/") +
This gadfly also frequents client side code, and the designer side of visual web parts.
The lesson is simple: test your solution in sites at different URL depths, even if you think you got it right. Test it anyway. We're only human, after all. Test your solution in:
- A site collection at the root of a web application, such as http://myserver/
- A site within that site collection, such as http://myserver/childsite
- A site collection within a wildcard managed path, such as http://myserver/sites/test/
- A site within that site collection, such as http://myserver/sites/test/childsite
Does that seem like a lot of trouble? It's worth it. You'll smile when someone says your web part worked perfectly, all the time.
3. Obses Non Compositus (The Security Scorpion)
There are several breeds of this pesky parasite:
- The solution was tested by a privileged user, and the end users don't have privileges
(Developers normally log in as farm and server administrators, after all!)
- The solution was tested with Windows Classic security, and production uses some form of claims
- The solution was tested in a different domain
It's important to test with the same security configuration as your production environment, and to test as an ordinary user. I don't know how many times I've seen this, but it's a lot. Your staging environment should run in the same domain as production. It's the only way to test reliably, and find bugs that appear for real users in your domain.
4. Dispono Quisquiliarum (The Garbage Fly)
This is an oldie but baddie. It makes me wonder what unmanaged code lurks in the bowels of SharePoint. It's those IDisposable objects in the SharePoint API.
For those not in the know, here's the scoop. The .NET garbage collector will clean up most .NET objects. All you need to do is stop referencing them, and the garbage collector comes along and frees up the mess. If only that worked in my living room!
But some objects aren't so tidy. They use resources that live outside of .NET, and, like my town (who won't recycle anything that's not on its list), the garbage collector won't free up those resources. So these objects implement a special interface, called IDisposable. You need to explicitly dispose of those objects to free up the unmanaged resources, or you'll experience memory leaks.
Some of these objects are the at the core of the SharePoint API, such as SPSite and SPWeb. Even Linq developers can't escape this louse: the DataContext object is IDisposable too. To correctly dispose of these objects, use either the .Dispose() method or the C# using statement. The following example shows both methods:
using (SPSite mySiteCollection = new SPSite(http://myserver/))
SPWeb myWeb = null;
myWeb = mySiteCollection.RootWeb;
// Do something with the SPWeb ...
// Now formally dispose of the SPWeb object
if (myWeb != null) myWeb.Dispose();
// Closing the using statement will dispose of the SPSite object
Notice the use of the try/finally block to ensure that the SPWeb object is disposed of even if the code throws an exception. The using statement does this automatically.
All this wouldn’t be so bad if the API were consistent, but there are times when you shouldn't dispose of these objects after all, such as when you obtain them from SPContext.Current.Site or SPContent.Current.Web. In this case, you're accessing SPSite and SPWeb objects that SharePoint is using to render the page, and disposing of them will render the UI unstable. If you've got a web part that seems to make the rest of the page go buggy, make sure you're not disposing of an object that belongs to SharePoint.
All the rules are spelled out here: http://msdn.microsoft.com/en-us/library/ie/aa973248(v=office.12).aspx. It's also a good idea to install SPDisposeCheck on your development machine to automatically flag these errors. You can download it here: http://archive.msdn.microsoft.com/SPDisposeCheck.