Adding a dynamic SiteMap for search engine optimization using LINQ

A couple months ago I added a feature to this site to build a Site Map for this site dynamically based on the information from the database for posts and files for the downloads. If your not familiar with how Sitemap files can help your site searchability, Google has a good documentation about Sitemaps in their Webmaster tools.

The SiteMap Protocal is a rather simple XML document consisting of a set of url nodes that consist of the following:

  • loc - URL for the page link
  • lastmod - Date the page was last modified
  • changefreq - How often the page is changed
  • priority - How high you think the page should be ranked relative to other pages on your site.

For this site, I decided to index the main (default.aspx) File, about and contact pages. In addition, I indexed each post as a separate url node. If you want to view the resulting data, browse to http://www.thinqlinq.com/sitemap.aspx. To do this, I used LINQ to XML with VB XML Literals. To begin, we need to add the XML Namespaces. At the top of our file, we enter the following inports:

Imports <xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
Imports <xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

We continue by adding the root urlset node and one child node representing the main page:

Dim map = _

    <?xml version='1.0' encoding='UTF-8'?>

    <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

        xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9

            http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"

        xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

 

        <url>

            <loc>http://www.ThinqLinq.com/default.aspx</loc>

            <lastmod>

               <%= (From p In dc.PostItems _

                    Order By p.PublicationDate Descending) _

            .First.PublicationDate.ToString("yyyy-MM-dd") %>

            </lastmod>

            <changefreq>daily</changefreq>

            <priority>1.0</priority>

        </url>

    </urlset>

 

Most of this is standard XML. The main difference is the use of a LINQ query to show the last modification date based on the most recent post from our database. In this case we just want the First date when the dates are ordered descending. We do need to format it properly so that our search engine (Google) will be able to recognize it.

Next up, we need to add the link for the Downloads page. We'll do this much the same way that we added the url for the default page. However, in this case the modification date won't come from the database, but rather use a LINQ to Objects query to get the most recent file in the downloads directory on this site.

<url>

    <loc>http://www.ThinqLinq.com/Files.aspx</loc>

    <lastmod>

        <%= (From f In New System.IO.DirectoryInfo( _

            Server.MapPath("~/Downloads")).GetFiles _

            Order By f.LastWriteTime Descending) _

            .FirstOrDefault.LastWriteTime.ToString("yyyy-MM-dd") %>

    </lastmod>

    <changefreq>weekly</changefreq>

    <priority>1.0</priority>

</url>

The About and Contact pages are relatively straight forward. The remaining url nodes are generated based on the records in the PostItems from our database. To populate them, we'll create a LINQ query pulling the data from the database using LINQ to SQL and projecting (Select) out individual url nodes for each row in the database:

<%= From p In dc.PostItems.ToList _

    Select <url>

               <loc>http://www.ThinqLinq.com/default/<%= p.TitleUrlRewrite %>.aspx</loc>

               <lastmod><%= p.PublicationDate.ToString("yyyy-MM-dd") %></lastmod>

               <changefreq>daily</changefreq>

               <priority>0.3</priority>

           </url> %>


As you can see, there isn't much here that is overly complex. It's just a series of LINQ querys filling the data from various sources. For reference purposes, Here's the complete code:

Imports <xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

Imports <xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

 

Partial Class SiteMap

    Inherits System.Web.UI.Page

 

    Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load

        Response.Buffer = True

        Response.Clear()

        Response.Cache.SetCacheability(HttpCacheability.NoCache)

        Response.ContentType = "text/xml"

        Response.AddHeader("Content-Disposition", "inline;filename=blog.rss")

        WriteRss()

        Response.End()

    End Sub

 

    Private Sub WriteRss()

        Try

            Using dc As New LinqBlog.BO.LinqBlogDataContext

                Dim map = _

                    <?xml version='1.0' encoding='UTF-8'?>

                    <urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       
xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd"

                        xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">

 

                        <url>

                            <loc>http://www.ThinqLinq.com/default.aspx</loc>

                            <lastmod>

                                <%= (From p In dc.PostItems _

                                    Order By p.PublicationDate Descending) _

                                    .First.PublicationDate.ToString("yyyy-MM-dd") %>

                            </lastmod>

                            <changefreq>daily</changefreq>

                            <priority>1.0</priority>

                        </url>

                        <url>

                            <loc>http://www.ThinqLinq.com/Files.aspx</loc>

                            <lastmod>

                                <%= (From f In New System.IO.DirectoryInfo( _

                                    Server.MapPath("~/Downloads")).GetFiles _

                                    Order By f.LastWriteTime Descending) _

                                    .FirstOrDefault.LastWriteTime.ToString("yyyy-MM-dd") %>

                            </lastmod>

                            <changefreq>weekly</changefreq>

                            <priority>1.0</priority>

                        </url>

                        <url>

                            <loc>http://www.ThinqLinq.com/about.aspx</loc>

                            <lastmod>

                                <%= System.IO.File.GetLastWriteTime( _

                                    Server.MapPath("About.aspx")).ToString("yyyy-MM-dd") %>

                            </lastmod>

                            <changefreq>monthly</changefreq>

                            <priority>1.0</priority>

                        </url>

                        <%= From p In dc.PostItems.ToList _

                            Select <url>

                                       <loc>http://www.ThinqLinq.com/default/<%= p.TitleUrlRewrite %>.aspx</loc>

                                       <lastmod><%= p.PublicationDate.ToString("yyyy-MM-dd") %></lastmod>

                                       <changefreq>daily</changefreq>

                                       <priority>0.3</priority>

                                   </url> %>

 

                        <url>

                            <loc>http://www.ThinqLinq.com/Contact.aspx</loc>

                            <lastmod>2008-02-28</lastmod>

                            <changefreq>never</changefreq>

                            <priority>0.1</priority>

                        </url>

                    </urlset>

                Response.Write(map)

            End Using

        Catch ex As Exception

            Response.Write(<error><%= ex.ToString %></error>)

        End Try

 

    End Sub

