Click here to Skip to main content
15,887,596 members
Articles / All Topics

Adventures while building a Silverlight Enterprise application part #6

Rate me:
Please Sign up or sign in to vote.
5.00/5 (1 vote)
18 Aug 2009CPOL4 min read 13.6K   1   5
Last time in this series we discussed projects and how to setup your large application. One point that dictates project layout in our application is the fact that we load XAP files as needed. Recently I've had some interesting insights from our functional specialist.

Last time in this series we discussed projects and how to setup your large application. One point that dictates project layout in our application is the fact that we load XAP files as needed. Recently I've had some interesting insights from our functional specialist. He not only wanted multiple modules on one screen, but he also wanted users, or at least administrators, to be able to configure which modules would be shown on which screen.

The first alarm bells started ringing at this point. It could all end up in a performance nightmare. In the initial faze of the project we figured we would load modules on a single screen from a single XAP file, all at once. This would greatly reduce any overhead involved in loading XAP files dynamically. But now we would have to load multiple modules from multiple XAP files, up to fifteen at a time. So at that moment in time I told the functional specialist, "No, we can't do that." I explained why this was likely to give problems and his response was, "Well can't you build these XAP files dynamically as you need them?" Well, wouldn't that be a heroic solution and a hell of a blog post :-).

I told him that would complicate things a great deal and we decided that for the most part we would decide which modules would end up on which screens, but users would be able to configure extra screens, with the knowledge that these screens could end up being slower than others.

In the two weeks since, I've come up with some alternative solutions to our problem. First of all, you don't have to put an assembly in a XAP file in order to load it, BUT... be warned! If you decide to load an assembly on it's own, you won't load anything referenced from the assembly, not other assemblies you might need and no resource files either. You would have to resolve these references yourself or have some kind of common set of references that every module is restricted to. In our case, this would be troublesome at best, so I discarded this.

I did figure out we would need some kind of meta database that would contain the settings of all these modules and who wants to see what where. Also this database would have to include security information on all the modules.

Then my colleague came up with the following solution. We do build everything we need to dynamically place any module on any screen as this is configured, but we don't solve the performance issue in the code. As customers experience issues, we simply build a new XAP file with the required modules and deploy it. We add it to our meta database and Bob's your uncle. This does mean we have to build some algorithm that can figure out the most optimal set of XAP files possible with what's available, but that is actually doable if you design a decent entity structure. After this was sorted I concluded that we didn't even have code yet that would load multiple modules from a single XAP file. So on I went and build that. It's basically a variation on the code I showed you all earlier in this post. Unfortunately I won't be able to upload all the code for this, but I can show you some highlights.

public void LoadModules(Collection<ModuleInfo> modules)
{
_modules = modules;
foreach (ModuleInfo module in modules)
{
GenerateTab(module);
}
var result = from ModuleInfo module in modules
group module by module.XapFileName;

foreach (IGrouping<string, ModuleInfo> xapFileGroup in result)
{
string xapFileName = xapFileGroup.Key;
WebClient client = new WebClient();
client.OpenReadCompleted += new OpenReadCompletedEventHandler(client_OpenReadCompleted);
client.OpenReadAsync(new Uri(xapFileName, UriKind.Relative), xapFileGroup);
}
}

void client_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error != null)
{
return;
}
IGrouping<string, ModuleInfo> xapFileGroup = e.UserState as IGrouping<string, ModuleInfo>;
if (xapFileGroup == null)
{
return;
}

var assemblyNames = (from ModuleInfo module in xapFileGroup
select module.AssemblyFileName).Distinct();
string[] stringAssemblyNames = assemblyNames.ToArray();

var classNames = (from ModuleInfo module in xapFileGroup
select module.ClassName).Distinct();
string[] stringClassNames = classNames.ToArray();

Collection<UIElement> modules = XapHelper.LoadUIElementsFromXap(e.Result,
    stringAssemblyNames, stringClassNames);

foreach (UIElement module in modules)
{
PlaceModule(module);
}
}

These are basically the loading of the modules and receiving a stream. In the LoadModules method I get a collection of ModuleInfo structs, that tell me what XAP file the module is in, what assembly and which class. In a linq query I group the modules by XAP file and then I kick off a load for each of these.

In the completed event I pick up the grouping from the linq query and use it to get to the right assembly and class names. I then use an adjusted method from the XapHelper class I wrote earlier to load the UIElements I need from the XAP file:

public static Collection<uielement> LoadUIElementsFromXap(Stream xapStream,
    string[] assemblyNames, string[] classNames)
{
Collection<uielement> elements = new Collection<uielement>();

string applicationManifest = ReadApplicationManifest(xapStream);
XElement deploymentElement = XDocument.Parse(applicationManifest).Root;
IEnumerable<xelement> deploymentParts =
    from assemblyParts in deploymentElement.Elements().Elements()
select assemblyParts;

Collection<assembly> assemblies = LoadAssemblies(xapStream, assemblyNames,
    deploymentParts);

foreach (Assembly assembly in assemblies)
{
foreach (string className in classNames)
{
UIElement element = assembly.CreateInstance(className) as UIElement;
if (element != null)
{
elements.Add(element);
}
}
}

return elements;
}

As you may notice, the only thing I changed is what I do with each assembly and class as I load them.

I hope this post was helpful for you and once again, if you have any questions or comments, please let me know.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior) KnowledgePlaza
Netherlands Netherlands
Since early 2001 I've been working full time as a software developer and since 2004 I've been working mostly with Microsoft technology.
I started out as a product developer, but after a few years I switched to a project company where my roles ranged from developer up to consultant and from team lead and coach to manager.
Eventually I switched jobs and focused on the consultant part and then I got back to building a product once again. Now I work in a job where I get to do both.

Comments and Discussions

 
GeneralExisting codebase... Pin
Jason Sobell23-Sep-09 23:26
Jason Sobell23-Sep-09 23:26 
GeneralRe: Existing codebase... Pin
mrjvdveen11-Nov-09 22:55
professionalmrjvdveen11-Nov-09 22:55 
GeneralRe: Existing codebase... Pin
Jason Sobell11-Nov-09 23:58
Jason Sobell11-Nov-09 23:58 
GeneralRe: Existing codebase... Pin
mrjvdveen9-Feb-10 0:39
professionalmrjvdveen9-Feb-10 0:39 
GeneralRe: Existing codebase... Pin
Jason Sobell9-Feb-10 1:11
Jason Sobell9-Feb-10 1:11 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.