| Comments

One of the new features I mentioned in my What’s new/changed post on Silverlight 3 is the fact that any application developer can take advantage of the cached assembly functionality provided by Silverlight.  Let me show you how and start with the current situation.

Current Situation with Silverlight assembly references

If you are building a Silverlight application, chances are you are referencing assemblies either from the SDK, Silverlight Toolkit or other great Silverlight third party controls/frameworks.  When you Add Reference to these controls/frameworks, their assembly is copied to your application and packaged up in your XAP.  For some situations this is fine and you have no reason to care at all…it just works.  When you look at the AppManifest.xml file for your application you’ll notice the AssemblyPart nodes for each referenced assembly not in the core – and these are in your packaged XAP file.  Take a look at my sample application which has a reference to a simple 3rd party assembly: TimHeuer.SimpleFunctions.dll:

   1: <Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" 
   2:     xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="SilverlightApplication1" 
   3:     EntryPointType="SilverlightApplication1.App" RuntimeVersion="3.0.40624.0">
   4:   <Deployment.Parts>
   5:     <AssemblyPart x:Name="SilverlightApplication1" Source="SilverlightApplication1.dll" />
   6:     <AssemblyPart x:Name="TimHeuer.SimpleFunctions" Source="TimHeuer.SimpleFunctions.dll" />
   7:   </Deployment.Parts>
   8: </Deployment>

You can see that my custom assembly is included in the manifest as a separate AssemblyPart entry.  Looking at the XAP package contents you’ll also see it there:

Current XAP package

This is how we’ve been building Silverlight applications since Silverlight classic…um…I mean, Silverlight 2.  All of this will still work and if you do nothing, then no worries.

Introducing Application Library Caching (or External Assembly Parts)

In Silverlight 3 beta we introduced this feature to reduce the initial XAP package size of your application by providing external assembly parts for certain Microsoft components and controls.  In beta, it was a feature only reserved for Microsoft-delivered assemblies.  Now that we’ve released, we’ve opened this feature to any developer wishing to take advantage of it. 

What does it do?  Let’s take a look at perhaps a common referenced assembly: DataGrid.  When you add this assembly to your application, your XAP package will increase in size because it is not a part of the core (mine went from 4K to 204K because DataGrid also brings some other assemblies with it).  In Visual Studio, take a look at the properties dialog for your Silverlight application and you’ll notice a new checkbox:

VS Settings reduce XAP size

By clicking this checkbox you are telling Visual Studio to make sure that any assembly that offers this feature, to not package it in your XAP but to reference it as an external assembly part.  The result is two-fold: your manifest changes and your XAP no longer includes this assembly.  Take a look at what happened to my sample application’s AppManifest.xml when I enabled that feature:

   1: <Deployment --attributes removed for formatting only-->
   2:   <Deployment.Parts>
   3:     <AssemblyPart x:Name="SilverlightApplication1" Source="SilverlightApplication1.dll" />
   4:   </Deployment.Parts>
   5:   <Deployment.ExternalParts>
   6:     <ExtensionPart Source="TimHeuer.SimpleFunctions.zip" />
   7:   </Deployment.ExternalParts>
   8: </Deployment>

Notice the new node ExternalParts?  There’s no reference to my TimHeuer.SimpleFunctions.dll but there is one to a TimHeuer.SimpleFunctions.zip file as an ExtensionPart.  This is because my assembly has chosen to opt-in to enabling this feature if the developer wants to use it in their application deployment.  My XAP file also reduced from 6K to 4K in size (note: in my DataGrid example you’d see a reduction of approximately 200K).  Using this feature, when users visit your web application, the application and package are downloaded.  These files are added to the browser cache so they can be used on subsequent requests.

How is this accomplished?

So what’s going on here?  It’s actually quite simple and there are two ways you can do it as an assembly author (well, really one way, but two methods of how to provide your assembly deployment).  First, you have to make sure that your assembly has a strong-name key.  This is probably good practice anyways for your development team.  This gives you a public key token which you’ll need for this feature.

The next thing you have to do is provide the external part manifest for your assembly.  This is a simple XML file that does not have to be included *in* your assembly, but needs to exist in the same physical place as your assembly (i.e., right next to it in the file system).  This file must also be named <AssemblyFileName>.extmap.xml.  As an example in my sample, my custom assembly is TimHeuer.SimpleFunctions.dll and my file will be TimHeuer.SimpleFunctions.extmap.xml.  The contents of the file is simple and here is mine for this sample:

   1: <?xml version="1.0"?>
   2: <manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3:           xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   4:  
   5:     <assembly>
   6:         <name>TimHeuer.SimpleFunctions</name>
   7:         <version>1.0.0.0</version>
   8:         <publickeytoken>f265933def965411</publickeytoken>
   9:         <relpath>TimHeuer.SimpleFunctions.dll</relpath>
  10:         <extension downloadUri="TimHeuer.SimpleFunctions.zip" />
  11:     </assembly>
  12:  
  13:  
  14: </manifest>

Taking a look at this manifest, let me point out some important things.  The obvios of the assembly short name and version are there.  The publickeytoken node must be there as well.