End Class

 

Posted on 1/3/2009 3:32:00 PM - Comments (2)
Categories: VB Dev Center , LINQ , VB , VS 2008

F# and the Excel Financial Functions

With my focus on LINQ, I have been considering the functional implications of the changes to the standard imparitive languages. As a result, I've been trying to watch the changes coming with the new F# Functional programming language which will be part of VS 2010. When thinking about the use cases for F#, areas with heavy computational needs are often the best use case. In many ways, I would think one ideal place for using F# would be in Excel where many business people have no problems chaining function calls together with complex nested If() functions.

The team has gone one better and taken the financial functions in Excel and re-written them in F#. The code is available on the MSDN Code Gallery. While this may be a nice feature, I'm not sure it was absolutely needed other than proof of concept. Why? Because most of the typical financial functions are already available in the VisualBasic.Financial namespace. Indeed, when I started my most recent project, I had the choice of language and choose VB.Net in part because it supported a number of the functions I needed in the banking industry out of the box.

Below is a comparison of the methods included in each library. I do find it interesting that the F# implementation chose not to implement the Rate function which is one of the trickier functions to implement because it is essentially a goal-seek.

VisualBasic.Financial F# Library
PV PV
FV FV
PMT PMT
NPER NPER
IPMT IPMT
PPMT PPMT
CUMIPMT
CUMPRINC
ISPMT
FVSCHEDULE
IRR IRR
NPV NPV
MIRR MIRR
XIRR
DB
SLN SLN
SYD SYD
DDB DDB
VDB
AMORLINC
AMORDEGRC
COUPDAYS
COUPDAYSBS
COUPDAYSNC
COUPNUM
COUPPCD
COUPNCD
ACCRINTM
ACCRINTM
PRICE
PRICEMAT
YIELDMAT
YEARFRAC
INTRATE
RECEIVED
DISC
PRICEDISC
YIELDDISC
TBILLEQ
TBILLYIELD
TBILLPrice
DOLLARDE
DOLLARFR
EFFECT
NOMINAL
DURATION
MDURATION
ODDFPRICE
ODDLPRICE
ODDLYIELD
RATE

Posted on 12/24/2008 10:26:00 AM - Comments (0)
Categories: VB

VS 2010 CTP Timebomb

I've been playing with the Visual Studio 2010 VPC image from the PDC in preparation for my talk at the Atlanta MSDN Developer Conference. I've started to notice warnings that Visual Studio will stop working in 20 days (before the end of January). In order to continue using this image, you need to stop the synchronization with the host PC. Ben Armstrong has a good post on how to disable time synchronization in VPC 2007.

VS isn't the only thing that will time bomb in this image. I'm also planning on doing some demos of office integration without requiring the Primary Interop Assembly. The versions of Word and Excel also have a limit on the number of times you can open them. Hopefully, I won't run those demos more than 20 times because the demos will stop working after that (until I dump that instance of VPC in favor of a clean instance at least). Brian Keller has a good post detailing the various time bombs that are included in the VPC image.

Hopefully I'll see some of you at the MDC and can share my enthusiasm for VS 2010 before it time bombs on me.

Posted on 12/9/2008 10:07:00 AM - Comments (0)
Categories:

VB 10 Samples and White-paper

If you want to get a head start on the new language features coming in VB 10, head on over to the MSDN Code Gallery and download the samples and white-paper. Some of the new features are:

  • Multi-line and statement lambdas
  • Auto-Implemented Properties
  • Collection and list initializers
  • No PIA (that's Primary Interop Assembly, not the other PIA)
  • Generic Variance
  • Implicit line continuation

We'll be showing these off at the MDC in a couple weeks. Make sure to come out and see what these are all about.

PS. For those who are saddened about the pleight of the downsized underscores, the http://www.unemployedunderscores.com/ website is dedicated to retraining them to be productive members of society. Please contribute.

Posted on 11/29/2008 4:58:00 PM - Comments (0)
Categories: VB

PDC Content comes to Atlanta

In case you missed the PDC, don't miss the opportunity to get some of the content for a greatly reduced cost. Microsoft touring the country with their MSDN Developer Conference  and are coming to Atlanta on December 16th at the Westin Peachtree Plaza.

I'll be among the who's who list of local experts presenting the content. For my part, I'll be showing you the language enhancements that are coming for C# 4.0 and VB 10.0. This release is more about parity than ground-breaking advances like we had with LINQ in the last release. There are a number of nice features which should make programming a bit easier.

In addition to the language session, Glen has a full writeup including the speakers and sessions. There's also a number of cool give-aways. Sign-up today for a great event.

Posted on 11/29/2008 4:42:00 PM - Comments (1)
Categories: C# , VB

    Next