MVC 3 XML Sitemap

Ok, so you’ve got your shiny new mvc3 app up and running. Now, it’s time to bake in some of that SEO goodness. Here’s how you can easily add a sitemap.xml to your new mvc application. The solution specified below not only pumps out the xml file to submit to the search engines, but it is also a System.Web.SiteMapProvider, so you can use it for menus and wherever else you want to use your sitemap.

I’m a big fan of NuGet, so bring up the Package Manager Console and grab MvcSiteMapProvider (or download it from github).

Install-Package MvcSiteMapProvider

Once it is installed there will be a new Mvc.sitemap file added with the following to your solution:

<mvcSiteMapNode title="Home" controller="Home" action="Index">
    <mvcSiteMapNode title="About" controller="Home" action="About"/>
</mvcSiteMapNode>

Now for the easy bit: to get a ~/sitemap.xml to auto-generate for us based on our controller actions we specified in the sitemap file above. All you need to do is simply add the following to your route registration (generally Global.Application_Start()):

XmlSiteMapController.RegisterRoutes(RouteTable.Routes);

Now, like a lot of other people out there my next question was, what about my dynamically generated content from the database (such as Product/Details/1)?  Easy – MvcSiteMapProvider comes with dynamic node support. Just create a class like the following:


public class ProductDetailsDynamicNodeProvider : DynamicNodeProviderBase
 {
     public override IEnumerable<DynamicNode> GetDynamicNodeCollection()
     {
         // Create a node for each product
         foreach (Product product in productService.GetAllProducts())
         {
             var node = new DynamicNode();
             node.Title = product.Name;
             node.RouteValues.Add("id", product.Id);

             yield return node;
         }
     }
     public override CacheDescription GetCacheDescription()
     {
        return new CacheDescription("ProductDetailsDynamicNodeProvider")
        {
            SlidingExpiration = TimeSpan.FromMinutes(60);
        };
    }
 }

Things to note with the above code: you don’t have to override the cachedescription method, but this does allow us to specify how long all our products will be cached for in the sitemap. Then, you can add your dynamic node to the mvc.sitemap files as follows:

<mvcSiteMapNode title="Details" action="Details"
dynamicNodeProvider="Website.ProductDetailsDynamicNodeProvider, Website"/>

Now you are done and have a working auto-generated mvc sitemap. For those of us who are using it to generate breadcrumbs, check out the awesome example of html helpers in the documentation.

Next time, I’ll be looking at creating SEO friendly URLs in MVC.

Posted in .Net, Asp.Net, Xml at January 8th, 2012. No Comments.

Overriding the default serialization behavior in Json.NET

We use Newtonsoft Json.NET heavily in our project to control our JSON serialization. For the most part, it works absolutely perfectly out-of-the-box, but every now and then, we need to customize the way that serialization works for a particular object.

Such an occasion occurred today. Basically, I have a list of EmployeeActivity objects – where each instance is a subclass of EmployeeActivity. So, for example:

public abstract class EmployeeActivity
{
    public TimeSpan Start { get; set; }
    public TimeSpan Stop { get; set; }

    public virtual bool IsAvailable
    {
        get { return false; }
    }
}

public class AbsenceActivity : EmployeeActivity
{
    public string Reason { get; set; }
}

public class BreakActivity : EmployeeActivity
{
    public string BreakName { get; set; }
}

json.NET does a perfect job of serializing my objects, and they rebuild perfectly when I’m back in javascript land – however, I have no way of knowing the actual type of each activity in my array! To alleviate this, I wanted to create my own serializer which also injected the type name into each of the serialized objects – or at least, that’s what I thought I wanted to do!

As it turns out, there’s a bit of an easier option. JsonConvert uses a ContractResolver in order to work out how to Serialize/Deserialize each class, and you can override the default contract resolver when running serialization:

// This class contains the activity list
ActivityViewModel = new ActivityViewModel(DateTime.Today);

// Serialize using a custom contract resolver
string jsonViewModel = JsonConvert.SerializeObject(viewModel,
    Formatting.None, new JsonSerializerSettings
    {
        ContractResolver = new ActivityJsonContractResolver()
    });

