Load Assemblies Deployed With Shell Application

Jan 27, 2011 at 2:37 PM
Edited Jan 27, 2011 at 2:38 PM

I'm building a composite application that makes use of DeploymentCatalogs to load modules in the background. These elements use a common framework project that Exports various parts.

Currently these modules, lets call them ComponentsA and B, both distribute a Shared assembly in their respective .xap files. I'd like to include the shared assembly with the Main Silverlight application and remove it from the DeploymentCatalogs to save bandwidth. Currently this is achieved in the following way:

public class MyBootstrapper : Bootstrapper<ShellViewModel>
    {
        private CompositionContainer _container;

        protected override void Configure()
        {
            var aggCatalog = new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>());

            // Load all core & shared assemblies into MEF/Caliburn
            var assemblies = new List<Assembly>
                                 {
                                     typeof (Shared).Assembly   // Can we populate this list with the DLLs distributed in the MainApp.xap ?
                                 };
            assemblies.ForEach(ass =>
            {
                aggCatalog.Catalogs.Add(new AssemblyCatalog(ass));
                AssemblySource.Instance.Add(ass);
            });

            // Load modules in the background
            var depCatalogs = new List<DeploymentCatalog>
                                  {
                                      new DeploymentCatalog("ComponentA.xap"),
                                      new DeploymentCatalog("ComponentB.xap")
                                  };
            depCatalogs.ForEach(dep =>
                                    {
                                        // Select & Load only those Assemblies & Parts that are not already loaded
                                        dep.DownloadCompleted += 
                                                (s, ea) => dep.Parts
                                                            .Select(part => ReflectionModelServices.GetPartType(part).Value.Assembly)
                                                            .Where(assembly => !AssemblySource.Instance.Contains(assembly)).Distinct()
                                                            .Apply(assembly =>
                                                                        {
                                                                            AssemblySource.Instance.Add(assembly);
                                                                            aggCatalog.Catalogs.Add(new AssemblyCatalog(assembly));
                                                                        });
                                        dep.DownloadAsync();
                                    });

            _container = CompositionHost.Initialize(aggCatalog);
            base.Configure();
        }
}

I ensure that assemblies downloaded in DeploymentCatalogs are not loaded into MEF & CM more than once, I was having issues with it importing the same exports multiple times.So currently we've got the initial assemblies being loaded first.

Now there's quite a few assemblies that we want to distribute like this and it seems highly cumbersome adding a (SomeRandomClass).Assembly every time, is there a way to get all the assemblies distributed with the .xap loaded into MEF & Caliburn Micro? The approach above goes through all those in DeploymentCatalogs but I can't seem to get at the main application's "catalog" as such. The approaches I've looked at seem to violate the Silverlight security model.

Jan 27, 2011 at 7:46 PM
Edited Jan 27, 2011 at 7:46 PM

 

I use something like this, stolen from the comments section on one of the wiki articles:

private static void RegisterAlreadyLoadedAssemblies()
{
     var currentlyLoadedAssembliesFullNames = CurrentAssemblies().Select(p=>p.FullName);

     LoadModuleResult.AddToAssemblyFullNameCache(currentlyLoadedAssembliesFullNames.ToArray());
}

 


 

/// <summary>
/// Retrieves The current list of assemblies for the application XAP load. Depends on the Deployment.Current property being setup and
/// so can only be accessed after the Application object has be completely constructed.
/// No caching occurs at this level.
/// </summary>
public static IEnumerable<Assembly> CurrentAssemblies()
{
    var assemblies = new List<Assembly>();

    // While this may seem like somewhat of a hack, walking the AssemblyParts in the active 
    // deployment object is the only way to get the list of assemblies loaded by the initial XAP. 
    foreach (AssemblyPart ap in Deployment.Current.Parts)
    {                
        StreamResourceInfo sri = Application.GetResourceStream(new Uri(ap.Source, UriKind.Relative));
        if (sri != null)
        {
            // Keep in mind that calling Load on an assembly that is already loaded will
            // be a no-op and simply return the already loaded assembly object.
            Assembly assembly = ap.Load(sri.Stream);
            assemblies.Add(assembly);
        }                
    }
    return assemblies;
}

 

 

 

Feb 3, 2011 at 4:36 PM

That's great Joe, the "somewhat of a hack but the only way to get the list" piece of code was exactly what I was hoping for.