Sigh… stay focussed, Dennis!

A couple of days ago I was playing around with Silverlight 4. Amazing platform, especially since they now support multi-touch events. But before you can run, you’ve got to learn how to walk, right? So, in order to learn some of the new capabilities of the platform I decided to make an application to keep track of all my contacts. I know, there are numerous applications out there that can peform that task much better, but I didn’t want to create a usefull program, I just wanted to have a ‘real’ project in order to see what Silverlight 4 does for me.

As you probably know, in Silverlight you have to make use of webservices whenever you want to get some data from the server. You cannot create a database connection from the Silverlight app directly so you have to call the server who will go to the database for you.

No problem, I’ve worked with WCF so many times before that this should be a no-brainer. I can probably do that with my eyes closed. As it turned out, I think I did just that. Read on about my stupid mistake and maybe you can spare yourself the humiliation of doing the same thing.

Let’s walk through the steps I took to get some data from the database into my SL app. I won’t bore you with all the details; in this post I will just recreate what I did but make it a lot simpler.

Of course, we need a database. In this case I have just one table, named Contacts. It looks like this:

image

ContactId is an Identityfield, FirstName is a nullable nvarchar(50) and LastName is non-nullable nvarchar(50). Just a basis table.

I added a Linq to SQL field in my web project and I am ready to go.

Now, I don’t want to pass the WebContact class to my SL client, so I decided to make a simple POCO class to hold the data. It’s redundant, but it gives me flexibility should I decide to change my data access strategy and don’t want to mess with my WCF interface. The class looks like this:

using System;

namespace MyStupidBugProgram.Web.WCF
{
    [Serializable]
    public class Contact
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

Nothing special about that one. I made it Serializable so I could, well, serialize it.

Next, the WCF Service:

using System;
using System.Linq;
using System.ServiceModel;
using System.Collections.Generic;
using System.ServiceModel.Activation;

namespace MyStupidBugProgram.Web.WCF
{
    [ServiceContract( Namespace = "" )]
    [AspNetCompatibilityRequirements( RequirementsMode = AspNetCompatibilityRequirementsMode.Allowed )]
    public class DataService
    {
        [OperationContract]
        public List<Contact> GetAllContacts()
        {
            using (var ctx = new Data.ContactClassesDataContext())
            {
                var allDBContacts = from dbContact in ctx.WebContacts
                                    select dbContact;

                var result = new List<Contact>();

                foreach (var dbContact in allDBContacts)
                {
                    result.Add( new Contact()
                    {
                        Id        = dbContact.ContactId,
                        FirstName = dbContact.FirstName,
                        LastName  = dbContact.LastName
                    } );
                }

                return result;
            }
        }
    }
}

I just get all the contacts in my database, move them to my new Contact class and return the List<Contact> with all those found items.

Let’s move to the Silverlight part.

I’ll add a new Service reference to the project containing my webservice (which is the same project that also houses the Silverlight ClientBin folder). When I added the service reference, I noticed that the generated Contact class had some weird looking fieldnames in it. The field Id for instance was called Idk__BackingFieldField. It had probably to do with some option I didn’t set correctly so I don’t really care about it (after all, it’s just a fool-around project, right?).

But I don’t like those names so I decide to add a new class, named WebContact that will hold the data for me. In fact, I’ll make it a viewmodel so I can use MVVM for this:

using System;
using System.ComponentModel;
using MyStupidBugProgram.DataServices;

namespace MyStupidBugProgram
{
    public class ContactViewModel : INotifyPropertyChanged
    {
        private Contact _Contact;

        public ContactViewModel(Contact contact)
        {
            _Contact = contact;
        }

        public event PropertyChangedEventHandler PropertyChanged;

        public int Id
        {
            get { return _Contact.Idk__BackingField; }
            set
            {
                _Contact.Idk__BackingField = value;
                RaisePropertyChanged( "Id" );
            }
        }

        public string FirstName
        {
            get { return _Contact.FirstNamek__BackingField; }
            set
            {
                _Contact.FirstNamek__BackingField = value;
                RaisePropertyChanged( "FirstName" );
            }
        }

        public string LastName
        {
            get { return _Contact.LastNamek__BackingField; }
            set
            {
                _Contact.LastNamek__BackingField = value;
                RaisePropertyChanged( "LastName" );
            }
        }