NOTE: How do you obtain your public key token from a signed assembly?  Man I wish there was a tool in VS (plugin maybe) that made this simple.  But just get to the Visual Studio command prompt and execute: sn.exe –T <path-to-assembly>

Notice the next two nodes: relpath and extension.  The relpath node is the name of your assembly file.  The extension node indicates where to get the package for the assembly part.  Here’s where it might help to explain the options.  First, the endpoint to the extension part will be a ZIP file.  Within that file can exist your assemblies.  Take a look at the downloadUri attribute.  This tells the Silverlight application where it is going to get the zip file.  If you just specify a file name like the above two things will happen.  Your external part will be packaged into the ZIP file name you provide here and then that zip will be deployed alongside your XAP file (not in, but alongside it like in the ClientBin directory).  This means that wherever your XAP is, so does this ZIP need to reside.  After compiling your application you’d see it in your web applications folders like my sample here:

Alongside deployment

You can, however, provide a URI to the file.  Let’s look at a modification of the above:

   1: <?xml version="1.0"?>
   2: <manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   3:           xmlns:xsd="http://www.w3.org/2001/XMLSchema">
   4:     <assembly>
   5:         <name>TimHeuer.SimpleFunctions</name>
   6:         <version>1.0.0.0</version>
   7:         <publickeytoken>f265933def965411</publickeytoken>
   8:         <relpath>TimHeuer.SimpleFunctions.dll</relpath>
   9:         <extension downloadUri="http://timheuer.com/asmcache/1.0/TimHeuer.SimpleFunctions.zip" />
  10:     </assembly>
  11: </manifest>

Notice the different downloadUri attribute.  This is a unique URI that must be accessible by the Silverlight application (i.e., don’t put an Intranet URI here if you are using this feature on those bigger web tubes).  The resulting AppManifest.xml now looks like this:

   1: <Deployment --removed attributes for readibility-->
   2:   <Deployment.Parts>
   3:     <AssemblyPart x:Name="SilverlightApplication1" Source="SilverlightApplication1.dll" />
   4:   </Deployment.Parts>
   5:   <Deployment.ExternalParts>
   6:     <ExtensionPart Source="http://timheuer.com/asmcache/TimHeuer.SimpleFunctions.zip" />
   7:   </Deployment.ExternalParts>
   8: </Deployment>

And notice my project no longer has a local ZIP file in the ClientBin alongside my XAP:

Project explorer

This now tells Silverlight that it will need to get this assembly extension from this remote location.  This operation is done asynchronously by the runtime and you don’t have to add any additional code to request it.  Basically if your downloadUri is a file name, the build system packages the assembly into a zip file and deploys it alongside your XAP file.  If the attribute is an absolute URI, you are then responsible for providing the ZIP yourself and will need to ensure it is packaged and in that URI location point.

Why would you use this?

You may be asking yourself why or when you’d want to use this feature.  After all, in the end, the assembly will still be downloaded.  So whether or not it is in your initial XAP or later in a ZIP assembly request, the bits will still get to the end-user.  So why?

I can think of two initial scenarios where you’d want to consider this.  If you are a component vendor and want to provide a location for your consumers to get your bits, you could do this.  This may help manage a central location of known versions and enables the application developer to get the bits from the source rather than have to deploy them to their servers.  The second scenario I can think of is one of enterprise common assemblies.  If your team develops common components that all applications should use, then you can provide a central location to deliver these from and ensure people are using the correct bits/versions.

Things to look out for

As with any new feature, I’m sure you may be skeptical…and perhaps somewhat confused (if so, I apologize).  One of the obvious things to look out for in this feature is that if you rely on those referenced assemblies and you aren’t packaging them in your XAP or aren’t deploying them from your own servers, you are depending on that remote location to deliver them.  The natural concerns about latency, uptime, firewalls, etc. come into play here so be sure to understand your architecture choices correctly.

If you are using an absolute URI for the downloadURI attribute, there are two things you should consider.  First, if it is going to be delivered from a different domain than the XAP file, then a valid cross-domain policy file must exist at the domain serving the assembly package (zip).  Second, treat the absolute URI as a unique identifier.  Version should be unique as well.  Consider a URI naming scheme along the lines perhaps of something like this:

   1: http://timheuer.com/asmcache/1.0/TimHeuer.SimpleFunctions.zip

This way each version of the assembly library cache is unique.

If you have checked that “reduce XAP size…” checkbox and then try to also check the out-of-browser settings, you’ll be greeted with:

Warning with out-of-browser and assembly cache settings

Unfortunately right now the assembly caching feature is not available in out-of-browser experiences.

Summary

The assembly caching feature is a cool feature to be able to take advantage of when your architecture makes sense to leverage it.  It isn’t going to be for everyone, but that’s what makes it great – it is an opt-in feature for you as the assembly developer as well as the consumer.  With a few simple steps you can take your Silverlight assembly and prepare it for use on this feature as described above.  What do you think of assembly caching?


This work is licensed under a Creative Commons Attribution By license.

Please enjoy some of these other recent posts...

Comments