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.

Thursday, March 06, 2008

Thoughts on General Purpose Implicit Typing

Ilya over at JetBrains recently blogged about the why the latest version of ReSharper is suggesting that you use the var keyword and implicitly type your variables as opposed to explicitly declaring types. 

I'm not sure if I'm down with this idea (yet).  Seems to me like this is taking a feature that was designed to tackle specific need for a compiler-deferred type assignment (Lambda and LINQ), and applying it with too broad of a stroke.

I don't disagree with Ilya's arguments in an idealistic sense, which tend to be focused around intuitive naming of your methods and properties.  That is no doubt are a great thing, but does the naming focus and implicit typing really make code better?  Will it help people who have difficulty naming methods and properties any better at it?  Saying that makes me think I'm saying "build practices for the lowest common denominator", but I think that naming can be a difficult thing sometimes.  Are there cases out there where type can't be inferred or formed from method name?

Also, I think there's something to be said about the fact that when reading C# your eyes focus on the left hand side of the assignment, and with the var keyword you're widening the focus needed to determine type.  Even if the variable is named in a manner that is blatantly descriptive of it's type, it still is easier to read for me.  For instance read the two examples below. 

var cat = GetCat();
var dog = GetDog();
var animal = GetAnimal();
 
Cat cat = GetCat();
Dog dog = GetDog();
Animal animal = GetAnimal();

Obviously the second one was easier to determine type.

For me, in principal, it all comes down to is whether or not it's true that the var keyword is a feature in the language or an evolution of the language.

For now, I'm going to hold off on this refactoring until these thoughts shake out a bit.

I'd like to hear what the C# language designers think about this.  I'd love to hear your thoughts as well.

Tuesday, March 04, 2008

"Going to Bat" For Your Team

After my premature departure from my four year institution, I got into the restaurant business.  And by getting into the restaurant business, I mean, I started driving pizzas for a living.  After a year of living the good life, my manager approached me about taking the dive into the shitty glorious job of managing restaurants.   I took it on because I saw it as a challenge, even though it was a pretty huge pay cut (if you don't care much for your car, the pizza delivery occupation can be relatively lucrative.)  Moving up through the ranks of the the chain restaurant business is pretty straight forward: shift manager to assistant manager, assistant manager to restaurant manager, and so on.

Eventually, I became next in line for a restaurant manager position, and finally a store opened up.  The outgoing manager I was filling the position for was an intelligent and outspoken woman, who was leaving to be a full-time parent.  She had built a really great team there.  They were motivated and believed in team work, which had resulted in extremely low turnover (relative to the business).  The team loved her, and they were all pretty sad to see her go, so I was moving into a tough transition.

One of the more senior people on this team was a delivery driver named Roger, who had been there for like 10 years, which is an eternity in that business.  Roger was a religious guy, but not in a "read the bible in the corner on your break" kind of way.  What he would do is drop little comments, about how what you were doing wasn't virtuous, which on occasion was pretty funny. 

After a couple of weeks I decided to grab a couple of the more senior people and see what they missed about her (which looking back was pretty smart, considering all the other stupid stuff I was doing in my life at the time.) 

In addition to being great about pointing out the fact that I was going to "H-E-double hockey sticks", Roger was extremely insightful and candid.  What he said has always stuck with me:

"She went to bat for us."

It so was simple, but striking to me (pun intended.)  What was most important to him is that she would make sure that any issues he had were paid attention to and followed through on.  If he had a grievance, she stepped up the plate and addressed it right away.  If she noticed that there was something destructive on his plate, she went to bat for him and worked to remove it, whatever it took.  On occasion she struck out, but what was important to him is that she stepped up and made the effort.

I took his advice, and tried to carry that on there.  We did lose some people, and we had to do some rebuilding.  But I think there were quite a few people that stayed because I went to bat for them.  Roger was there dropping hints about my looming trip to purgatory until the day I left.

To this day, what he said sticks with me, and I've tried to make it part of the principles that I live by.  Go to bat for your team.