When asp.net 2 arrived i was very pleased to see full support included for accessing the head section of a page programmatically. This meant i could now access and add all the meta tag information dynamically. Recently i started looking into dynamic sitemap generation and picked up a wealth of good tips from Cristian Darie and Jaimie Sirovichs' excellent book on SEO for Asp.Net - including a basic implementation of a sitemap http handler.
I decided to blend the management and generation of the meta tags and sitemap into one set of tools that could be easily implemented into any asp.net site.
Screenshot of pageManager.aspx
How it works
PageManager allows you to add meta tags to pages within your site and specify whether or not the page should be included in the sitemap. This is all stored in a sql server database, and the tables can easily be imported into an existing site.
When a page loads it performs a query against tblPage to see if an entry exists for a page with its name (including any paths after the domain name for uniqueness of course). if it does it then proceeds to load any meta tag data into its header, and a custom page title if one is present.
Sitemap.ashx generates an xml sitemap of all the pages from tblPage flagged as 'include in sitemap' via pageManager.aspx. The sitemap is based on the sitemaps.org standard , but only includes a minimal amount of information (such as page priority).
But most of my pages are data driven - did you think about that?
Yes, most of mine are too..
The sitemap generator uses reflection to load a custom class that you create, which implements a single method - returning a custom generated list of sitemap entries
lets say you have product.aspx (as included in the example project download below) and it accepts a simple parameter that determines which product to display on the page. such as mysite.com/product.aspx?productID=1544
Simply create a class that implements ICustomPageInfo and implement its only member - GetCustomPageItems() As List(Of SitemapItem)
The purpose of this function is to return a List of sitemapitems for insertion into the generated sitemap instead of 1 parameterless link to product.aspx. So ideally, you would scan the products table, pick out the product pages you want indexed and return those urls as a list of sitemap items.
The only other thing you have to do is specify the fully qualified name of this class you have created in the 'Custom Call' box in pageManager for the page in question and the sitemap handler will append these to the generated sitemap automatically.
For an example of this, see the ProductSitemapInfo class in the example project download below.
I have already set this up in the attached mdf example database.
But what about specific meta tags for these data driven pages?
I implement these at product level within the system, so the user adding/editing the product would insert a heap of keywords specific to the product in question. When that product is loaded into product.aspx i populate the keywords and description meta tags from the product record in the database, essentially overriding the insertion at basepage level from tag data specified in pageManager. You can easily extend this by creating a 'populate_header' event in the basepage, subscribing to this at page level and inserting your custom keywords/tags right there and then.
This is how i do it in some of the sites i have developed.
But i dont use Query strings in my pages?
If you understand how this works then surely you can work out the rest yourself :-P
The Contents of the example project
admin/pageManager.aspx - where you set what pages to include in the sitemap and what meta tags to pump into each page you want indexing.
App_Data/pageManager.mdf - the database used as an example (includes the products table from Northwind :-P )
code/basepage.vb - you may have one already - so do i. This just contains the basic hook into the page_load event to populate the necessary meta tag information and title - if found in the database.
code/db.vb - database access code, just to save you the time of rolling your own if you choose to download the demo project and want to run it.
code/ICustomPageInfo.vb - an interface to allow multiple sitemap url generation for data driven pages (explanation below)
code/PageInfo.vb - a helper class for the page data, quite basic but easily extended. I will no doubt rewrite this based around LINQ very soon (time permitting :-S)
code/ProductSitemapInfo.vb - an example implementation of ICustomPageInfo for the product.aspx page
code/siteMapItem.vb - simple helper object that holds sitemap url information
code/util.vb - just some utility functions for generic stuff like web.config access etc...
default.aspx - do i really have to explain this one? :-P
product.aspx - a data driven page that shows an individual product based on the id passed in via query string.
siteMap.ashx - the http handler that returns the xml sitemap.
Yes, there are quite a lot of things that can be done to extend this, some of which i have already done and used in live sites, for example....
Create a specific basepage method that can be overridden to allow extra tags/keywords to be appended/inserted at page level
use LINQ to query the aspx pages that populate the treeview in pageManager.aspx (not really necessary but hey its new!)
Use caching to store specific page tags after first retrieval.
The gridview for adding metatags is pretty mediocre, but serves as an example, ok? :-P
I hope you find this tool to be beneficial like i have.
The core of it is currently in use on live sites that rank well in their area, and it saves a lot of manual work when we need to modify our keywords and add pages to the sitemap.
Both versions come with a populated database dumped from SQL Server 2000, and
also a create database script if you want to start from scratch. The
tblProduct table comes directly from Northwind.
Vs2008 .Net 3.5 in Visual Basic.NET Download
Vs2008 .Net 3.5 in C# Download
note - some of the directory parsing code originates from this post by Scott Mitchell
This is my first blog post on here, so Hello from me.
Rather than just have a meaningless 'hello' first post, i thought i would at least post something useful (albeit rather small)..
With all the talk of LINQ at the moment there are a lot of examples popping up on sites and most of them follow this kind of format...
myDbDataContext ctx = new myDbDataContext("connectionString");
var query = from.... //great LINQ example etc...
//oh, wait, no disposal of my data context?
When i first started learning LINQ I was pretty curious as to why most examples do not offer some kind of indication that disposal is required. Maybe because that is exactly what they are - examples. Regardless, i have always disposed my dataContext objects, usually taken care of automatically by wrapping them in a using block.
It is advised on various sites, as pointed out in this MSDN forum post
I am currently preparing a rather long post on SEO management for Asp.Net and hope to have that up soon.
Subscribe in a reader