        private void RaisePropertyChanged(string propertyName)
        {
            var eventCopy = PropertyChanged;
            if (eventCopy == null)
                return;

            eventCopy( this, new PropertyChangedEventArgs( propertyName ) );
        }
    }
}

Notice the weird names on my Contact class…. Ah well, I’ve got them wrapped in my viewmodel so I don’t have to worry about them right now.

Ok. Let’s get some data. I create a ContactGetter class that will call my WCF service and gets the data:

using System;
using System.Windows;
using System.Collections.ObjectModel;
using MyStupidBugProgram.DataServices;

namespace MyStupidBugProgram.DataStuff
{
    public class ContactGetter
    {
        private DataServiceClient GetServiceClient()
        {
            Uri sourceUri = new Uri( Application.Current.Host.Source, "../WCF/DataService.svc" );

            var result = new DataServiceClient( "CustomBinding_DataService", sourceUri.AbsoluteUri );
            return result;
        }

        public void GetAllContacts(Action<ObservableCollection<Contact>> callback)
        {
            var client = GetServiceClient();
            client.GetAllContactsCompleted += (sender, e) =>
            {
                callback.Invoke( e.Result );
            };

            client.GetAllContactsAsync();
        }
    }
}

In the GetAllContacts method I pass an action which is called when the data is returned. Since all communications in Silverlight will be done asynchronously you will have to jump through some hoops to get the data but this works just fine. Of course, there are much better ways of doing this (I recommend the Rx framework, but that’s something for another post) but for this demo, this will do.

Note the construction of the sourceUri: this way I don’t have to change anything should I ever move the project from the webserver in Visual Studio to IIS or even to a hosting company: the code will figure out where the webservice is.

In my MainWindow I call this code like this: (don’t get confused by the lambda’s :-) )

First, I create a dependencyproperty of the type ObservableCollection<ContactViewModel> that will be the source for my listbox later on:

public static readonly DependencyProperty AllContactsProperty =
    DependencyProperty.Register(
        "AllContacts",
        typeof( ObservableCollection<ContactViewModel> ),
        typeof( MainPage ),
        new PropertyMetadata( null ) );

public ObservableCollection<ContactViewModel> AllContacts
{
    get
    {
        return (ObservableCollection<ContactViewModel>)GetValue( AllContactsProperty );
    }
    set
    {
        SetValue( AllContactsProperty, value );
    }
}

In the MainWindow constructor I get the data:

public MainPage()
{
    InitializeComponent();
    DataContext = this;
    // Get the data
    ContactGetter contactGetter = new ContactGetter();

    Action<ObservableCollection<Contact>> loadingAction =
        p =>
        {
            Action<ObservableCollection<Contact>> dispatchedAction =
                cvm =>
                {
                    AllContacts = new ObservableCollection<ContactViewModel>();
                    foreach (var contact in cvm)
                    {
                        AllContacts.Add( new ContactViewModel( contact ) );
                    }
                };
            Dispatcher.BeginInvoke( dispatchedAction, p );
        };
    contactGetter.GetAllContacts( loadingAction );
}

A little clarification might be in order:

When I call my contactGetter.GetAllContacts I pass in an Action<ObservableCollection<Contact>>. This action is called back from the ContactGetter.GetAllContacts whenever the data is retrieved from the server. Since it’s all asynchronously we don’t know when this will happen but we are guaranteed it will be on another thread. That’s the reason I created another action that will be given to the Dispatcher. The Dispatcher makes sure the processing of tthe data will be done on the UI thread so we can set the ObservableCollection<ContactViewModel> dependencyproperty (which is the itemssource for a listbox).

We could split the code up, it would look like this:

public MainPage()
{
    InitializeComponent();
    DataContext = this;
    // Get the data
    ContactGetter contactGetter = new ContactGetter();

    Action<ObservableCollection<Contact>> loadingAction =
        (Action<ObservableCollection<Contact>>)ProcessDataFromWCF;

    contactGetter.GetAllContacts( loadingAction );
}

private void ProcessDataFromWCF(ObservableCollection<Contact> p)
{
    Action<ObservableCollection<Contact>> dispatchedAction = (Action<ObservableCollection<Contact>>)ParseDataForUIThread;
    Dispatcher.BeginInvoke( dispatchedAction, p );
}

