Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

XML Sitemap in ASP.NET Application

0.00/5 (No votes)
11 May 2014 1  
XML sitemap in ASP.NET application

What is an XML Sitemap?

Here is a good set of information where you can find what it is and why it is used. Typical structure of an XML sitemap is like below:
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
  <url>
    <loc>http://www.domain.com/store/great-coffee-shop</loc>
    <lastmod>2012-3-3</lastmod>
    <changefreq>Weekly</changefreq>
    <priority>1</priority>
  </url>
</urlset>
After creating an XML sitemap, it needs to be submitted to search engine and maintaining an accurate Sitemap is important to proper indexing of a website pages. Creating an XML in C# is no difficult job, but one may find it hard to maintain the source code in future releases as it will grow as the website progresses. So I came up with few simple rules and patterns I can use to maintain the future maintenance.

The Diagram

sitemap-diagram

As the diagram implies, Sitemap classes will implement the interface ISitemap and inherit an abstract class SitemapBase. When I implement, I can set the ExecutionOrder which will take an integer value and Sitemap classes will execute their Build method in that order. The config class has a static method BuildSitemapXml which will start the process. You can generate the XML on Application_Start or you can make a separate controller (for MVC app) for that.

The Code

public class SitemapConfig : SitemapBase
{
    public static void BuildSitemapXml()
    {
        var ctx = HttpContext.Current;
            var sitemap = new SitemapConfig();
            var sitemapThread = new Thread(() =>
            {
                HttpContext.Current = ctx;
                sitemap.Start();
            });
            sitemapThread.Start();
    }

    public void Start()
    {
        string encoding = Response.ContentEncoding.WebName;

        var sitemap = new XDocument(new XDeclaration("1.0", encoding, ""),
            new XElement(Ns + "urlset",
                new XAttribute("xmlns", Ns.NamespaceName),
                new XAttribute(XNamespace.Xmlns + "image", Imagens),
                new XAttribute(XNamespace.Xmlns + "video", Videons),
                base.GetSitemapElements())
            );
        sitemap.Save(HttpContext.Current.Server.MapPath(@"~/sitemap.xml"));
    }
}

… and the ISitemap.cs and SitemapBase.cs.

public interface ISitemap
{
    int ExecutionOrder { get; }
    string UrlPrefix { get; }
    List<XElement> Build();
}

public abstract class SitemapBase
{
    protected XNamespace Ns = "http://www.sitemaps.org/schemas/sitemap/0.9";
    protected XNamespace Imagens = "http://www.google.com/schemas/sitemap-image/1.1";
    protected XNamespace Videons = "http://www.google.com/schemas/sitemap-video/1.1";
    protected HttpContext CurrentContext { get { return HttpContext.Current; } }
    protected HttpRequest Request { get { return HttpContext.Current.Request; } }
    protected HttpResponse Response { get { return HttpContext.Current.Response; } }

    protected XElement GetElement(string url, DateTime lastModified, ChangeFrequency changeFrequency, int priority)
    {
        var element = new XElement(Ns + "url", new XElement(Ns + "loc", url));
        element.Add(new XElement(Ns + "lastmod", lastModified.ToString("MM-dd-yyyy")));
        element.Add(new XElement(Ns + "changefreq", changeFrequency));
        element.Add(new XElement(Ns + "priority", priority.ToString(CultureInfo.InvariantCulture)));
        return element;
    }

    protected XElement GetImageElement(string content, string caption)
    {
        var imageElement = new XElement(Imagens + "image");
        imageElement.Add(new XElement(Imagens + "loc", content));
        imageElement.Add(new XElement(Imagens + "caption", caption));
        return imageElement;
    }

    protected XElement GetVideoElement(string thumbnailUrl, string title, string description, string videoUrl)
    {
        var videoElement = new XElement(Videons + "video");
        videoElement.Add(new XElement(Videons + "thumbnail_loc", thumbnailUrl));
        videoElement.Add(new XElement(Videons + "title", title));
        videoElement.Add(new XElement(Videons + "description", description));
        videoElement.Add(new XElement(Videons + "content", videoUrl));
        return videoElement;
    }

    public enum ChangeFrequency
    {
        Always,
        Hourly,
        Daily,
        Weekly,
        Monthly,
        Yearly,
        Never
    }

    /// <summary>
    /// Returns all types in the current AppDomain implementing the interface or inheriting the type. 
    /// </summary>
    public static IEnumerable<Type> TypesImplementingInterface(Type desiredType)
    {
        return System.Reflection.Assembly.GetCallingAssembly().GetTypes()
               .Where(type => (desiredType.IsAssignableFrom(type) && 
               !type.IsAbstract && !type.IsGenericTypeDefinition && !type.IsInterface));
    }

    public List<XElement> GetSitemapElements()
    {
        var sitemapElements = new List<XElement>();
        IEnumerable<Type> sitemaps = TypesImplementingInterface(typeof(ISitemap));
        return sitemaps.Select(sitemap => (ISitemap)Activator.CreateInstance(sitemap))
            .OrderBy(instance => instance.ExecutionOrder)
            .Select(instance => instance.Build())
            .Where(elements => elements != null)
            .Aggregate(sitemapElements, (current, elements) => current.Union(elements).ToList());
    }
}

… and here is a demo implementation. The Build() function returns the store information along with url in a XElement List object. Chances are you will have more of these types of implementations for your website.

public class SitemapStores : SitemapBase, ISitemap
{
    public int ExecutionOrder { get { return 1; } }
    public string UrlPrefix { get { return "Store/"; } }
    
    public List<XElement> Build()
    {
        var stores = Db.GetStores();
        var storeElements = new List<XElement>();

        stores.ForEach(store =>{
            string storeUrl = string.Format("{0}/{1}{2}", WebHelper.SiteDomain, UrlPrefix, store.StoreUrl);
            var element = base.GetElement(storeUrl, store.UpdateDate, ChangeFrequency.Weekly, 1);
            storeElements.Add(element);
        });

        return storeElements;
    }
}

From all the implementations, the Build() function will be invoked by GetSitemapElements() from SitemapBase. This method will look for all the ISitemap implantation in its assembly and execute its Build() function.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here