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: <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
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
}
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.