Tuesday, March 18, 2008

Putting It All Together - LINQ to SQL, ADO.NET Entity Framework, REST and Silverlight 2.0

A buddy of mine keeps bugging me about putting together something about Silverlight.  So, I thought I'd indulge him and put together an end to end example showing the use of ADO.NET Data Services as our data access layer, which in turn will publish a RESTful web service, that will be queried with LINQ and displayed in a Silverlight application.

Hopefully, this will be the first in many Silverlight posts, as the more and more I use the technology, the more and more it's potential strikes me.

To start, you're going to need a few things to execute this example:

  1. The usual suspects, Visual Studio 2008, SQL Server
  2. The AdventureWorks sample database
  3. Silverlight 2.0
  4. Silverlight Tools CTP from MIX 08
  5. ASP.NET Extensions

First thing we'll do is create a Silverlight application in Visual Studio.  Go to File -> New Project, and select Silverlight Application and click OK.  When Visual Studio prompts you about creating a test site or html page, select Web Site.

image

After creating our project, the next thing we'll do is create our LINQ to SQL entities.  To do so, right click on the web project (not the Silverlight application project) and select Add New Item, then select LINQ to SQL Classes.  Name the class AdventureWorks.dbml and click OK.

Create a connection to your AdventureWorks database in server explorer, and drag the Product table on to the design surface.  You should have a simple surface that looks like this:

image

Next we'll create our ADO.NET Data Service.  Right-click on your web project, choose Add New Item and select ADO.NET Data Service and name it ProductsDataService.svc.  Open the service code behind, and edit the class definition to include your LINQ to SQL DataContext like so, and then un-comment out the lines that restrict access to your entities.  You should have a service code behind that looks like the following:

public class ProductDataService : WebDataService<AdventureWorksDataContext>
{
    public static void InitializeService(IWebDataServiceConfiguration config)
    {
        config.SetResourceContainerAccessRule("*", ResourceContainerRights.AllRead);
    }
}

\When you run the service, you should see a page that looks like this:

image

Now that we have our REST service, we can move on to our Silverlight control.  I'd recommend going through Scott Guthrie's series of tutorials on Silverlight 2.0 if you need an introduction to using Silverlight.  The following goes on the assumption that you've gone through that article.

Our Silverlight application is going to be a simple control that lists out the top 20 products in the products table.  I'm going to use a grid layout with one column, two rows.  The top row will have a header, and the bottom row will have our list of products.  First we'll layout our grid, which will result in the following XAML markup in our Page.xaml file in our Silverlight project:

<UserControl x:Class="WillsProductCatalog.Page"
   xmlns="http://schemas.microsoft.com/client/2007"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml">
    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
    </Grid>
</UserControl>

Next thing we'll do is add our title to the control, which will make the markup look like so:

<UserControl x:Class="WillsProductCatalog.Page"
   xmlns="http://schemas.microsoft.com/client/2007"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:Data="clr-namespace:System.Windows.Controls;assembly=System.Windows.Controls.Data" >
    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" FontSize="20" Text="Will's Product Catalog" HorizontalAlignment="Center" />
    </Grid>
</UserControl>

Now, if we run the Silverlight project, we should get a test page that looks like this:

image

Next thing we'll do is add our ListBox.  Your final XAML markup for the page should look like this:

<UserControl x:Class="WillsProductCatalog.Page"
   xmlns="http://schemas.microsoft.com/client/2007"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" >
    <Grid x:Name="LayoutRoot" Background="White" ShowGridLines="True">
        <Grid.RowDefinitions>
            <RowDefinition Height="40" />
            <RowDefinition Height="*" />
        </Grid.RowDefinitions>
        <TextBlock Grid.Row="0" FontSize="20" Text="Will's Product Catalog" HorizontalAlignment="Center" />
        <ListBox Grid.Row="1" x:Name="productListBox" />
    </Grid>
</UserControl>

Now we can write our code that calls the RESTful service, and binds this data to the ListBox.  To do this, we'll open up the code behind for our Page.xaml, create a simple structure representing our product, and then in the constructor, we'll use the built in Silverlight networking and make the call to our service, hydrate our structure, and bind it to our list box.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Net;
using System.Xml.Linq;
 