The contract resolver’s job is to return a JsonContract which describes the object to be returned. For a POCO, this would typically be an instance of JsonObjectContract, which (amongst other things) describes the properties of the class which should be serialized.

So, all I needed to do was to extend the contract for subclasses of EmployeeActivity, such that it inherited a new Property to be serialized:

public class DiaryActivityJsonContractResolver : DefaultContractResolver
{
    protected override JsonObjectContract CreateObjectContract(Type objectType)
    {
        JsonObjectContract contract = base.CreateObjectContract(objectType);

        if (typeof(EmployeeActivity).IsAssignableFrom(objectType))
        {
            contract.Properties.Add(new JsonProperty
            {
                Readable = true,
                ShouldSerialize = value => true,
                PropertyName = "Type",
                PropertyType = typeof(string),
                Converter = ResolveContractConverter(typeof(string)),
                ValueProvider = new StaticValueProvider(objectType.Name)
            });
        }

        return contract;
    }
}

Truth be told, I’m not 100% sure if I need to implement as many of the properties on the JsonProperty that I’m creating. But you can see what’s going on here – I’m effectively telling the object contract that there’s an additional property called Type which needs to be serialized, that it’s a string, and that it should return whatever value is resolved by the StaticValueProvider. Speaking of which, here’s the last piece of the puzzle – a simple value resolver that always returns the same value:

/// <summary>
/// JSON value provider that always returns a static value
/// </summary>
public class StaticValueProvider : IValueProvider
{
    private readonly object _staticValue;

    public StaticValueProvider(object staticValue)
    {
        _staticValue = staticValue;
    }

    public void SetValue(object target, object value)
    {
        throw new NotSupportedException();
    }

    public object GetValue(object target)
    {
        return _staticValue;
    }
}

json.NET is fantastic, and it’s very extensible, but finding out where to start and what to do can be a little bit tricky. I managed to figure this out only by downloading the source files and poking through the code myself; hopefully this will be helpful to someone else in the same situation!

Posted in Asp.Net, C# at August 8th, 2011. No Comments.

jQuery Intellisense Support in Content Pages

I was trying my hand at whipping up some jQuery today and realising the awesome intellisense that is provided, couldn’t wait to get stuck in. Since I do enjoy adhering to the DRY principle wherever possible I decided I needed to use master pages first before I create my example content. I then set out to try and do a basic hello world example but realized my intellisense wasn’t working with jQuery. It was working with vanilla ASP .NET pages, just not content pages.

It turns out, that VS uses -vsdocs javascript files to provide this support. It requires it to be sitting next to your jquery file that you are referencing. Which it was thanks to the MVC Internet Application project template. It seems that ASP .NET doesn’t know at design time that I’m referencing jQuery in the master page.

After asking Professor Google, I stumbled upon a solution. I decided to slap the following bit of code into any content page that I have created and want some good ol’ intellisense.

<% #if (false) %>
<script src="~/scripts/jquery-1.4.4.js" type="text/javascript"></script>
<% #endif %>

Yes, this is a bit of hack to get it working and yes, I would like Microsoft to fix it. In the mean time, I will be egarly awaiting the intellisense support to be released for the latest 1.5 version.

UPDATE: I’ve just found that somebody has generated a new vsdoc for jQuery 1.5

UPDATE 2: If you reference your jQuery from the Microsoft CDN, you get intellisense since they also host the vsdoc files. Sadly, the Google CDN does not.

Posted in Asp.Net at February 3rd, 2011. 2 Comments.

DateTime Localization in ASP .NET

You’ve got an ASP .NET app and you need to display a date. Normally that just involves the old DateTime.ToShortDateString() or DateTime.ToString(“d”) and you continue coding on your merry little way. But what happens when your users are from different countries and expect different date formats.  Between Australia, USA and Japan there are 3 different date formats.

Then answer? The HttpRequest’s UserLanguages. This string array basically maps to the following languages that you can setup in your browser:

Now for the code:


CultureInfo culture = Request.UserLanguages != null
? CultureInfo.CreateSpecificCulture(Request.UserLanguages[0])
: CultureInfo.CurrentCulture;
myLabel.Text = DateTimeOffset.Now.ToLocalTime().ToString("d", culture);
Don’t forget to check if the UserLanguages is not null since you can remove all the values from the list if you like.
Also, if you are looking at ways to do this client side, ScottGu’s Blog has a nice post on a new jquery plugin for this.

* Update *
Just realised that you don’t need to any of this if you use the following in your web.config:

<globalization uiCulture="auto" culture="auto" />
Posted in .Net, Asp.Net, C# at June 18th, 2010. No Comments.

Path resolution in ASP.NET

One thing I’ve always thought that ASP.NET doesn’t do a great job of is handling resource paths. For example, lets say on your development box, you have a website hosted under the virtual directory “MyProject”, such that you browse to http://localhost/MyProject/ to go to the home page. Lets also say that when you deploy the website to an external domain, it falls into the domain root, so instead you go to http://myproject.com/ to see your home page.

Sound familiar? The real problem then comes when you try and define a resource (e.g. a javascript file) using a relative path:

<script
    type="text/javascript"
    src="/Scripts/jquery.js">

This will work fine and dandy when you deploy the website, however on your local machine it will be looking for jquery at the path http://localhost/Scripts/jquery.js” – which more than likely isn’t going to work.

To get around this problem, I wrote a quick helper method called “Locate”, which I find myself reusing time and time again between websites. More often than not, I put it into a static class called “SiteManager” which basically just contains a bunch of helper functions relevant to the current website. Here’s the method:

/// <summary>
/// Resolves the path to a URL
/// </summary>
/// <param name="url">The path to be resolved, relative to the
/// application root</param>
/// <param name="formatArgs">String formatting arguments</param>
/// <returns>The URL</returns>
static public string Locate(string url, params object[] formatArgs)
{
    if (String.IsNullOrEmpty(url))
        url = "/";

    if (formatArgs != null && formatArgs.Length > 0)
        url = String.Format(url, formatArgs);

    HttpContext context = HttpContext.Current;
    return String.Concat(
        context.Request.ApplicationPath,
        !url.StartsWith("/") ? "/" : String.Empty,
        url);
}

To use the method, you simply change your script declaration (or whatever resource you’re trying to find) as follows:

<script
    type="text/javascript"
    src="<%= SiteManager.Locate("/Scripts/jquery.js") %>">
</script>

You’ll also notice that it supports string formatting – so even though it would be pointless in this particular example, you could, if you preferred, do something like this:

<script
    type="text/javascript"
    src="<%= SiteManager.Locate("/Scripts/{0}.js", "jquery") %>">
</script>

In both instances, this will resolve the URL in your output document using the current application path –

  • http://localhost/MyProject/Scripts/jquery.js on your local machine; and
  • http://myproject.com/Scripts/jquery.js on the web server.

Enjoy!

Posted in Asp.Net, C# at January 14th, 2010. 5 Comments.

Containing floats in HTML

Have you ever created a block-level element (e.g. a div), whose only contents are floating elements – only to find that your block-level element doesn’t stretch down to contain it’s children? Well, I have, and it’s a problem that always annoys me because I can never bring to mind the easiest way of fixing it.

The simplest solution is to use “overflow:hidden” in the style of your parent element, however this doesn’t work for all browsers scenarios. So, for a much more comprehensive description of the problem, as well as a number of approaches you can use to solve it, check out this post.

Posted in Asp.Net at November 27th, 2009. No Comments.

What Is a Race Condition?

Just thought I would put a post out on something I learnt today that can occur when using ajax- a race condition. A race condition is is where the output or result of something is dependent on the timing of other events/code. A good example of this is below:

function ValidateInput() {
//some slow running code here...
}

function SaveForm() {
//save the form
}

<form>
<input type="text" onchange="ValidateInput()" />
<input type="button" onclick="SaveForm();" value="Submit" />
</form>

The problem here can occur when the user hits the Submit button before the validation method can finish. One way to overcome this is use a semaphore to indicate whether the form still needs validating on the SaveForm method.

