Silverlight 2 dynamic assembly loading - Part 2

6 03 2009

In this article:
- Unpacking and loading the assemblies.
- Direct & Deferred Loading
- Download Progress
In part 1 (click to open) :
- Rebuilding the AppManifest.xaml
- (Re)compressing .xap files with 7zip

A little but later then promised, mostly because I kept building more and more functionality. But here is second part of the Dynamic Assembly Loading. Click here for part 1.

In part 1 we rexapped out applications to meet our requirements, now let’s start loading the application, the custom xaml and the referenced assemblies. We start out with the solutions created in part 1, click here to download it.

ReferencedAssemblies.xaml

To reference and handle the data while loading we create a ReferencedAssembly class looking like this:

public enum ReferencedAssemblyStates { None, Loading, Error, Done };

public class ReferencedAssembly

{

    public ReferencedAssemblyStates State;

    public long ByteSize;

    public long ByteSizeLoaded;

    public double PercentLoaded;

    public string Name;

    public Uri Uri;

    public EventHandler Loaded;

    public Exception Error;

    public AssemblyPart AssemblyPart;

    public LoadingTypes loadingType = LoadingTypes.Direct;

}

Then we load our custom created ReferencedAssemblies.xaml from the Application Resource Stream, in the project this is done when you access the getter of the ReferencedAssemblies property:

var xmlReader = XmlReader.Create(

    Application.GetResourceStream(

        new Uri("ReferencedAssemblies.xaml", UriKind.Relative)

        ).Stream

    );

And use the XamlReader to put the data in our class:

while (xmlReader.ReadToFollowing("ReferencedAssembly"))

{

    // Read Name & Source and Size

    xmlReader.MoveToAttribute("Name");

    var name = xmlReader.Value;

    xmlReader.MoveToAttribute("Source");

    var uri = new Uri(Application.Current.AbsolutePath() + xmlReader.Value, UriKind.Absolute);

    xmlReader.MoveToAttribute("ByteSize");

    var byteSize = long.Parse(xmlReader.Value);

 

    // Create a new ReferencedAssembly class and set values

    var referencedAssembly = new ReferencedAssembly()

        {

            Name = name,

            Uri = uri,

            ByteSize = byteSize

        };

 

    // Add it to the Dictionary

    Current._ReferencedAssemblies.Add(name, referencedAssembly);

}

 

So now we have a list of assemblies, their xap file and their size. (This is a new addition to the ReXapper not done in Part 1). With this list we can load up all our referenced assemblies.

Direct & Deferred Loading

As you might have notice by looking at the code above, I added 2 kind of loading methods: Direct and Deferred.

I added these 2 types to make it possible to load assemblies on demand. This can be very useful when you are building bigger applications.

App.Xaml.cs

Now lets look at what we have to do in our applications. All other code we did so far is a one of, meaning we can just reference it next time. This piece of code needs to be added to every application we wish to use this type of assembly loading.

By default your app.xaml.cs contains the following to initiate your Page:

private void Application_Startup(object sender, StartupEventArgs e)

{

    this.RootVisual = new Page();

}

This should be changed to:

private void Application_Startup(object sender, StartupEventArgs e)

{

    AppAssemblyLoader.Current.LoadReferencedAssemblies(

        new string[] { "System.Xml.Linq" },

        () => this.RootVisual = new Page()

    );

}

What we are doing here is loading the referenced assembly System.Xml.Linq and when it is done call the Page creation method.

Because we also have a MyLibrary.dll in our application this will also be loaded, but this happens in the background. I have added a couple of Events to attach to to monitor what is happening:

public static LoadingDoneEventHandler LoadingDone;

public static LoadingDoneEventHandler LoadingStarted;

public static DownloadProgressChangedEventHandler DownloadProgressChanged;

public static AssemblyDownloadProgressEventHandler AssemblyDownloadProgressChanged;

public static AssemblyStateChangedEventHandler AssemblyStateChanged;

 

If you want to application to start only after every assembly is loaded you just call it like this:

AppAssemblyLoader.Current.LoadReferencedAssemblies(

    () => this.RootVisual = new Page()

);

Loading the Assembly

Loading an assembly is pretty straightforward. First we use a WebClient to get a stream to .xap file.

var wc = new WebClient();

wc.OpenReadCompleted += (sender, e) =>

    {

        LoadAssemblyFromWebResponse(referencedAssembly, e);

    };

 

wc.OpenReadAsync(referencedAssembly.Uri);

Then we take the Result stream and use it load create an AssemblyPart:

private static void LoadAssemblyFromWebResponse(ReferencedAssembly referencedAssembly, OpenReadCompletedEventArgs e)

{

    // Load .xap as StreamResource

    var xap = new StreamResourceInfo(e.Result, null);

    // Get the assembly (.dll) from the .xap StreamResource

    var assemblySri = Application.GetResourceStream(xap, new Uri(referencedAssembly.Name + ".dll", UriKind.Relative));

 

    // Load the stream into a new AssemblyPart

    var assemblyPart = referencedAssembly.AssemblyPart = new AssemblyPart();

    assemblyPart.Load(assemblySri.Stream);

}

 

Luckily Silverlight takes care of everything else. The assembly is added to the Assembly Pool, and when another part of you application requests the assembly it is automatically referenced.

Managed Download Progress

You have the possibility to get the download progress for the total and for every separate assembly. If you would load everything deferred, this would enable you to make a Managed Code Loader.

WrapUp

The end project contains a lot more  code which I won’t talk about here, but if you download the source code you can check it out yourself.

It ended up being a lot more that just the Loader I was planning it to become. But the things that were added, make it very useful for a few other scenario’s as well:

  • Easily separate a Silverlight application into different xap files, making it a good base for bigger applications in SL.
  • Create a managed loader, which is currently impossible with Silverlight.

I hope to have time in the next weeks, to build some demo applications to show off the possibilities.

You can download the finished source code here:
Robertjan Tuit - DynamicAssemblyLoading - Part 2 - Source Code.zip

Of course if you have any questions, ideas or remarks please let me know!

Stay in the Light!

Robertjan Tuit

Technorati:


Actions

Informations

One response to “Silverlight 2 dynamic assembly loading - Part 2”

4 05 2009
Bruce MacKenzie (4.35 am) :

I haven’t reviewd the code, but from what I can see the only concern here would be that delay-loading is no longer possible without explicit developer intervention. With the “normal” loading process, assemblies are sought and loaded as needed (or not) in a fashion that is transparent to the developer. If an error-handling assembly is not needed, for example, it is never loaded and the developer need not do anything specific for this to be true.

I don’t see any workaround for this - just pointing out a slight drawback.

Leave a comment

You can use these tags : <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>