Still looking for a sponsor Max Paulousky is looking for a Silverlight/.Net job in the Commonwealth

Share to Facebook Tweet this! Share to MySpace Share to Google Share to Live   Share via AddThis

Search Engine Optimization (SEO) for Silverlight Applications. Part 1

Introduction

As I announced earlier here and here, I prepared a session for the remix.ru conference about SEO for Silverlight applications and published a demo application. Several days ago all my materials (Power Point presentation, video tutorial, sources etc) were published on the Techdays server. As the main language of the conference was Russian, I had prepared all stuff in Russian as well. It was the first reason to write detailed post here in English. The second reason was I had no time to cover all details of implementation in the video tutorial.

Basics

I am sure, all web developers hold the opinion that search engine optimization is one of the most important part of web-development. And you can be sure any successful web application has search engine optimized structure and content. If a web site is absent on a search portal (like Bing or Google), people says it is out of the Internet.

 image

It will be as difficult to find it, as difficult to find a meteorite size of a match head. I spend just several minutes to find a meteorite like one on the picture above. You can ask me ‘How is it possible?’. I do it so fast because celestial visitors are optimized for search and can be easily detected by special search tools.

Why SEO is important

As I mentioned above, SEO is very important for modern web sites. First of all, search engines produce majority of web site traffic. For example, about a half of visits of my blog are inspired by search engines. Some people say, search engines produce up to 80% of traffic for their sites. So, web developers cannot ignore this audience.

image

Secondly, all users start searching a resource from a search portal because it is the fastest way. And last but not least, large traffic leads to great deal of ad revenue.

Why search engines do not work with Ria directly

So, why?

To be honest, search engines do not support officially crawling of Flash or Silverlight content. There is every likelihood that they will support neither Silverlight/Flash nor other Ria applications.

First of all, such applications load data dynamically from a remote server. Search engines work with static content only and cannot analyze dynamic data.

Secondly, data is not accessible until the client code is executed. Lastly, search engines cannot process non-html code (Silverlight, Flash). Google, Microsoft and other search engine owners are not ready to pay for non-html code crawling support. The search giants shift the blame onto developers.

As a result, developers should prepare Ria applications for search crawlers and engines to generate optimal code.

What is the way out for Silverlight applications?

Just three features should be implemented to optimize Silverlight applications for search engines.

First of all, deep linking should be implemented. This feature allows getting access to any page within an application. And vice versa – a user should be able to store/copy a permalink for any displaying page. On the one hand, it will allow storing links in Favourites, send them to friends, publish in twitter etc.  On the other hand, search engines will be able to index particular pages.

If a search engine can access to any page by URL, developers should provide a list of URLs for all pages. Of course, references to pages from twitter or other web sites will be indexed by search engines. But this approach is not optimal because developers will spend a lot of time to provide the full list of URLs. The easiest way is to provide a sitemap to search engines. Sitemap is an xml file that contains enumeration of all pages and it is a standard for web sites.

Eventually, developers should provide static html content for search engines and other users if they do not have Silverlight plug-in. According to Microsoft’s top managers, about 60% of users have installed Silverlight plug-in and it would be foolish to ignore the second half of users (all search engines do not have Silverlight plug-in installed).

In most cases, developers use MVVM pattern to implement Silverlight applications. I think, it would be really helpful to base my demo application on that pattern. I have chosen the Prism implementation of MVVM pattern as the most popular one.

Demo application

I would like to implement a demo job board that can display:

  • All vacancies;
  • List of vacancies by a region;
  • Detailed information for a vacancy;

A user can:

  • Click an application’s header to display all vacancies;
  • Click a region name in the list of regions to display vacancies for the region;
  • Open a vacancy to get detailed information.

