For the uninitiated, Pandora is a
popular Flash-based internet radio service: users create their own radio
stations by seeding an artist or song, and by giving a thumb-up or thumb-down
to played songs. The software responds by playing more of what the user likes
based off these inputs.
In this article, I'm going to show you how to build a Pandora-like
music service using Silverlight 4, WCF, and Entity Framework. The end result is Chavah ("HA-vah"), a fun Silverlight clone of Pandora which can be seen live at judahhimango.com/chavah.
Introduction
Why build a Pandora clone, you ask?
I have a selfish motivation: Pandora doesn’t play the music
I’m most interested in. As a Jewish follower of Yeshua (Jesus), I enjoy a tiny
genre of religious music called Messianic Jewish music, a genre so small
Pandora doesn’t know about any of our music.
I thought, “Why not build a Pandora clone to play some great
Messianic Jewish tunes?” It would serve myself primarily, but also others in our community. And it'd raise
the awareness of some of the great Messianic music out there to boot.
But a more general motivation is, “Why not?” If anything,
it’s a great learning experience to expose one’s self to a full client/server
application using Silverlight, WCF, and Entity Framework. These technologies
are currently popular and in demand by employers, so it’s great résumé fodder
to boot.
Out of these motivations, the Chavah project was born. Chavah is
my attempt to build a Pandora-like clone that plays Messianic Jewish music, and
is the subject of this article.
Why Silverlight?
During the 2010
Professional Developer’s Conference, Microsoft hyped up IE9 and its HTML 5
support, without commenting much on Silverlight. Because of this, there has
been speculation as to whether Silverlight would fade out in favor of HTML 5;
folks questioning why to use Silverlight over HTML 5.
While Microsoft has since reiterated their long term support
for Silverlight, the question of why use Silverlight was a very real question
when starting a web app like Chavah.
My reasons for ultimately choosing Silverlight are
pragmatic:
- Silverlight is more cross-platform than HTML 5. Most of my target audience is still running browsers with limited or no HTML 5 support. And even for the few that do have HTML 5-compatible browsers , the support is neither consistent across browsers, nor stable. My target audience is Windows and Mac, and Silverlight runs great on both, right now, in most any web browser.
- The tooling kicks ass. Visual Studio + Blend is a hard combo to beat. C# is a great language with great tool and refactoring support. Animations, paths, soft UI edges, glow effects, drop shadows, and all things UI sexy are easy with Blend.
- Silverlight will always be a step ahead of the HTML spec. By the time HTML 5 has broad reach, Silverlight will have features that only HTML 6 will bring. This is the nature of design-by-committee of the HTML spec. Silverlight will innovate and bring new features plain old HTML is missing, and will deliver it faster and broader than the HTML spec ever will.
- Silverlight is an app development platform; HTML was intended for documents. Chavah is an application, not a document. HTML + Javascript + CSS hacks can turn a document into an app, but ultimately, a platform built for apps is more compelling for an application like Chavah.
- Since Chavah is an application, I want users to be able to install Chavah locally onto their desktops. With Silverlight’s out-of-browser capabilities, users can optionally install Chavah as a native application. This simply would not be possible with HTML + CSS + Javascript, barring browser-specific hacks like IE9’s “pinned sites” or Google Chrome’s “application shortcuts”.
\
Points of Interest
In showing how Chavah is built, this article will show you how you can simulate the look and feel of apps like Pandora – smooth UI layout, animations,
fluid feel – all in Silverlight 4. I’ll show you how a real-world application can make use of some of the new features in Silverlight, like out-of-browser
functionality, easing animations, and GPU acceleration.
Additionally, we’ll cover communication with the backend WCF
web service where the “pick a song based off the user’s likes and dislikes”
logic is located.
I’ll show you how to add some fun social features to your
application using WCF, Entity Framework, and SQLite.
Also, I’ll touch on some upcoming sexy tech -- Reactive
Extensions and Code Contracts – and offer my take on their viability and utility in modern apps.
One thing to note: while Chavah is inspired by Pandora, be
advised that, unlike Pandora, Chavah does not use Music Genome Project
to predict what kind of music you’ll like. That is beyond the scope of this project. Chavah instead
plays everything until you start thumbing up and down songs, at which point it
uses a special algorithm to choose a song based on your preferences. We’ll
cover this simpler algorithm later in this article.
Using the Code
When it comes to visuals, the meat-and-potatoes of Pandora are individual song tiles appearing on the right side of the screen, fluidly moving into position. Once in position, the song starts playing. The user can then pause the song, skip the song, thumb-up the song, or thumb-down the song.
This is the first part we’ll tackle – how do we build a UI
control like Pandora’s song tile pictured above? And how can we fluidly move it
into place like that?
Building a Song Tile
The first order of business is building a song tile.
As a testament to Silverlight’s extensible UI framework, we’re
going to build the song tile control entirely from the built-in controls in the
Silverlight framework. We’ll customize the look and feel for some of the
components, but there’s no special 3rd party toolkits used here,
it’s all in the box.
The control outline
is quite simple:
As you can see, there’s nothing too fancy going on here:
We’ve got a Border
that holds the whole tile. Achieving the rounded corner
effect is simple as setting the Border’s CornerRadius property.
The only part of this UI that required any amount of work is
the thumb-up and thumb-down buttons. Functionally, they are RadioButtons
: if
one is checked, the other is unchecked. (That is, you cannot simultaneously
thumb-up and thumb-down a song.)
Obviously, the built-in RadioButton’s look and feel won’t
do. We need to change the look of the radio button to show the thumb up/down.
To accomplish this, you can build a custom ControlTemplate
for the radio buttons. For Chavah’s “thumb up” button, it’s dead simple:
<ControlTemplate x:Key="ThumbUpStyle" TargetType="RadioButton">
<Grid>
<Image x:Name="imageUnchecked" Width="16" Height="16"
Source="http://judahhimango.com/Chavah/asyncImages/thumbUp.png" />
<Image x:Name="imageChecked" Width="16" Height="16"
Opacity="0" Source="http://judahhimango.com/Chavah/asyncImages/thumbUpChecked.png" />
<VisualStateManager.VisualStateGroups>
<VisualStateGroup x:Name="CheckStates">
<VisualState x:Name="Checked">
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="imageChecked"
Storyboard.TargetProperty="(UIElement.Opacity)" To="1" />
</Storyboard>
</VisualState>
<VisualState x:Name="Unchecked">
<Storyboard>
<DoubleAnimation Duration="0:0:0.2" Storyboard.TargetName="imageChecked"
Storyboard.TargetProperty="(UIElement.Opacity)" To="0" />
</Storyboard>
</VisualState>
</VisualStateGroup>
</VisualStateManager.VisualStateGroups>
</Grid>
</ControlTemplate>
The control template defines 2 images: a “checked” thumb up,
and an “unchecked” thumb up. When the RadioButton goes from checked to
unchecked and vice-versa, we simply hide one image and show the other using an
opacity animation. End result is a decent-looking thumb-up control with a nice
animation effect when switching between checked states:
Silverlight’s control templating made it dead simple to
create a custom radio button similar to Pandora’s thumb up control.
You’ll notice we have
a number between the thumb up and thumb down buttons. That’s part of a fun
social feature we’ll cover momentarily.
Animating the song tiles into position
I mentioned earlier that when Pandora plays a song, rather
than the song tile suddenly appearing out of nowhere in a jarring fashion, an
edge of the tile appears on the right-side of the screen and fluidly moves into
place.
This effect is part of a broader effort to build fluid UIs,
something gaining popularity in the software development world, particularly in
the rich internet application niche.
How can we achieve the same effect in Silverlight?
My first thought was to use Silverlight’s built-in animation
system to animate the margin of the tile: make the song title’s margin a large
number so that it’s off screen, then animate it into position.
In WPF, we’d accomplish this by animating the song control's margin using a ThicknessAnimation.
Unfortunately, as of Silverlight 4, Silverlight does not have a ThicknessAnimation.
Having consulted the mighty google gods, I found others have
worked around this problem using a custom
ThicknessAnimation. I’ve customized it further with some easing animations,
more on that in a minute:
public class ThicknessAnimation
{
public static DependencyProperty TimeProperty = DependencyProperty.RegisterAttached("Time",
typeof(double), typeof(DoubleAnimation), new PropertyMetadata(OnTimeChanged));
public static DependencyProperty TargetProperty = DependencyProperty.RegisterAttached("Target",
typeof(DependencyObject), typeof(ThicknessAnimation), null);
public static DependencyProperty FromProperty = DependencyProperty.RegisterAttached("From",
typeof(Thickness), typeof(DependencyObject), null);
public static DependencyProperty ToProperty = DependencyProperty.RegisterAttached("To",
typeof(Thickness), typeof(DependencyObject), null);
public static DependencyProperty TargetPropertyProperty = DependencyProperty.RegisterAttached(
"TargetProperty", typeof(DependencyProperty), typeof(DependencyObject), null);
public static Timeline Create(DependencyObject target, DependencyProperty targetProperty,
Duration duration, Thickness from, Thickness to)
{
DoubleAnimation timeAnimation = new DoubleAnimation() { From = 0, To = 1, Duration = duration };
timeAnimation.EasingFunction = new ExponentialEase() { Exponent = 9,
EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut };
timeAnimation.SetValue(TargetProperty, target);
timeAnimation.SetValue(TargetPropertyProperty, targetProperty);
timeAnimation.SetValue(FromProperty, from);
timeAnimation.SetValue(ToProperty, to);
Storyboard.SetTargetProperty(timeAnimation, new PropertyPath("(ThicknessAnimation.Time)"));
Storyboard.SetTarget(timeAnimation, timeAnimation);
return timeAnimation;
}
private static void OnTimeChanged(DependencyObject sender, DependencyPropertyChangedEventArgs e)
{
DoubleAnimation animation = (DoubleAnimation)sender;
double time = GetTime(animation);
Thickness from = (Thickness)sender.GetValue(FromProperty);
Thickness to = (Thickness)sender.GetValue(ToProperty);
DependencyProperty targetProperty = (DependencyProperty)sender.GetValue(TargetPropertyProperty);
DependencyObject target = (DependencyObject)sender.GetValue(TargetProperty);
target.SetValue(targetProperty, new Thickness((to.Left - from.Left) * time + from.Left,
(to.Top - from.Top) * time + from.Top,
(to.Right - from.Right) * time + from.Right,
(to.Bottom - from.Bottom) * time + from.Bottom));
}
public static double GetTime(DoubleAnimation animation)
{
return (double)animation.GetValue(TimeProperty);
}
public static void SetTime(DoubleAnimation animation, double value)
{
animation.SetValue(TimeProperty, value);
}
}
Getting funky with Silverlight easing animations
Draw your attention to the animation creating code, you’ll notice
we’re using Silverlight’s new easing animations:
timeAnimation.EasingFunction = new ExponentialEase()
{
Exponent = 9,
EasingMode = System.Windows.Media.Animation.EasingMode.EaseOut
};
An easing animation is a
kind of animation that, like the name suggests, eases from the source state to the target state. Easing animations are important in that it helps build a fluid feel to
the application.
Without any animation, a song tile just appearing out of
nowhere feels jarring.
With plain vanilla animations, sliding in the
song tile will feel mechanical.
Only with easing animations can we
achieve the warm fuzzy gooey fluidity that makes users sh*t their pants in awe.
Easing animations are comprised of 2 parts:
- Easing function
- Easing mode
For easing functions,
you’ve got an impressive list of options (hat tip Mike
Snow):
- BackEase – Moves the element backwards by an
amount specified through its amplitude before moving forward.
- BounceEase – Creates an effect like a bouncing
element.
- CircleEase – Accelerates the animation based upon
a circular function.
- CubicEase – Accelerates the animation based upon a cubic function.
- ElasticEase – Uses springiness and oscillation
to animate.
- ExponentialEase – Accelerates the animation
based upon an exponent value.
- PowerEase – Accelerates the animation based upon
a power of time.
- QuadraticEase – Accelerates the animation based
upon the square of time.
- QuarticEase – Accelerates the animation based
upon the cube of time.
- QuinticEase – Accelerates the animation based
upon the time to the power of 5.
- SineEase – Accelerates the animation along a
sine wave.
And for easing modes,
- EaseIn – start the animation slow, then
accelerate to full speed
- EaseOut – start the animation full speed, then
decelerate to end
- EaseInOut – start the animation slow, accelerate
to full speed, then decelerate to end
For Chavah, we’ll use the ExponentialEase function with the
EaseOut mode. The result is a song tile that shows up in the UI quickly, then
slowly rests into place. Nice and smooth. The users will have to change their underwear
after seeing this.
Using the animation
Now that we’ve done the hard work of defining a
ThicknessAnimation, and got all gooey
about the easing animation stuff, actually using the animation is a piece of
cake:
Create a storyboard, add our thickness animation to the
storyboard, and .Begin() the animation:
var animationTime = TimeSpan.FromSeconds(1.5);
var timeline = ThicknessAnimation.Create(control, SongControl.MarginProperty,
new Duration(animationTime), new Thickness(5, 5, -150, 5), new Thickness(5, 5, 5, 5));
Storyboard storyBoard = new Storyboard();
storyBoard.Children.Add(timeline);
storyBoard.Begin();
We’re animating the song tile’s right margin from -150 (e.g. pushed off the right side of the screen) to 5, such that when the song is added to the UI, it will shift all the existing song tiles over, smoothly making room for the new UI. Nice and fluid.
Using these effects, we’ve imitated Pandora’s song tile
control, including its rounded look and fluid animations with very little developer effort. Pure woot!
Installing Silverlight apps onto users desktops
Since Chavah is an application, I’d like users to be able to
run the application from their desktops without having to deal with browsers,
addresses, bookmarks, etc. They should be able to run my app without having to launch a web browser.
One relatively new feature of Silverlight is the
out-of-browser support. With out-of-browser, you can give users the opportunity
to install your application with optional shortcuts on the desktop and start
menu.
For Chavah, I wanted the install process to be completely
optional and, from a UX point of view, out of the way. For many users, in-your-face
installation prompts are scary and generally a pain point with desktop software.
My target audience may not be software-savvy, so I do not wish to scare them
away with installation prompts.
To avoid the in-your-face scary installation prompts, Chavah
will be installed only if the user deliberately clicks an innocuous, subtle
link:
We provide a tool tip for that link, “Install Chavah onto
your desktop”.
Clicking that link will trigger will run installation code.
Silverlight out-of-browser installation code is trivial:
if (!Application.Current.IsRunningOutOfBrowser &&
Application.Current.InstallState != InstallState.Installed)
{
var success = Application.Current.Install();
…
}
The Application.Current.Install()
method is all it takes.
When invoked, Silverlight will prompt the user to install. Here comes the scary
installation prompt:
Not too bad. Again, users only get here if they deliberately
try to install it.
The install experience is nice: it takes milliseconds to
install, the prompt does not have any scary red X’s about my app harming the
user’s system, and the user doesn’t have to do anything besides click “OK”.
Contrast this with typical installs, where you’ve got an admin privileges prompt, a frightening screen asking if you’re sure you
want to download an .exe which will
destroy your C:\ drive, a multi-step installation wizard, EULA agreements,
install directory choices, install component customization, and other
superfluous stuff that makes users afraid to install desktop applications on Windows.
Yeah, Silverlight is a step up from that, thank the good
Lord. It's close to the painless, frictionless experience you get with iPhone/iPad app installations.
If the user clicks OK to install, Chavah will be installed
like a typical application, with shortcuts on the start menu and desktop:
Additionally, the application shows up in the installed
programs like a typical native application:
If the user runs the app from their desktop or start menu,
it looks like a standard desktop application, no browser chrome needed:
With Silverlight’s out-of-browser functionality, you can
turn your web app into a desktop app with native window chrome, start menu and
desktop shortcuts, app icon, all that you’d expect from a normal install, but
without the security problems.
Perfect, and exactly what I was hoping for with Chavah.
Making use of your $500 video card
Unlike WPF, which is Windows-only and has full access to
DirectX and the power of all your local hardware at its fingertips, Silverlight
runs mostly on the CPU.
But with a recent addition to Silverlight, you can now offload
portions of your UI to the GPU for better rendering performance.
There is a lot of confusion and misinformation on the web
about how to do this. (Here’s to hoping I don’t contribute to that, cheers! )
Most people seem to
think passing the “EnableGPUAcceleration=true” parameter to your Silverlight
object in the HTML page is all that’s required.
This is a false assumption.
To make use of the GPU, 2 things must happen:
- Pass the “EnableGPUAcceleration=true” parameter
to your Silverlight object in the HTML page. (If you’re running out of browser,
you’ll also need to set the “Enable GPU Acceleration” option in your
out-of-browser publish settings in Visual Studio.)
- Set the CacheMode to BitmapCache for all the UI
elements (or a top-level element) you want accelerated by the GPU.
For Chavah, I wanted the song tiles to be GPU accelerated; I
am animating them, I’m changing their opacity, etc. This seems like a good
candidate for GPU acceleration.
To do this, I made
these changes:
In the HTML document containing the Chavah app, I’ve passed
in the parameter indicating GPU acceleration:
<object data="data:application/x-silverlight-4,"
type="application/x-silverlight-4" width="100%" height="100%">
<param name="EnableGPUAcceleration" value="true" />
…
</object>
Additionally, I’ve set the CacheMode on our song tile control:
<UserControl x:Class="JudahHimango.Chavah.SongControl"
...
CacheMode="BitmapCache"
...
</UserControl>
Also, since we’re potentially running out of browser, we
need to enable GPU acceleration there, too. You can do this through the
Silverlight project’s properties page:
As great as Silverlight GPU acceleration is, the whole world
is not [double] rainbows and unicorns; there are some important caveats as of
Silverlight 4:
Some UI pieces cannot
be hardware accelerated:
- Effects (Blur, DropShadow, etc)
- WriteableBitmap
- PerspectiveTransforms
- Non-rectangular clipping
- OpacityMasks
Also, some hardware/OS combinations cannot do GPU
acceleration at all:
- On Windows XP, if your video card is not from
NVidia, ATI, or Intel, or if your driver date is older than November 2004, you
won’t get any GPU acceleration.
These restrictions aren’t too terrible. The only one that
affects Chavah is the Effects limitation – we use the DropShadowEffect pretty
heavily, and those won’t be accelerated. That said, we have few enough effects
on screen at once that the load on the CPU is minimal. In fact, at runtime,
Chavah typically runs at < 1% of my 2.8 GHZ CPU. Not bad at all.
So, with all these caveats and steps to getting GPU acceleration
in your Silverlight app, how do you know it’s working? How do you know you set
everything up correctly and that your desired elements are being passed into the GPU pipeline?
The answer is provided through a debug parameter you can
send to your Silverlight object in the HTML:
<param name="enableCacheVisualization" value="true" />
With that debug parameter in place, Silverlight will tint
each element that is not hardware accelerated. Running Chavah with that in
place, you’ll see our song tiles are indeed accelerated:
Silverlight has tinted all non-accelerated elements. As you
can see, the song tiles are not tinted (ignore the opacity on the
already-played ones). This indicates Silverlight is sending these UI element bitmap caches through the GPU
hardware acceleration pipeline.
For more information about Silverlight’s GPU acceleration
capabilities, I encourage you to read MSDN's comprehensive
article.
Social cloud collaboration = buzzword overload (but a cool feature)
With Chavah, I wanted to go a step further than Pandora and
promote a sense of community through the software. With Chavah being targeted
at a small, niche community, wouldn’t it be cool to have some features where
the community works together to exalt the good songs and bury the bad?
In particular, I wanted to add some social features to
Chavah to promote community and raise awareness of great Messianic Jewish
music:
- Messianic community’s combined rank for each song (up
vote = +1, down vote = -1)
- Messianic community’s top-ranked songs
- Messianic community’s trending songs (recently
up-voted songs)
- Total songs streamed
And of course, we want Chavah to be intelligent and play
more of what the user likes, and less of what he doesn’t like.
To accomplish these features, I used a WCF web service on
the backend. This works nicely; instead of writing code to communicate with the
server, Silverlight can generate a nice asynchronous API to perform calls on
the server and get results back.
For data storage on the server, because Chavah doesn’t need
huge scale, I didn’t need the full SQL server. Even SQL Express was more than I
needed.
What I really wanted was a low-impact, single-file database,
no services required. Also, I didn’t want to map objects to the database by
hand; I wanted some kind of ORM on top that made it real easy.
While I toyed with the idea of going No-SQL, what ended up
working for me was going with the free SQLite
database. The ADO.NET driver for this is also free: System.Data.SQLite.
For object-relational mapping, I was hoping to avoid writing
ADO.NET data reader code by hand. I was pleasantly surprised to find Entity
Framework can be used atop SQLite, complete with full design-time support.
Entity Framework will generate all the mapping code for you, leaving you to
write simple LINQ queries to get at your data. Perfect.
The end result is nice clean code that looks like this:
public Song[] GetTrendingSongs(int count)
{
using (var entities = new ChavahEntities())
{
var trendingSongs = from like in entities.Likes
where like.LikeStatus == true
orderby like.Id descending
select like;
var songs = from like in trendingSongs
where like.LikeStatus == true
join song in entities.SongInfos on like.SongId equals song.Id
select song;
return songs
.Take(count)
.ToArray();
}
}
Look ma, no ugly SQL strings! Just sexy,
intellisense-friendly LINQ.
Building the social features like trending songs, community
rank, top-rated songs, and so forth, were created with ease thanks to Entity
Framework and nice integration between Silverlight and WCF.
The Super Secret Song Selection Algorithm
Psst. Come ‘ere. Yeah, you. Can you keep a secret? A’ight.
I’m no algorithm wizard, but I was able to come up with
(read: steal
from the software community) an algorithm that picks a song based on the
user’s likes and dislikes.
It’s worth mentioning again that, unlike Pandora, Chavah
does not use the Music Genome Project to predict music that you’ll like.
Instead, Chavah uses a much simpler algorithm that plays mostly music you like,
some music you haven’t rated, and very rarely plays music you dislike.
Enter the song weight algorithm.
For every song, assign a weight:
- Liked songs get a heavy weight (e.g. 5)
- Unranked songs get a normal weight (e.g. 1)
- Disliked songs get a low weight (e.g. 0.01)
To pick the next song to play, we line up all the songs, sum
their weights, and pick a random number between 0 and the sum.
For example, pretend there are only 3 songs in our entire
song repository: Shalom, Blessings, and King.
If the user has liked Shalom, Blessings is unrated, and King
is disliked, our algorithm would assign 0 through 5 to Blessings, 5 through 6
to Blessings, and 6 to 6.01 to King. We’ll generate a random number from 0 to
6.01. If the random number is 3, we’ll play Shalom. If it’s 5.5, we’ll play Blessings. If it’s 6.01, we’ll play King.
Simple, yet effective. End result is that liked songs are
more likely to play than normal songs, and normal songs are more likely to play
than disliked songs. Also, if the user hasn’t rated many songs, he’s still get
a good mix of liked songs and unrated. Controlling the “how much more”
threshold is a simple matter of adjusting the weight constants for liked,
unrated, and disliked songs.
CPian dessert special: Code Contracts whip cream with an Rx cherry on top
While not directly related to building a Pandora clone, I
want to touch on 2 topics that may be impactful to the .NET software world in
the next 5 years: Code Contracts and the Reactive Extensions (Rx) project.
Using them in a real world Silverlight app like Chavah has been enlightening,
and I wish to pass on my enlightened zen awesomeness to you fine CPians.
Reactive Extensions (Rx)
The Reactive
Extensions (Rx) framework is taking the idea of LINQ and applying it to
events. By “events” I do not mean standard .NET events only, but rather, really anything
that comes in in an asynchronous, unpredictable way: queue messages, mouse clicks,
key presses, user gestures, object change notifications, server messages, you name it.
The “reactive” part is key: rather than polling for data,
like you do with LINQ and IEnumerable, Rx lets you react to changes in data by observing an IObservable<T>, a new type
in .NET 4.
Rx is currently a Microsoft research project, however, it has some pieces already baked into the .NET framework. Some of the folks behind the Rx project – Wes Dyer, Bart deSmet, Eric Meijer – are a few of the brilliants minds that gave us LINQ , which has proven an overwhelming success.
Where Rx really shines is in composition. Imagine drag and
drop handling code: you might store a field where the mouse was first pressed.
You’d listen for mouse down. You’d listen for mouse move. You’d listen for
mouse up. You’d coordinate the whole process. And when you were done, there’s a
whole slew of event handlers, mutable state, and general ugliness; you can no
longer see the forest for the trees.
By contrast, Rx can compose the MouseUp, MouseDown,
MouseMove events together in a single declarative call: voila! You’ve
implemented drag and drop.
In Chavah, I used Rx to observe changes to my view models, thus
replacing the repeated plumbing of change notifications. I also used it to
loosely communicate between components: rather than view model 1 talking to
view model 2, I instead publish a message to a mediator, then interested parties can
compose and handle that message. For example, to listen for when we start
playing the Shalom song, I can write
mediator.Messages
.OfType<SongPlayingEventArgs><playsongeventargs>()
.Where(s => s.Name == “Shalom”)
.Subscribe(OnShalomPlaying);
Rx joins the power of compositional LINQ with events. Turns
out, this is a powerful combination that proved quite useful in this
Silverlight project.
Just as LINQ changed
the way we deal with data, I predict Rx will change the way we deal with events.
I’m at the point now where it’s hard to look at a problem and not see it as an
operation over a stream of events.
I suggest Microsoft graduate Rx as a first-class citizen of
the .NET Framework in the near future.
Code Contracts
Code contracts
is a new addition to the .NET 4 framework (and Silverlight 4). It was born out
of another research project at Microsoft: the Spec# language, an attempt at
merging C# and design-by-contract principles. Code Contracts, however, opted
for the language-agnostic route, implemented only as an API in the .NET
framework without any language support.
The idea of code contracts is not new: design-by-contract
programming, where the developer specifies required conditions – about methods,
classes, interfaces – right in the code. Then, a tool runs during compilation
to verify your conditions will never be violated.
The end result, in theory, is code that has fewer bugs, as
all of your assumptions are now codified as contracts and verified by a tool.
No more NullReferenceExceptions, no more ArgumentExceptions – wouldn’t that be
nice?
In Chavah, I used Code Contracts primarily on the
Silverlight end, with mixed results. Occasionally, Code Contracts catches a bug
– which is great! But other times I’m writing lots of contracts and just not feeling
the love.
You see, Code contracts works well when you control the
whole API. For example, if your entire project is using APIs that utilize code
contracts, you’ll get a great experience and will have fewer bugs in your code.
But that, in essence, is the problem: for Code Contracts to
really work, the ocean must be
boiled. That is to say, all the APIs you talk to need to utilize Code
Contracts, otherwise you’re going to get a lot of noise.
Even though Microsoft owns the ocean, they haven’t done much
boiling.
The reality is that much of the .Net framework is missing contracts; I’ve
personally ran into some not-too-obscure Silverlight APIs that were obviously
missing contracts. As a result, I had to write code to reassure the contract
checker that one .NET API really will return a non-negative number.
Additionally, the code contracts framework just doesn’t have
broad support right now, which introduces yet more noise. For example, Chavah
uses the Ninject inversion of control
framework for dependency resolution and injection, yet Ninject has no
contracts, so once again, to avoid the noise I must write more code to reassure
the contract tool that the sky is not, in fact, falling.
Another hardship with Code Contracts is that there is no
support from the language side. C# has no love for Code Contracts. When putting
contracts on interfaces, for example, you must jump through hoops and write
more patty-cake code. Another example is how the contract checker doesn’t
understand C# constructs like this:
private readonly string foo = “bar”;
“Oh noes!” says the contract checker, “foo might be null!”
Uhhh…no, foo is assigned just once, but the contract checker
tool, being insecure about himself, blusters on about foo possibly being null.
Again, C# and Code Contracts have no love for each other, and their marriage problems are passed onto you, the developer.
Bottom line: Code Contracts has potential, but until it gets
broad framework support, it’s too much noise. Also, it's not smart enough to see some things won't be null. (Do I really have to check that some event handler's EventArgs is not null? Really?) Some language integration would help,
too.
Summary
Building a Pandora-like music app in Silverlight was surprisingly
simple. I wrote the first version in about one weekend.
Silverlight, combined with WCF integration, can be used to
make client/server apps that run great on the web or on your desktop. It’s a
decent choice for building applications that run on the web.
Building nice, fluid UIs in Silverlight came natural, even
for this designer-challenged programmer. Control templates, data templates, easing
animations, and commanding support make for nice UIs with very little code
behind.
Entity Framework is maturing. Combining Entity Framework
with WCF made getting data back to the client dead-simple.