Another feature of Silverlight 3 that has flown under the radar since the product’s release is application extension services, or application services for short. Application services are client-side services that start when the application starts and end when the application ends. In other words, their lifetime parallels that of the application itself. An application service is an excellent deployment vehicle for features that in Silverlight 2 might have been added to the Application-derived class in App.xaml.cs.
Writing an application service is a simple matter of building a class that implements Silverlight’s IApplicationService interface. That interface has just two methods: StartService and StopService, which are called when the service starts and when it ends. In practice, StartService is called immediately before the Application.Startup event fires, and StopService is called right after the Application.Exit event fires. A skeletal service implementation appears below. Note the static property named Current, which provides a simple means for application code to acquire a reference to a running service instance:
public class SimpleService : IApplicationService
{
private static SimpleService _current = null;
public static SimpleService Current
{
get { return _current; }
}
// TODO: Implement other class members here
public void StartService(ApplicationServiceContext context)
{
_current = this;
}
public void StopService()
{
_current = null;
}
}
Although not shown in the example above, a service can optionally implement the IApplicationLifetimeAware interface, too. This interface adds four methods named Starting, Started, Exiting, and Exited. Calls to these methods bracket calls to StartService and StopService and provide an application service with additional opportunities to perform any necessary initialization or cleanup chores.
Application services are loaded by declaring instances of them in App.xaml:
<Application
xmlns=”http://schemas.microsoft.com/winfx/2006/xaml/presentation”
xmlns:x=”http://schemas.microsoft.com/winfx/2006/xaml”
x:Class=”SimpleServiceDemo.App”
xmlns:local=”clr-namespace:SimpleServiceDemo”
>
<Application.Resources>
</Application.Resources>
<Application.ApplicationLifetimeObjects>
<local:SimpleService />
</Application.ApplicationLifetimeObjects>
</Application>
ApplicationLifetimeObjects is a new property added to the Application class in Silverlight 3. You can use it to register application services as shown above, and also to enumerate loaded services at run-time.
So, the big question: why would you ever write one of these things? To demonstrate, I built an application service named AssemblyLoaderService that provides a useful service to Silverlight applications that rely on external assemblies: asynchronous assembly loading. Rather than swell the size of your XAP file by packing it with extra assemblies, you can register those assemblies with the assembly loader service when the application starts up and allow the service to download them in the background and load them into the app domain. It’s an easy way to deploy applications that utilize multiple assemblies without increasing the size of the XAP and delaying the startup of the entire application. Done properly, it also allows you to avoid jumping through some of the hoops I described in an earlier blog post on dynamic assembly loading in Silverlight.
You can download the source code for AssemblyLoaderService (and a sample app that uses it) from Wintellect’s Web site. When you run the application, you’ll initially see a blank page. After a short time (very short since you’re running against a local Web server), a button will appear. Clicking the button removes the button from the page and replaces it with a dynamically created Calendar control.
It’s what happens on the inside that’s interesting. The Calendar class lives in System.Windows.Controls.dll, which isn’t part of the core Silverlight run-time. To create a Calendar control, you must include a reference to System.Windows.Controls.dll in your project. Normally, that increases the size of your XAP file. Here it doesn’t, because after adding the assembly reference to the project, I changed the assembly’s Copy Local setting from true to false. This satisfies the compiler but prevents the assembly from being packaged in the XAP file.
The following statements in MainPage.xaml.cs initiate an asynchronous download of System.Windows.Controls.dll from the server’s ClientBin folder:
AssemblyLoaderService als = AssemblyLoaderService.Current;
if (als != null) // Just to be sure
{
als.AssemblyLoaded +=
new EventHandler<AssemblyLoadedEventArgs>(OnAssemblyLoaded);
als.AssemblyLoadedError +=
new EventHandler<AssemblyLoadedErrorEventArgs>(OnAssemblyLoadedError);
als.LoadAssemblyAsync(new Uri(“System.Windows.Controls.dll”, UriKind.Relative));
}
Before calling the service’s LoadAssemblyAsync method to start the download, MainPage registers handlers for the service’s AssemblyLoaded and AssemblyLoadedError events, which notify interested parties when an assembly is successfully loaded, or when an error prevents an assembly from being loaded. MainPage’s AssemblyLoaded event handler displays the “Create Calendar Control” button to the user, so there’s no chance that the application will attempt to create a Calendar control before the required assembly has been loaded into the app domain.
The service is implemented in the AssemblyLoaderService class. Look through the source code and you’ll see that it sometimes uses a DispatcherSynchronizationContext to marshal to the application’s UI thread. That’s because AssemblyPart.Load throws an exception if called from a background thread, and also because I wanted all events fired by the service to execute on the UI thread. I’ll describe all this and more next week in Berlin when I do my “Biggest Little-Known Features in Silverlight” talk at TechEd, so if you’re attending the conference, please come by and join the fun!