The application contains 6 projects:

  • SLJobBoardSeo.Infrastructure – common functions and classes for all projects;
  • SLJobBoardSeo.Modules.Dashboard – View and ViewModel for all/vacancies by region
  • SLJobBoardSeo.Modules.Details – View and ViewModel for vacancy details;
  • SLJobBoardSeo.ProxyService – contains RIA Services domain context auto generated code;
  • SLJobBoardSeo.Shell – contains routing logic and a root visual element of the application;
  • SLJobBoardSeo.Web – web and DomainService hosting application for the Silverlight application.

The application includes four xap modules that are loaded on demand:

  • SLJobBoardSeo.Modules.Dashboard.xap
  • SLJobBoardSeo.Modules.Details.xap
  • SLJobBoardSeo.ProxyService.xap
  • SLJobBoardSeo.xap

Deep linking

This part of the demo application is the most difficult and takes a lot of time to implement.

Initially, I have to add SEO links to the web host application. Then I have to do the same in the Silverlight application.

Adding routes support the web host applications

I am going to use the Route technique for building SEO links. So, I register routes in Global.asax.cs:

public class Global : System.Web.HttpApplication
{
  private void RegisterRoutes(RouteCollection routeCollection)
  {
    routeCollection.MapPageRoute("all-vacancies", "all-vacancies", 
                                 "~/Default.aspx", false);
    routeCollection.MapPageRoute("vacancies-in-{regionName}", "vacancies-in-{regionName}", 
                                 "~/Default.aspx", false);
    routeCollection.MapPageRoute("vacancy-{id}", "vacancy-{id}", 
                                 "~/Default.aspx", false);
  }
 
  protected void Application_Start(object sender, EventArgs e)
  {
    RegisterRoutes(RouteTable.Routes);
  }
}

I register all routes once on Application start. All route values are easy to understand by users and search engines. All routes reference to the same web page – Default.aspx. This web form has got a container that loads the main Silverlight module SLJobBoardSeo.xap.

The routes will be used to understand which Module and View should be loaded and displayed. Also, I am planning to use them later in a sitemap and to generate static html content too.

Adding routes support to the Silverlight application

I am going to use the Navigation framework to handle all requests and display particular page/control/form. I add information about routes for the Silverlight application to the App.xaml file:

<Nav:UriMapper x:Key="uriMapper">
  <Nav:UriMapping Uri="" MappedUri="/View/Module.xaml"/>
  <Nav:UriMapping Uri="/all-vacancies" MappedUri="/View/Module.xaml?module=DashboardModule"/>
  <Nav:UriMapping Uri="/vacancies-in-{region}" MappedUri="/View/Module.xaml?module=DashboardModule&amp;region={region}"/>
  <Nav:UriMapping Uri="/vacancy-{id}" MappedUri="/View/Module.xaml?module=DetailsModule&amp;id={id}"/>
</Nav:UriMapper>

In the ShellView.xaml file I add a Frame control and initialize UriMapper:

<navigation:Frame x:Name="ContentFrame" UriMapper="{StaticResource uriMapper}" >
</navigation:Frame>

All requests are redirected to the Module.xaml file with some QueryString parameters. This file contains all logic for loading different modules with different parameters. Look into it.

Module.xaml is not a UserControl (as usual in Silverlight applications). I use Page control because it has access to Navigation entities like NavigationContext. Also, I can handle the OnNavigatedTo event. It helps me to understand what module should be shown for the particular request.

protected override void OnNavigatedTo(NavigationEventArgs e)
{
  string moduleParam = NavigationContext.QueryString
    .FirstOrDefault(d => d.Key.Equals("module", StringComparison.OrdinalIgnoreCase)).Value;
 
  if (string.IsNullOrEmpty(moduleParam))
  {
    string moduleName = RoutingUtils.LoadModuleNameByDocumentUri(HtmlPage.Document.DocumentUri);
    ShowModule(moduleName);
  }
  else
    ShowModule(moduleParam);
}

I use NavigationContext to get access to QueryString Collection and analyze “module” QueryParameter. If this parameter is not empty, I use it to load and display corresponding xap module. If this parameter is empty, I try to get the module name from the current Uri. if moduleName is empty, I display the Dashboard module and all vacancies.

