Silverlight 2 (RC0 / RTM) - Dynamic Assembly Loading Fix
If you are utilizing the dynamic loading of assemblies (component based architecture inside Silverlight) in Beta 2, you will be in for a nasty surprise when upgrading to RC0. Apparently, the way the Deployment class is utilized using a Singleton, it breaks some code you may have seen on several Microsoft blogs.
Specifically this line of code will fail with an InvalidOperationException ("Use the Current property to access the Deployment instance"). Below is a screen shot of Silverlight 2 RC0/RTM throwing a InvalidOperationException:
Deployment deploy = XamlReader.Load(appManifest) as Deployment;
So what can you do? Luckily all the XamlReader load method does above is parse the AppManifest.xaml file and extract the deployment parts. We can use LINQ to XML and do the parsing manually.
First, lets try to understand what we are doing. The XAP package is a zip compressed file that includes our assemblies and configuration files. One such file is the AppManifest.xaml. In my case, I am utilizing Tim Heuer's dynamic loading example and the xap file we are downloading dynamically includes a AppManifest.xaml file that looks like this:
<Deployment xmlns="http://schemas.microsoft.com/client/2007/deployment" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" EntryPointAssembly="DynamicXapDataGrid_CS" EntryPointType="DynamicXapDataGrid_CS.App" RuntimeVersion="2.0.30523.6">
<Deployment.Parts>
<AssemblyPart x:Name="DynamicXapDataGrid_CS" Source="DynamicXapDataGrid_CS.dll" />
<AssemblyPart x:Name="System.Windows.Controls.Data" Source="System.Windows.Controls.Data.dll" />
</Deployment.Parts>
</Deployment>
This file is a manifest of what assemblies are included inside the package. You can see above we downloading a xap package which includes 2 dlls (highlighted in bold above). We simply need to get the string xaml and parse it using LINQ to XML. Below is the code to do this:
IsolatedStorageFileStream fileStream = store.OpenFile("DynamicXapDataGrid_CS.xap", FileMode.Open, FileAccess.Read); StreamResourceInfo sri = new StreamResourceInfo(fileStream, "application/binary"); Stream manifestStream = Application.GetResourceStream(sri, new Uri("AppManifest.xaml", UriKind.Relative)).Stream; string appManifest = new StreamReader(manifestStream).ReadToEnd(); // remove this, doesn't work in RC0 / RTM//Deployment deploy = XamlReader.Load(appManifest) as Deployment; XElement deploymentRoot = XDocument.Parse(appManifest).Root; List deploymentParts = (from assemblyParts in deploymentRoot.Elements().Elements() select assemblyParts).ToList();Assembly asm = null;foreach (XElement xElement in deploymentParts){string source = xElement.Attribute("Source").Value;AssemblyPart asmPart = new AssemblyPart();fileStream = store.OpenFile("DynamicXapDataGrid_CS.xap", FileMode.Open, FileAccess.Read);StreamResourceInfo streamInfo = Application.GetResourceStream(new StreamResourceInfo(fileStream, "application/binary"), new Uri(source, UriKind.Relative));if (source == "DynamicXapDataGrid_CS.dll"){asm = asmPart.Load(streamInfo.Stream);}
Code overview:
- Remove this line of code, this doesn't work anymore in Silverlight 2 RC0/RTM: Deployment deploy = XamlReader.Load(appManifest) as Deployment;
- Next make sure you add a reference to the LINQ to XML namespace in order to be able to parse the AppManifest.xaml file
- The following line of code loads the AppManifest.xaml file and extracts the 2 assembly names into a list. Notice that all we want is the name of the assembly, which are found inside the AssemblyPart element inside the Source attribute. You can do this outside of LINQ to XML and you can even write the code a little more expressively.
- XElement deploymentRoot = XDocument.Parse(appManifest).Root; List deploymentParts = (from assemblyParts in deploymentRoot.Elements().Elements()
select assemblyParts).ToList();
- Now that we have our list of XElements we simply can iterate through them and extract the Source attribute:
- foreach (XElement xElement in deploymentParts)
{
string source = xElement.Attribute("Source").Value;
- The rest of the code is the same
In conclusion, the new Silverlight 2 RTM Deployment class utilizes a singleton which breaks the XamlReader.Load method. In Beta 2, this code did the parsing for you. Luckily with LINQ to XML, we can easily parse the data and extract the assembly information ourselves.