Business Apps Example for Silverlight 3 RTM and .NET RIA Services July Update: Part 4: SEO, Export to Excel and Out of Browser

More from my Mix09 talk “building business applications with Silverlight 3”.

You can watch the original  video of the full session 

The demo requires (all 100% free and always free):

  1. VS2008 SP1 (Which includes Sql Express 2008)
  2. Silverlight 3 RTM
  3. .NET RIA Services July '09 Preview

Also, download the full demo files and check out the running application.

Today, we will talk about Different views of our application logic. 

Different Views

Now, let’s take take a look at putting different views on this same application.  One of the key elements to supporting different views is deeplinking.   Let’s look at adding that to this application.  

image

First, we need to add a textbox to display the deeplink for each of our super employees.

 <TextBlock Text="PermaLink:"></TextBlock>
 <TextBox x:Name="PermalinkTextBox" Width="400" Height="25" TextWrapping="NoWrap" Foreground="#FFB4B4B4" />

And just wire this up in code behind..

 private void dataGrid1_SelectionChanged(object sender, SelectionChangedEventArgs e)
 {
     var emp = dataGrid1.SelectedItem as SuperEmployee;
     if (emp != null)
     {
         PermalinkTextBox.Text = Application.Current.Host.Source.ToString().Replace("ClientBin/MyApp.xap", "") +
             "#/Home?EmpId=" + emp.EmployeeID;
     }    
  
 }

image_thumb[98]

Now we need to make the page smart enough to know about this deep link..

 protected override void OnNavigatedTo(NavigationEventArgs e)
 {
     var qs = NavigationContext.QueryString;
  
     if (qs.ContainsKey("EmpId"))
     {
         dds.FilterDescriptors.Add(
             new FilterDescriptor("EmployeeID",
                 FilterOperator.IsEqualTo, qs["EmpId"]));
     }
 }

Now, run it…  cut and paste this url into a browser brings up exactly this record. 

image_thumb[100]

OK, now that we have a deeplink, what can we do with it?  Well, you can send the link around in email, IM and in blogs.