private void ShowModule(string moduleName)
{
  IModuleManager manager = Container.Resolve<IModuleManager>();
 
  if (!ViewContainer.ActivatedModules.Contains(moduleName))
  {
    manager.LoadModule(moduleName);
    ViewContainer.ActivatedModules.Add(moduleName);
  }
  else
  {
    string moduleType = Container.Resolve<IModuleCatalog>().Modules
                        .First(m => m.ModuleName == moduleName).ModuleType;
    IModule module = Container.Resolve(Type.GetType(moduleType)) as IModule;
    module.Initialize();
  }
}

The method ShowModule() gets IModulManager from the container. If a module has been ever loaded, I get full type name of the module from IModuleCatalog, load module itself by full type name from the container and call the Initialize() method. If the module has not been loaded, I use IModulManager to download and initialize module.

public void Initialize()
{
  RegisterViewsAndServices();
  RegisterView();
}
 
public void RegisterView()
{
  IRegion region = regionManager.Regions[Defines.RegionName.Main];
  region.RemoveAll();
 
  DetailsView view = container.Resolve<DetailsView>();
  IDetailsViewModel viewModel = container.Resolve<IDetailsViewModel>();
 
  view.DataContext = viewModel;
  viewModel.Initialize();
 
  region.AddAndActivateIfNotExists(view);
 
  viewModel.NavigationService = view.NavigationService();
  viewModel.NavigationContext = view.NavigationContext();
 
  container.Resolve<ActivationManager>().SwapToView(Defines.ModuleNames.Details);
  viewModel.LoadVacancy(viewModel.CurrentVacancyId);
}
 
protected void RegisterViewsAndServices()
{
  container.RegisterType<IDetailsViewModel, DetailsViewModel>();
}

In the module I register a ViewModel in the container. Then I:

  • Load a View and the ViewModel from the container;
  • Initialize DataContext of the View;
  • Add the View to the Main Region;
  • Initialize NavigationContext and NavigationService properties of the ViewModel; 
  • Show View;
  • Call a method to load a vacancy or list of vacancies;

ViewModels contain logic for displaying data. They load data using VacanciesDomainContext that is added to the container in the SLJobBoardSeo.ProxyService project. In addition, a ViewModel can navigate to other Views using NavigationService:

private void DoViewVacancy(object parameter)
{
  if (parameter == null)
    return;
 
  NavigationService.Navigate(new Uri(string.Format("/vacancy-{0}", 
    parameter.ToString()), UriKind.Relative));
}
 
private void DoViewVacanciesByRegion(object parameter)
{
  if (parameter == null)
    return;
 
  NavigationService.Navigate(new Uri(string.Format("/vacancies-in-{0}", 
    parameter.ToString()), UriKind.Relative));
}

That is the implementation of the Deep Linking feature.

I am going to share details of implementation of Sitemap and providing static html content in the second part of the article. Also, I am going to add my demo site to the search engines and show the result of indexing by search crawlers.

This work is licensed under a Creative Commons Attribution By license.

Leave a Comment [ RSS ]

  • ...

    Thank you for sharing this helpful information. It will be useful for us.

  • re: Search Engine Optimization (SEO) for Silverlight Applications. Part 1

    Requesting Gravatar... Cleyton says:

    Hi Max,

    Thanks for this article. I found it very interesting.

    I tried to find the source code so that I can follow the article with the source but since I do not understand Russian I could not find it.

    Could you please point me to the URL where I can find the source code?

    Cheers

    Cleyton

  • re: Search Engine Optimization (SEO) for Silverlight Applications. Part 1

    Cleyton, you can find links to sources at the very end of the second part of the article

  • re: Search Engine Optimization (SEO) for Silverlight Applications. Part 1

    Requesting Gravatar... Peter says:

    Hi - I'm not sure who the author was of this article, but there are some GREAT tips for on-page SEO here - thank you for taking the time to post them !

Comments have been closed on this topic.