Posted in .Net, Asp.Net, Javascript at October 6th, 2009. No Comments.

I’m back

After having 9 months off from doing .net (travelling, bumming and then a 3 month contract role as an architect in a coldfusion shop) I finally got myself a role doing what I love doing best – playing with .net and c#.

In the last 4 weeks I’ve been researching and prototyping apps that use

It’s been awesome to have the time and resources to touch across all of these technologies to enjoy their benefits and to loath the limitations.

All in all I reckon all of the technologies are a great addition to the .net stack and look forward to seeing them mature with each new release.

I look forward to documenting my findings as I find time to re-work them into don’t-give-away-IP examples.

Posted in .Net, Asp.Net, C#, Unit Testing at October 30th, 2008. No Comments.

Alternating items without using an AlternatingItemTemplate

Many of the controls in ASP.Net (e.g. a Repeater) support the concept of an AlternatingItemTemplate, which, as the name suggests, allows you to define an additional item template to change the look and feel for each alternating item in the data source. This is all well and good, but I’ve never seen a good use for it; more often than not, all you really want to do in your alternating item is, for example, slightly change the background colour for ease of visual differentiation.

Though you can use the alternating item template for this, it typically involves you duplicating a lot of code, since the AlternatingItemTemplate is mostly the same as your ItemTemplate (save perhaps for a CSS class declaration somewhere).


  <asp:Repeater ID="rpt" runat="server">
      <HeaderTemplate>
          <table cellpadding="0" cellspacing="0">
              <tr>
                  <th>First Name</th>
                  <th>Last Name</th>
                  <th>Age</th>
              </tr>
       </HeaderTemplate>
       <ItemTemplate>
              <tr>
                  <td><%# Eval("FirstName") %></td>
                  <td><%# Eval("LastName") %></td>
                  <td><%# Eval("Age") %></td>
              </tr>
       </ItemTemplate>
       <AlternatingItemTemplate>
              <tr class="Alternating">
                  <td><%# Eval("FirstName") %></td>
                  <td><%# Eval("LastName") %></td>
                  <td><%# Eval("Age") %></td>
              </tr>
       </AlternatingItemTemplate>
       <FooterTemplate>
          </table>
       </FooterTemplate>
  </asp:Repeater>

If this sounds like you, then never fear – there IS a better way!

When you’re binding, you have access to the RepeaterItem via the Container property, which contains an ItemIndex property. As such, the simplest way of alternating your items is simply to check if the number divides evenly by two:


  <asp:Repeater ID="rpt" runat="server">
      <HeaderTemplate>
          <table cellpadding="0" cellspacing="0">
              <tr>
                  <th>First Name</th>
                  <th>Last Name</th>
                  <th>Age</th>
              </tr>
       </HeaderTemplate>
       <ItemTemplate>
              <tr class="<%# Container.ItemIndex % 2 == 0 ? "Alternating" : String.Empty %>">
                  <td><%# Eval("FirstName") %></td>
                  <td><%# Eval("LastName") %></td>
                  <td><%# Eval("Age") %></td>
              </tr>
       </ItemTemplate>
       <FooterTemplate>
          </table>
       </FooterTemplate>
  </asp:Repeater>

There is a minor downside using this approach – the non-alternating rows will all declare an empty CSS class within them. But, this is a sacrifice I think is acceptable to decrease repetition of code.

Posted in Asp.Net at August 27th, 2008. 2 Comments.

DataBinding with XPath

Did you know that when you bind an item to a DataBound control (e.g. a repeater), you can use the XPath function to evaluate… well, an XPath? Obviously this is only going to work if you bind an XmlNode (or subclass) instance to the control, but still – comes in very handy!


// in code-behind
rpt.DataSource = document.SelectNodes("/ROOT/students/student");
rpt.DataBind();

<!-- in aspx page -->
<asp:Repeater id="rpt" runat="server">
    <ItemTemplate>
        <%# XPath("name/text()") %>
    </ItemTemplate>
</asp:Repeater>
Posted in Asp.Net, C# at October 12th, 2007. No Comments.
Quickduck logo