Defensive Coding for Sitecore

Inspired by a great post by Adam Weber about Sitecore Defensive Coding Practices, I’ve decided to throw down some of my favorite patterns and practices with Sitecore Development. If you have any of your own, share them in the comments!

Define a BaseSublayout Class

Define a BaseSublayout class to expose base-level functionality to any UserControl/Sublayout that you will have on your site, e.g. accessing a Data Source item. For example:

protected class PromoBox : BaseSublayout
{
	protected void Page_Load(object sender, EventArgs e)
	{
		// use any code exposed in the inherited class
	}
}

My experience with this: I started doing this when I realized the power of assigning data source items to a sublayout defined on an item. The code to get a datasource takes a few lines, so I abstracted it into a base class for reusability on any project. It turns out John West has had something even better on Shared Source for a while now, the Sublayout Parameter Helper.

Gracefully Degrade the Front-End

Note: This is not to be confused with the other graceful degradation pattern of front-end development, particularly with JavaScript. This is about accessing Sitecore data.

This is by far my favorite pattern that I use all the time. As developers, our job is to make the site render correctly per the content in Sitecore. That includes handling content editor errors that may occur, such as not filling out data or missing something. The concept is simple:

Set controls (ASP.NET controls, FieldRenderers, etc.) to be visible="false" by default, then prove the data to render exists and render the data. There are many sub-patterns you can go with here, depending on how you write your HTML and how the component works. For example, any sublayout I have that requires a DataSource is wrapped in a PlaceHolder with visible="false". My job as a developer is to ensure a data source is set and its of the right template type. Once confirmed, I turn the placeholder’s visibility on. This pattern ensures no widow HTML exists that is rendered without any real data.

Here’s a simple example:

<asp:PlaceHolder ID="TitleWithLinkPlaceholder" runat="server" Visible="false">
	<h4>
		<asp:Hyperlink ID="TitleWithLinkHyperlink" runat="server">
			<sc:Text ID="TitleWithLinkText" runat="server" Field="Title" />
		</asp:Hyperlink>
	</h4>
</asp:PlaceHolder>
bool linkSet = !String.IsNullOrEmpty(genericCallout.Link.Url);

if (!string.IsNullOrEmpty(genericCallout.Title.Text))
{
	if (linkSet) // With link
	{
		TitleWithLinkHyperlink.NavigateUrl = genericCallout.Link.Url;
		TitleWithLinkText.Item = genericCallout.InnerItem;
		TitleWithLinkPlaceholder.Visible = true;
	}
	//...

As you can see above, I assume the data is not in Sitecore by wrapping my HTML in a placeholder that is visible="false" then in C#, I see if the data exists and turn the controls on. In this simple example, it prevents a random <h4> tag from appears with nothing in it. Is it a lot of code for a little check like this? Yes. Is it worth it when not all fields in Sitecore are required and they affect the front-end? YES!

Use FieldRenders or at Least Trigger the renderField Pipeline

Many Sitecore solutions need to support Page Edit mode. In order for code to support this, the renderField pipeline needs to be triggered. Why? Because this pipeline injects JavaScript and JSON into the page in Page Edit mode to allow for dynamic control of the page. Triggering this can be done by using FieldRenderer controls on the front-end when possible, or if not possible, at least use the FieldRenderer via C#. Luckily, the Custom Item Generator (CIG) exposes easy access to the Rendered value of a field, so at least use this if you need to use regular .NET controls. (Note: the CIG is another useful pattern Adam covers in his post).

Use a Page Load Pattern with Try-Catch Blocks

Every sublayout I write follows the same pattern:

  1. The Page_Load method body contains a try-catch block.
  2. The try block calls a helper method to do the main work.
  3. If the sublayout takes a DataSource, a check occurs to ensure the item is set and is of the right template.
  4. The catch block logs the exception to the Sitecore log with the name of the specific sublayout.

Here’s an example where we use several of the patterns in this blog post (BaseSublayout to expose the DataSource, CIG items, Page_Load pattern):

protected void Page_Load(object sender, EventArgs e)
{
	try
	{
		if(DataSource != null && DataSource.TemplateId.Equals(PromoItem.TemplateId.ToString()))
		{
			LoadPromo(DataSource);
		}
	}
	catch(Exception ex)
	{
		Sitecore.Diagnostics.Log.Error("Promo Sublayout", ex, this);
	}
}

In the above code, the method LoadPromo(Item item) would actually bind the data to controls, etc.

My experience with this: I started doing this when I found it hard to determine where some null errors were occurring on sublayouts that didn’t have appropriate null checks. I started to follow the same pattern over and over and eventually tacked on the try-catch with Sitecore logging to make it easier to know when issues came up.

Avoid Hard-coded Paths and Instead Expose Global Item Links

To explain this approach, let me pose a scenario:

Say you’re building a search results page on your site to host the results sublayout for your global full text site search (using Lucene.NET, or Coveo, or Google Search Appliance, etc.). You have a textbox in your header that handles the search query and you push the query to the results page to actually search the site. How does your code for the textbox know which page to go to for the results?

  • Option 1: hard-code it (BLAH!)
  • Option 2: expose a global field in Sitecore to set the results page (FTW!)

In code, your search box query method would do something like this:

protected void btnSearch_Click(object sender, EventArgs e)
{

	// assumes Globals.GlobalItem is a static helper class to get the globals item
	ReferenceField searchPage = Globals.GlobalItem.Fields["Search Page"];
	var url = LinkManager.GetItemUrl(searchPage.TargetItem) + "?q=" + txtSearch;
	Response.Redirect(url);

}

Why is Option 2 better? If someone renames the search results page, say from search-results to just results then the global field in Sitecore would handle the change.

For Any Standard User Control in Use, Consider Statically Binding it as a Sublayout

If your code in layouts and sublayouts references other modular controls such as standard users controls, e.g. <uc:Promo runat="server"></uc:Promo> you should consider loading the user controls the Sitecore way. By changing a standard user control reference to a statically bound sublayout, you can leverage Sitecore’s HTML output cache to cache the rendered HTML per the business rules of the control. E.g. <sc:Sublayout Path="~/path/to/your/control.ascx" Cacheable="true" runat="server" />, then in C# you could perhaps set the VaryByParm property to alter the cache instances by your uniqueness of the control.

Leverage Utility Classes, Do Not Reinvent

Sitecore comes with a plethora of utility classes in many namespaces. Learn them and use them. They’re very handy and you can avoid writing code that may already exist (e.g. sanitizing an item name).

 

Mark Ursino

Mark is Sr. Director, CMS Practice Lead at Rightpoint and a Sitecore Technology MVP.

 

2 thoughts on “Defensive Coding for Sitecore

  1. You are able to conveniently deal with any of these jobs in a
    weekend’s well worth of time. The drawback of this design
    is that once you close the valve you’ll see that it’s
    still dripping, and the first inclination is to crank the valve tighter.
    This is a digital audio output, which you can connect to the appropriate jack on your surround processor
    or receiver and will deliver that full surround sound impact.

    Feel free to surf to my web-site: painters

Leave a Reply

Your email address will not be published. Required fields are marked *