private void ParseDataForUIThread(ObservableCollection<Contact> cvm)
{
    AllContacts = new ObservableCollection<ContactViewModel>();
    foreach (var contact in cvm)
        AllContacts.Add( new ContactViewModel( contact ) );
}

It’s a bit more readable but not as cool. And next to that: my code will be littered with methods I never use but on one place. I don’t like that. When you get used to lambda’s they are much more readable.

Final detail: the view.Well, I just put the whole thing in my xaml:

<UserControl x:Class="MyStupidBugProgram.MainPage"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   mc:Ignorable="d"
   d:DesignHeight="300" d:DesignWidth="400">

    <Grid x:Name="LayoutRoot" Background="White">
        <ListBox ItemsSource="{Binding AllContacts}">
            <ListBox.ItemTemplate>
                <DataTemplate>
                    <Grid>
                        <Grid.ColumnDefinitions>
                            <ColumnDefinition />
                            <ColumnDefinition />
                        </Grid.ColumnDefinitions>
                        <Grid.RowDefinitions>
                            <RowDefinition />
                            <RowDefinition />
                            <RowDefinition />
                        </Grid.RowDefinitions>
                        <TextBlock Grid.Row="0" Grid.Column="0" Text="Id:"/>
                        <TextBlock Grid.Row="1" Grid.Column="0" Text="Voornaam:"/>
                        <TextBlock Grid.Row="2" Grid.Column="0" Text="Achternaam:"/>

                        <TextBlock Grid.Row="0" Grid.Column="1" Text="{Binding Id}"/>
                        <TextBlock Grid.Row="1" Grid.Column="1" Text="{Binding FirstName}"/>
                        <TextBlock Grid.Row="2" Grid.Column="2" Text="{Binding LastName}"/>
                    </Grid>
                </DataTemplate>
            </ListBox.ItemTemplate>
        </ListBox>
    </Grid>
</UserControl>

 

So we have a listbox, it’s bound to our DP AllContacts and in the ItemTemplate we bind the correct fields.

Let’s run it:

image

To quote Borat: great success!

At this point I was quite pleased with myself. I was so happy with it that I decided to put it on my website. So I published the whole project to my hoster. I did, it published, I ran it and then….

Nothing.

Empty screen.

No error message.

Mmm.

I checked the whole thing. Did it deploy correctly? Yes. Did I change to connectionstring to the correct database? Yes.

Then I noticed this tiny little error icon in the leftbottom corner of my webbrowser. Double clicking this produced the next result:

image

What could this be? It works on my machine.. (I’ve heard that one before).

I tried using my own IIS and that worked fine. Why didn’t the server at the hoster work? It runs Silverlight 4, I’ve tested that. I have the ClientAccessPolicy.xaml in the root, although I don’t need it in this case. What is the cause?

I guess it’s debug time… But how to debug when it all works locally? I decided to write a small .aspx page to read out the data from the wcf service. Just to skip the Silverlight application all together. And then I got this:

image

Not using HTTP protocol? And why is that a problem? Why doesn’t that work on the remote server? What does “This could be due to the service endpoint binding not using the HTTP protocol” mean?

Then it all became clear… if it doesn’t use the HTTP protocol, or doesn’t use it correctly, it probably uses some form of binary serialization of my data. Uhm… wait a minute. Let’s go back to the service I started out with:

using System;

namespace MyStupidBugProgram.Web.WCF
{
    [Serializable]   
    public class Contact
    {
        public int Id { get; set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
    }
}

Ah.

I see the problem.

Let’s change it to:

using System;
using System.Runtime.Serialization;

namespace MyStupidBugProgram.Web.WCF
{
    [Serializable]   
    [DataContract]
    public class Contact
    {
        [DataMember]
        public int Id { get; set; }
        [DataMember]
        public string FirstName { get; set; }
        [DataMember]
        public string LastName { get; set; }
    }
}

Now it’s a datacontract. Now WCF knows how to serialize this before giving this to my Silverlight application.

Let’s rebuild, let’s refresh the service reference, let’s test local (it works) and let’s deploy.

Et voila, it works on the remote machine as well.

Focus, Dennis, focus!!!

Print | posted @ Monday, June 21, 2010 2:07 PM

Comments on this entry:

No comments posted yet.

Post A Comment
Title:
Name:
Email:
Comment:
Verification: