So we are all not just building one simple Silverlight project and publishing simple web pages. Most of us develop enterprise LOB application in Silverlight. When you develop that big application you will end up using same component again and again over multiple projects and also you will end up using multiple version of same controls over multiple places as you develop. When you develop major application, if you get a new version of a third party control or even your own control, you will not replace it everywhere because of the regression testing. You will end up versioning and upgrade the solution with version.
In my previous blog on XAP Optimization, I looked at the simple attempt to reduce the size of a small Silverlight application. We looked at two different approaches. From 880Kb, we were able to get it down to ~230KB. I looked at the XAP file to see what is taking that much space and it turned out, I am using Component One controls for UI and Jounce for MVVM Framework. My dll by itself, unzipped only 14k. I was ok with the size of the file since, some of my application would use one version of Component One control and some other XAP files would use different version of the Component One control so If I would strip them out and make it common zip file then I would run into version conflict. So I was happy with the result till last weekend 🙂
It turned out, even though we use different version of controls, as we make changes we bring the projects to latest controls, so 80% of them require version of the controls. So it makes sense to even strip the controls out of XAP to reduce the file size and also reduce repeated loading of common controls, but somehow put versioning in place.
So by default, System controls become ZIP file when we use XAP optimization and how would be able to do the same for other controls and our own libraries? The answer is very simple. If you would open up the XAP file, you would see
The first file is AppManifest.xaml,
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="XAPOptimization1" EntryPointType="XAPOptimization1.App" RuntimeVersion="4.0.50826.0"> <Deployment.Parts> <AssemblyPart x:Name="XAPOptimization1" Source="XAPOptimization1.dll" /> <AssemblyPart x:Name="C1.Silverlight.FlexGridExcel" Source="C1.Silverlight.FlexGridExcel.dll" /> <AssemblyPart x:Name="Jounce" Source="Jounce.dll" /> <AssemblyPart x:Name="System.Windows.Interactivity" Source="System.Windows.Interactivity.dll" /> </Deployment.Parts> <Deployment.ExternalParts> <ExtensionPart Source="c1.silverlight.FlexGrid.4.183.zip" /> <ExtensionPart Source="c1.silverlight.FlexGridFilter.4.183.zip" /> <ExtensionPart Source="System.ComponentModel.Composition.zip" /> <ExtensionPart Source="System.Windows.Data.zip" /> <ExtensionPart Source="C1.Silverlight.183.zip" /> <ExtensionPart Source="System.Xml.Linq.zip" /> <ExtensionPart Source="C1.Silverlight.Zip.183.zip" /> <ExtensionPart Source="C1.Silverlight.Pdf.183.zip" /> <ExtensionPart Source="System.ComponentModel.Composition.Initialization.zip" /> </Deployment.ExternalParts> </Deployment>
If you see there are two parts of Deployments. One is Deployment.Parts which specifies all the dlls that are in the XAP file and associated name space. The second part specifies all the zip files that might be required for application to run. In my opinion the only thing that should be in XAP file will be the application dll. So the two part question is how would you get all the common dlls separated into its own ZIP file and how did you update the Application Manifest to reflect it?
Here is a very good article explaining how to Configure an assembly for use with application library caching. Please read this link since it goes more detail about the Application Manifest file and also ext map XML file.
Any dll that is common and it need to be in a separate Zip file, should have associated ‘extmap’ file. What it means is, in the above project you see that I am using System.Xml.Linq.dll, if you would find the file location using the file property and navigate to the file location, you will see following files, The file we are interested in is the dll file name with ‘extmap.xml’
<?xml version="1.0"?> <manifest xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <assembly> <name>System.Xml.Linq</name> <version>126.96.36.199</version> <publickeytoken>31bf3856ad364e35</publickeytoken> <relpath>System.Xml.Linq.dll</relpath> <extension downloadUri="System.Xml.Linq.zip" /> </assembly> </manifest>
The first two line is name and version and the third line is the public key of the dll, that means anything you want to have as separate dll, have to be strong named. Next two specifies the relative path of the dll and where do you get it from. In our case, the dll is in a zip file in the same path of the XAP file.
Have a look at this blog, here the author explains how it is done and also provides a tool to create your own extmap files for your dlls.
Once we have extmap files created for the dlls that need to be separated and placed on the same path where the dll located, when compiled with XAP optimization enabled, all the ones with extmap files will be in separate ZIP files.
Now comes the question of versioning, some XAP files would use different version of same controls, so by creating extmap file we would end up overwriting the old one with new one and potentially crashing the application. How do we solve it, again it is very simple, now most of all third party controls comes with extmap files, including Component One. Whenever we get latest version of a control or our own library, I run a small ruby script, which run through all extmap files and adds version number to the destination zip file. So instead of
<ExtensionPart Source="C1.Silverlight.zip" />
our implementation would look like the following
<ExtensionPart Source="C1.Silverlight.183.zip" />
That’s about it. So when I compile my project pointing to the new version dll, it uses the modified extmap and generate version numbered zip file thus making all the project work without any problem. I am working on a script which will then walk though all the XAP files and look at the Application Manifest and remove any unwanted zip files from the deliver location thus keeping the folder clean.
Once I have the second script done, I will post both the scripts in the blog here.
To test this, I created two Silverlight applications. Both are exactly same except the first one uses version 152 of Component One dlls and second one uses version 183. Now if I would run, each application should use appropriate ZIP and run without any problem. So I ran the fiddler and then ran the application to see the what is it downloading and is it caching.
First I ran XAPOptimization with version 152 and as you can see the first green box shows all the files that got downloaded to the client. Now when I tried the second version of the XAP Optimization with version 183, as you can see it did get the version 183 and also it skipped the common files that was already downloaded by the first version. For example. System.Windows.Data.zip was not downloaded since it was downloaded along with the first XAP file.
I see lot of potential in using XAP optimization when developing large application. Hope this helps someone.
One thing I forgot to mention, with versioning and keeping couple dlls in the XAP file itself, we are at ~100kb. That is a excellent saving. So next time when we make a change the the solution and none of te thrid party control changes, our download is only the XAP file size that is ~100kb not 880kb.