namespace WillsProductCatalog
{
    public partial class Page : UserControl
    {
        public Page()
        {
            InitializeComponent();
            WebClient webClient = new WebClient();
            webClient.DownloadStringCompleted += new DownloadStringCompletedEventHandler(webClient_DownloadStringCompleted);
            webClient.DownloadStringAsync(
                new Uri("http://localhost:50492/WillsProductCatalog_Web/ProductDataService.svc/Products?$top=20&$filter=ListPrice%20gt%200"));
 
 
        }
 
        void webClient_DownloadStringCompleted(object sender, DownloadStringCompletedEventArgs e)
        {
            if (e.Error == null)
            {
                XDocument xLinqDoc = XDocument.Parse(e.Result);
 
                var products = from product in xLinqDoc.Descendants("{http://www.w3.org/2005/Atom}content")
                               select new Product
                               {
                                   ID = Convert.ToInt32(product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}ProductID").Value),
                                   Name = product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}Name").Value,
                                   ProductNumber = product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}ProductNumber").Value,
                                   ListPrice = Convert.ToDecimal(product.Element("{http://schemas.microsoft.com/ado/2007/08/dataweb}ListPrice").Value)
                               };
 
                productListBox.ItemsSource = products;
            }
        }
 
    }
 
    public class Product
    {
        public int ID { get; set; }
        public string Name { get; set; }
        public string ProductNumber { get; set; }
        public decimal ListPrice { get; set; }
    }
}

Its unfortunate that we can't use the ADO.NET Data Services classes (3.6 version of System.Web.Extensions) from Silverlight at this point, but I can understand that limiting the libraries in the Silverlight download keep it nice and small.  Obviously, if it were there, we could really cut down on the amount of code that we need to write here.

So, now that we've got our Listbox bound, you can see how this renders out in the browser:

image

Now, the last thing we need to do is use the Silverlight data binding syntax to put something other than the class name in the Listbox.  You can do that by changing your XAML syntax to the following:

<ListBox Grid.Row="1" x:Name="productListBox">
    <ListBox.ItemTemplate>
        <DataTemplate>
            <StackPanel Orientation="Horizontal">
                <TextBlock Text="{Binding ProductNumber}" Margin="5"/>
                <TextBlock Text="{Binding Name}" Margin="5"/>
                <TextBlock Text="{Binding ListPrice}" Margin="5"/>
            </StackPanel>
        </DataTemplate>
    </ListBox.ItemTemplate>
</ListBox>

And this will produce the following result:

image

Obviously, you can go much further with the formatting, and I'm hoping to be able to put together some smaller posts that highlight some of those formatting abilities.

8 comments:

Anonymous said...

I also wrote one tutorials how to use Astoria in Siverlight 2 beta 1 in my blog. I also used the way that you used for getting the data from astoria service but you can't use WebClient for inserting, deleting and updating data. I tried with WehHttpRequest and WebHttpResponse but it doesn't work. So, I had to use XmlHttpRequest wrapper to do CRUD before we got Astoria Silverlight Client...

Mark Fletcher said...

Hey Jim,

I hope you're on a short break from your blog - would love to see some articles on ASP.NET MVC... keep up the good work!

Mark

Security-Online said...

It was hard, but I still could poatorit your example. And has been impressed with the capabilities. Thank you for the article!
Richard Brown data room services

Thủ thuật smartphone said...

thực phẩm chức năng tăng chiều cao tốt nhất
thuốc tăng chiều cao tốt nhất
bí quyết tăng chiều cao ở tuổi 16

Tuyết Nhi said...

Nice post, I want to read more
http://phuongphaptangchieucao.info
http://tangchieucaohieuqua.info
http://thucdontangchieucao.net
http://thucdontangchieucao.org
http://canxi-nano.com
http://thucdontangchieucao.com
http://nano-canxi.com

Nhật Liên said...

Nice post can u visit my site :

https://toacotthong.com

https://satucunghoada.com/

http://www.tvbuy.vn/cach-tang-chieu-cao/dung-thuoc-tang-chieu-cao-o-tuoi-truong-thanh-thuc-su-co-tac-dung-174


http://www.tvbuy.vn/cach-tang-chieu-cao/nhung-cach-tang-chieu-cao-nhanh-nhat-cho-lua-tuoi-day-thi-154766

Unknown said...

Game
chamsocnhabep

Macey said...

Grateful for sharing thhis