But what if you wanted the search engines to be able to find all the items in your site?  It turns out there is a standard format for this called a sitemap (https://sitemap.org).  Check out https://amazon.com/robots.txt for an example in the real world.

To add this to our application, say File\New Item in the Server Project and select the “Search Sitemap”.

image_thumb[71]

This added a robots.txt and a sitemap.aspx.   Let’s look at customizing the sitemap.aspx file.  Notice we are using a new ASP.NET datasource control… that is designed to work with the DomainService class.  This gives us server access to the same application logic from Silverlight and ASP.NET. 

 <asp:DomainDataSource runat="server" ID="SitemapDataSource" 
     DomainServiceTypeName="MyApp.Web.SuperEmployeeDomainService" 
     SelectMethod="GetSuperEmployees" />
 <url>
     <loc><%= new Uri(this.Request.Url, this.ResolveUrl("Default.aspx"))+ "?EmpId="  %><%# HttpUtility.UrlEncode(Eval("EmployeeID").ToString()) %></loc>
     <lastmod><%# (Eval("LastEdit") as DateTime?).GetValueOrDefault().ToString("yyyy-MM-dd")%></lastmod>
 </url>

One last tweak to enable the server side of the app to access the employee ID..  in default.aspx.cs add:

 protected void Page_Load(object sender, EventArgs e)
 {
     string empId = Request.QueryString["EmpId"];
     var deepLink = "/Home?EmpId=" + empId;
  
     if (empId != null)
     {
         Response.Write("<script type=text/javascript>window.location.hash='#" + deepLink + "';</script>");
     }
  
  
 }

Now run it… 

image_thumb[73]

and the sitemap…

image_thumb[104]

Cut and pasting the URL brings open that item.

image_thumb[103]

OK… that works great.  Now the search engine can find every entity in our application.  But what will the search engine find when it gets there?  Search engines are great at parsing html, but dynamic content like we are seeing in this site or in rich ajax sites are a lot harder for search engines to deal with. 

image_thumb[82]

The art of addressing this is called SEO (Search Engine Optimization).    Let’s go and apply SEO principles to our application, open up BusinessApplicationTestPage.aspx  Because we have modeled our application logic in a domain service class it is very easy to add a standards based HTML view.    Just add this code into the object tag in default.aspx.. 

 <form id="form1" runat="server" style="height:100%">
              <asp:DomainDataSource runat="server" ID="DetailsDataSource" 
                        DomainServiceTypeName="MyApp.Web.SuperEmployeeDomainService" 
                        SelectMethod="GetSuperEmployee">
               <SelectParameters>
                   <asp:QueryStringParameter  Name="employeeID" Type="Int32" Querystringfield="EmpId" />
               </SelectParameters>       
           </asp:DomainDataSource>
          <asp:ListView ID="EmployeeDetails" runat="server"  DataSourceID="DetailsDataSource" >
             <LayoutTemplate>                  
               <asp:PlaceHolder ID="itemPlaceholder" runat="server"></asp:PlaceHolder>    
             </LayoutTemplate>
             <ItemTemplate>
                <h1><a href="<%# Eval("EmployeeID")%>" > <%# Eval("Name")%> </a>  </h1>
                  <table>
                  <tr> <td><b>Name:</b> <%# Eval("Name")%> </td> </tr>
                  <tr> <td><b>Publisher:</b> <%# Eval("Publishers")%> </td></tr>
                  <tr> <td><b>Gender:</b> <%# Eval("Gender")%></td> </tr>
                  <tr> <td><b>Origin:</b> <%# Eval("Origin")%></td> </tr>
                  <tr> <td><b>Issues:</b> <%# Eval("Issues")%></td> </tr>
                  <tr> <td><b>Sites: </b><%# Eval("Sites")%></td> </tr>
               </table>
             </ItemTemplate>
          </asp:ListView>
  
  

We need add a GetSuperEmployee domain method to support this code..  so back to our SuperEmployeeDomainService and add:

  
         public SuperEmployee GetSuperEmployee(int employeeID)
         {
             return this.Context.SuperEmployeeSet
                        .Where(emp => emp.EmployeeID == employeeID)
                        .FirstOrDefault();
         }

Now run it… Disable Silverlight in the browser because hey, search engines don’t have silverlight installed. 

image

Now hit refresh on a deep link…

image

Just for kicks… let’s run this application in lynx

image

image

Pretty cool, huh?  Now the search engine (or anyone without Silverlight) can access the data. 

But does it work?  well, try these searches:

Super employee placement Alfred (Yahoo)

Super Employee Placement Alfred (some other guy)

OK, that was a server view, let’s try a different client view..  Let’s export this data to the worlds best data manipulation tool – excel! 

Add the template file to the project to the client root. 

Then add an “export to excel” button:

 <Button Content="Export to Excel" 
         Width="105" Height="28"
         Margin="5,0,0,0" HorizontalAlignment="Left"
         Click="ExportToExcel_Click" ></Button>

And write out the excel file…  Notice this is all safe access as the Silverlight runtime proxies the user selection of the location to write the file.  The developer can not get direct access to the file location. 

 private void ExportToExcel_Click(object sender, RoutedEventArgs e)
 {
     var context = dds.DomainContext as SuperEmployeeDomainContext;
     var s = Application.GetResourceStream(new Uri("excelTemplate.txt", UriKind.Relative));
     var dialog = new SaveFileDialog();
  
     dialog.DefaultExt = "*.xml";
     dialog.Filter = "Excel Xml (*.xml)|*.xml|All files (*.*)|*.*";
  
     if (dialog.ShowDialog() == false) return;
  
     using (var sw = new StreamWriter(dialog.OpenFile()))
     {
         var sr = new StreamReader(s.Stream);
         while (!sr.EndOfStream)
         {
             var line = sr.ReadLine();
             if (line == "***") break;
             sw.WriteLine(line);
         }
  
         foreach (var emp in context.SuperEmployees)
         {
             sw.WriteLine("<Row>");
             sw.WriteLine("<Cell><Data ss:Type=\"String\">{0}</Data></Cell>", emp.Name);
             sw.WriteLine("<Cell><Data ss:Type=\"String\">{0}</Data></Cell>", emp.Origin);
             sw.WriteLine("<Cell><Data ss:Type=\"String\">{0}</Data></Cell>", emp.Publishers);
             sw.WriteLine("<Cell><Data ss:Type=\"Number\">{0}</Data></Cell>", emp.Issues);
             sw.WriteLine("</Row>");
         }
         while (!sr.EndOfStream)
         {
             sw.WriteLine(sr.ReadLine());
         }
     }
 }
     }

Run it…

image_thumb[83]

image

This creates an excel file on the user selected location.

image

Opening it and doing a bit of formatting…

image

And the final view… out of browser. 

Select properties on the client application

image_thumb[85]

Edit the settings.. 

image_thumb[86]

Run the application, right click and select “Install….”

image_thumb[88]

image_thumb[89]

Put a link on the desktop.. 

image

and run it!

image_thumb[90]