ptg 1114 CHAPTER 23 Using Site Maps DataRow[] results = siteMapTable.Select(“ParentId=” + parentNode.Key); foreach (DataRow row in results) { SiteMapNode node = new SiteMapNode(this, row[“Id”].ToString(), row[“url”].ToString(), row[“title”].ToString(), row[“description”].ToString()); AddNode(node, parentNode); BuildSiteMapRecurse(siteMapTable, node); } } } } Like the custom Site Map provider that was created in the previous section, the SqlSiteMapProvider derives from the base StaticSiteMapProvider class. The SqlSiteMapProvider class overrides three methods of the base class: Initialize(), GetRootNodeCore(), and BuildSiteMap(). The Initialize() method retrieves a database connection string from the web configura- tion file. If a database connection string cannot be retrieved, the method throws a big, fat exception. Almost all the work happens in the BuildSiteMap() method. This method loads the contents of the SiteMap database table into an ADO.NET DataTable. Next, it recursively builds the Site Map nodes from the DataTable. There is one special aspect of the code in Listing 23.18. It uses a SQL cache dependency to automatically rebuild the Site Map when the contents of the SiteMap database table are changed. To enable SQL cache dependencies for a database, you must configure the database with either the enableNotifications tool or the aspnet_regsql tool. Use the enableNotifications tool when enabling SQL cache dependencies for a SQL Express data- base table, and use the aspnet_regsql tool when enabling SQL cache dependencies for the full version of Microsoft SQL Server. NOTE To learn more about config uring SQL cache dependencies, see Chapter 29, “Caching Application Pages and Data.” To enable SQL cache dependencies for a SQL Express database named SiteMapDB that contains a table named SiteMap, browse to the folder that contains the SiteMapDB.mdf file and execute the following command from a Command Prompt: enableNotifications “SiteMapDB.mdf” “SiteMap” From the Library of Wow! eBook ptg 1115 Creating Custom Site Map Providers 23 You can configure your website to use the SqlSiteMapProvider class with the Web config- uration file in Listing 23.19. LISTING 23.19 Web.Config <?xml version=”1.0”?> <configuration> <connectionStrings> <add name=”conSiteMap” connectionString=”Data Source=.\SQLExpress;Integrated Security=True;AttachDbFileName=|DataDirectory|SiteMapDB.mdf;User Instance=True”/> </connectionStrings> <system.web> <siteMap defaultProvider=”myProvider”> <providers> <add name=”myProvider” type=”AspNetUnleashed.SqlSiteMapProvider” connectionStringName=”conSiteMap” /> </providers> </siteMap> <caching> <sqlCacheDependency enabled = “true” pollTime = “5000” > <databases> <add name=”SiteMapDB” connectionStringName=”conSiteMap” /> </databases> </sqlCacheDependency> </caching> </system.web> </configuration> The configuration file in Listing 23.19 accomplishes several tasks. First, it configures the SqlSiteMapProvider as the default Site Map provider. The provider includes a connectionStringName attribute that points to the connection string for the local SQL Express database named SiteMapDB. The configuration file also enables SQL cache dependency polling. The application is configured to poll the SiteMapDB database for changes every 5 seconds. In other words, if From the Library of Wow! eBook ptg 1116 CHAPTER 23 Using Site Maps FIGURE 23.9 Displaying a Site Map from a Microsoft SQL database. you make a change to the SiteMap database table, the Site Map is updated to reflect the change within 5 seconds. You can try out the SqlSiteMapProvider by opening the Default.aspx page included in the SqlSiteMapProviderApp web application on the website that accompanies this book. If you modify the SiteMap database table, the changes are automatically reflected in the Site Map (see Figure 23.9). Generating a Google SiteMap File Google provides a free service, named Google SiteMaps, that you can use to monitor and improve the way that Google indexes the pages on your website. For example, you can use Google SiteMaps to discover which Google search queries have returned pages from your website and the ranking of your pages in Google search results. You also can use Google SiteMaps to view any problems that the Google crawler encounters when index- ing your site. You can sign up for Google SiteMaps by visiting the following URL: http://www.google.com/webmasters/sitemaps To use Google SiteMaps, you must provide Google with the URL of a Google SiteMap file hosted on your website. The Google SiteMap file is an XML file that contains a list of URLs you want Google to index. From the Library of Wow! eBook ptg 1117 Generating a Google SiteMap File 23 The Google SiteMap XML file has the following format: <?xml version=”1.0” encoding=”UTF-8”?> <urlset xmlns=”http://www.google.com/schemas/sitemap/0.84”> <url> <loc>http://www.example.com/</loc> <lastmod>2005-01-01</lastmod> </url> <url> <loc>http://www.example.com/sample.html/</loc> <lastmod>2006-03-11</lastmod> </url> </urlset> The Google SiteMap file contains a simple list of <url> elements that contain <loc> elements representing the location of the URL and <lastmod> elements representing the last modified date of the URL. NOTE The Google SiteMap file also can contain <changefreq> and <priority> elements. The <changefreq> element indicates how frequently a URL changes, and the <priority> element represents the priority of a URL relative to other URLs in your site. These elements are optional and are ignored here. You can generate a Google SiteMap file automatically from an ASP.NET SiteMap. The HTTP Handler in Listing 23.20 generates a Google SiteMap that conforms to Google’s require- ments for a valid SiteMap file. LISTING 23.20 PublicSiteMap.ashx <%@ WebHandler Language=”C#” Class=”PublicSiteMap” %> using System; using System.Web; using System.Xml; using System.Text; using System.IO; public class PublicSiteMap : IHttpHandler { private XmlWriter _xmlWriter; public void ProcessRequest (HttpContext context) { context.Response.ContentType = “text/xml”; From the Library of Wow! eBook ptg 1118 CHAPTER 23 Using Site Maps XmlWriterSettings settings = new XmlWriterSettings(); settings.Encoding = Encoding.UTF8; settings.Indent = true; _xmlWriter = XmlWriter.Create(context.Response.OutputStream,settings); _xmlWriter.WriteStartDocument(); _xmlWriter.WriteStartElement(“urlset”,”http://www.google.com/schemas/ ➥ sitemap/0.84”); // Add root node AddUrl(SiteMap.RootNode); // Add all other nodes SiteMapNodeCollection nodes = SiteMap.RootNode.GetAllNodes(); foreach (SiteMapNode node in nodes) AddUrl(node); _xmlWriter.WriteEndElement(); _xmlWriter.WriteEndDocument(); _xmlWriter.Flush(); } private void AddUrl(SiteMapNode node) { // Skip empty Urls if (String.IsNullOrEmpty(node.Url)) return; // Skip remote nodes if (node.Url.StartsWith(“http”, true, null)) return; // Open url tag _xmlWriter.WriteStartElement(“url”); // Write location _xmlWriter.WriteStartElement(“loc”); _xmlWriter.WriteString(GetFullUrl(node.Url)); _xmlWriter.WriteEndElement(); // Write last modified _xmlWriter.WriteStartElement(“lastmod”); _xmlWriter.WriteString(GetLastModified(node.Url)); _xmlWriter.WriteEndElement(); // Close url tag _xmlWriter.WriteEndElement(); } private string GetFullUrl(string url) From the Library of Wow! eBook ptg 1119 Generating a Google SiteMap File 23 { HttpContext context = HttpContext.Current; string server = context.Request.Url.GetComponents(UriComponents.SchemeAndServer, UriFormat.UriEscaped); return Combine(server, url); } private string Combine(string baseUrl, string url) { baseUrl = baseUrl.TrimEnd(new char[] {‘/’}); url = url.TrimStart(new char[] { ‘/’ }); return baseUrl + “/” + url; } private string GetLastModified(string url) { HttpContext context = HttpContext.Current; string physicalPath = context.Server.MapPath(url); return File.GetLastWriteTimeUtc(physicalPath).ToString(“s”) + “Z”; } public bool IsReusable { get { return true; } } } The HTTP Handler in Listing 23.20 generates an XML file by iterating through each of the nodes in an ASP.NET Site Map. The XML file is created with the help of the XmlWriter class. This class generates each of the XML tags. NOTE You can think of an HTTP Handler as a lightweight ASP.NET page. You l earn about HTTP Handlers in Chapter 31, “Working with the HTTP Runtime.” The file in Listing 23.21 contains the XML file returned by the PublicSiteMap.ashx handler when the Handler is called from the sample application contained on the website that accompanies this book. (The file has been abridged to save space.) From the Library of Wow! eBook ptg 1120 CHAPTER 23 Using Site Maps LISTING 23.21 PublicSiteMap.ashx Results <?xml version=”1.0” encoding=”utf-8”?> <urlset xmlns=”http://www.google.com/schemas/sitemap/0.84”> <url> <loc>http://localhost:2905/SiteMaps/Default.aspx</loc> <lastmod>2005-10-30T03:13:58Z</lastmod> </url> <url> <loc>http://localhost:2905/SiteMaps/Products/Default.aspx</loc> <lastmod>2005-10-28T21:48:04Z</lastmod> </url> <url> <loc>http://localhost:2905/SiteMaps/Services</loc> <lastmod>2005-10-30T04:31:57Z</lastmod> </url> <url> <loc>http://localhost:2905/SiteMaps/Employees/Default.aspx</loc> <lastmod>1601-01-01T00:00:00Z</lastmod> </url> <url> <loc>http://localhost:2905/SiteMaps/Products/FirstProduct.aspx</loc> <lastmod>2005-10-30T03:43:52Z</lastmod> </url> </urlset> When you sign up at the Google SiteMaps website, submit the URL of the PublicSiteMap.ashx file when you are asked to enter your SiteMap URL. The Google service retrieves your SiteMap from the handler automatically. Summary In this chapter, you learned how to work with Site Maps. The first section discussed the SiteMapDataSource control. You learned how to declaratively represent different sets of nodes in a Site Map with this control. Next, the SiteMap and SiteMapNode classes were examined. You learned how to create new Site Map nodes dynamically by handling the SiteMapResolve event. You also learned how to programmatically retrieve the current Site Map node in a page. The next section discussed several advanced features of Site Maps. You learned how to display different Site Map nodes to different users depending on their roles. You also learned how to merge SiteMap files located in different subfolders. Finally, you learned how to extend Site Maps with custom attributes. From the Library of Wow! eBook ptg 1121 Summary 23 We also built two custom Site Map providers. We created an AutoSiteMapProvider that automatically builds a Site Map that reflects the folder and page structure of a website. We also created a SqlSiteMapProvider that stores a Site Map in a Microsoft SQL Server data- base table. Finally, you learned how to use ASP.NET Site Maps with Google SiteMaps. In the final section of this chapter, you learned how to create a custom HTTP Handler that converts an ASP.NET Site Map into a Google SiteMap so that you can improve the way that Google indexes your website’s pages. From the Library of Wow! eBook ptg This page intentionally left blank From the Library of Wow! eBook ptg CHAPTER 24 Advanced Navigation IN THIS CHAPTER . Remapping URLs . Using the VirtualPathProvider Class . Summary Websites tend to be organic—they grow and change over time. This can create problems when other applica- tions link to your application. You need some way of modi- fying your website without breaking all the existing links to your website. In this chapter, you learn how to remap URLs. In other words, you learn how to serve a different page than the page a user requests. In the first section of the chapter, you learn how to remap URLs in the web configuration file. Next, you learn how to remap URLs by creating a custom HTTP module. Using a module is useful when you need to support wildcard matches and other types of pattern match- ing when remapping a URL. Finally, you learn how to use the VirtualPathProvider class to remap URLs. You learn how you can store all your website pages in a database. In the last section of this chapter, a simple Content Management System (CMS) is built with the VirtualPathProvider class. All these techniques are different than the techniques that use the new ASP.NET 4 Routing Engine. If your goal is to provide “permalink” type functionality or specifically map URLs to pieces of content (rather than .aspx pages), these techniques are for you. However, if your goal is to provide a more SEO-friendly, user-friendly, and possibly REST-friendly URL pattern for your entire website, you should not use the techniques in this chapter and instead skip to the chapter on the URL Routing Engine. From the Library of Wow! eBook . the SqlSiteMapProvider by opening the Default.aspx page included in the SqlSiteMapProviderApp web application on the website that accompanies this book. If you modify the SiteMap database table,. <siteMap defaultProvider=”myProvider”> <providers> <add name=”myProvider” type=”AspNetUnleashed.SqlSiteMapProvider” connectionStringName=”conSiteMap” /> </providers>. <url> <loc>http://localhost:2905/SiteMaps/Products/Default.aspx</loc> <lastmod>200 5-1 0-2 8T21 :48 :04Z</lastmod> </url> <url> <loc>http://localhost:2905/SiteMaps/Services</loc>