XAP Optimization – Part III Versioning through program

In my previous blog I explained how to do manual versioning. At the end, I also mentioned I would write a ruby script to automate it so that when you have tons of third party controls, you can version them using a tool.

Initially I was going to write the script in Ruby and it turned out not many people does not know ruby and it could cause problem down the road so I stick to what everyone knows (at my place), C#. So what are we trying to do? Most of the third party controls comes with extmap files which is used for assembly caching.  By default the extmap file does not have versions. But in real life not all our applications are going to use one version in production all the time, we would end up using multiple versions in production as we release new features by using new controls while maintaining old features. Till the old features are migrated to use new controls you would need to keep both version in production. One another requirement for assembly caching is that the dll that you are going to cache need to be signed. I use component one controls and they are all signed, one less work for me.

Now lets see how are we going to do it, Lets first see the code

   1:  class Program
   2:      {
   3:          static void Main(string[] args)
   4:          {
   5:              if (args.Count() < 2)
   6:              {
   7:                  Console.WriteLine("Usage: XAPOptimizationVersioningdirectory versionnumber versionname");
   8:                  Console.WriteLine("     : Directory need to be full path.");
   9:                  Console.WriteLine("     : Version number can be anything you need, in my case it would be three digit number.");
  10:                  Console.WriteLine("     : versionname is optional. If it is available then it would consolidate all the dll into single version with this name.");
  11:                  Console.ReadKey();
  12:              }
  13:              else
  14:              {
  15:                  bool replaceFullAssemblyCacheFileName = false;
  16:                  string assemblyCacheZipFileName = string.Format(".{0}.zip", args[1]);
  17:                  if (args.Count() == 3)
  18:                  {
  19:                      assemblyCacheZipFileName = string.Format("{0}.{1}.zip", args[2], args[1]);
  20:                      replaceFullAssemblyCacheFileName = true;
  21:                  }
  23:                  foreach (var extFileName in Directory.GetFiles(args[0]).Where(p=> p.Contains("extmap")))
  24:                  {
  25:                      Console.WriteLine("Processing File {0}", extFileName);
  26:                      XDocument doc = XDocument.Load(extFileName);
  27:                      IEnumerable<XElement> element = doc.Descendants("extension");
  28:                      string attr = element.ElementAt(0).Attribute("downloadUri").Value;
  29:                      if (replaceFullAssemblyCacheFileName)
  30:                          attr = assemblyCacheZipFileName;
  31:                      else
  32:                          attr = attr.Replace(".zip", "." + args[1] + ".zip");
  33:                      element.ElementAt(0).Attribute("downloadUri").Value = attr;
  34:                      doc.Save(extFileName);
  35:                  }
  36:                  Console.WriteLine("Versioning Complete.");
  37:                  Console.ReadKey();
  38:              }
  39:          }
  40:      }

It is as simple as 40 line code but as you can see most of the magic happen between line 23 and 35. First lets split the program in a way it make sense. First of all this is a console application where you need to pass 2 optionally 3 parameters. Lets look at the usage code;

   1:  if (args.Count() < 2)
   2:              {
   3:                  Console.WriteLine("Usage: XAPOptimizationVersioningdirectory versionnumber versionname");
   4:                  Console.WriteLine("     : Directory need to be full path.");
   5:                  Console.WriteLine("     : Version number can be anything you need, in my case it would be three digit number.");
   6:                  Console.WriteLine("     : versionname is optional. If it is available then it would consolidate all the dll into single version with this name.");
   7:                  Console.ReadKey();
   8:              }

When you run the program, you call the name of the program, full directory path to the location of dlls, version number and finally and optionally a version name. First two parameter is self explanatory. What is the third parameter? Well, by default when you do assembly caching, if you have 10 dlls, then it will generate 10 different zip files.but what if you want all of them to be in single file zip file but separated out of the solution XAP file? here is it, pass the name of the file you want all these dlls zipped into. So if you would run the program just the first two parameter

XAPOptimizationVersioning c:\temp\componentone192 192

with the above parameters, it will modify all the extmap files to add version number and thus creating versioned dlls. For example, if I have a file called C1.Silverlight.dll, its expmap would let you create C1.Silverlight.zip out of the box, but after running this tool, it will create C1.Silverlight.192.zip in the client bin.

On the other hand if you would call the program using

XAPOptimizationVersioning c:\temp\componentone192 192 ComponentOne

then anytime when the program uses any dll from c:\temp\componentone192, it will be bundle all dlls referenced in the solution and create one single dll called ComponentOne.192.zip. This will reduce number ZIP files at the server side but on flip side, if someone for their project doesn’t require one of the dll used in your project but pointing to same reference folder then it will create new ComponentOne.192.zip without your reference dll and replace existing one in the server. Since the ZIP is changed, when the user goes to your page next time, it will download new ZIP and error out since one of your required dll is missing. So use this approach with Caution. I will not recommend this solution as it is, but you can get to accomplish this with little workaround I mentioned at the bottom.

Now lets look at the meat of the code

   1:  foreach (var extFileName in Directory.GetFiles(args[0]).Where(p=> p.Contains("extmap")))
   2:                  {
   3:                      Console.WriteLine("Processing File {0}", extFileName);
   4:                      XDocument doc = XDocument.Load(extFileName);
   5:                      IEnumerable<XElement> element = doc.Descendants("extension");
   6:                      string attr = element.ElementAt(0).Attribute("downloadUri").Value;
   7:                      if (replaceFullAssemblyCacheFileName)
   8:                          attr = assemblyCacheZipFileName;
   9:                      else
  10:                          attr = attr.Replace(".zip", assemblyCacheZipFileName);
  11:                      element.ElementAt(0).Attribute("downloadUri").Value = attr;
  12:                      doc.Save(extFileName);
  13:                  }

Line 1: Identify all the extmap files available in the directory. This implementation does not recursively look for extmaps in sub folders. You can extend the code to do it.

Line 4: Load the extmap file as XDocument

Line 5: Identify the extension element which have the downloadUri attribute.

Line 7: Check to see if we are going to replace the full name with user supplied zip file name or append only version to the zip filename.

Line 8: Replace the default file to user supplied zip file name.

Line 10: Appends version number to zip file name.

Line 11: Replace the value with modified value.

That’s about it, but here are some thoughts on the whole ‘XAP Optimization’ worth considering.

  • By adding XAP optimization your very first page load will have same load time as the one you would do with out ‘XAP Optimization’.
  • When you modify the code and none of the ZIP changed, the file download time will be drastically reduced because it downloads only changed dll. So the benefit is in the future downloads.
  • Here is a very important catch in ‘XAP Optimization’, if you are using, say 15 or 20 reference dlls and they are zipped up into seperate ZIP files, for every page visit, regardless if you are coming to the page first time or next time, there is a round trip call the web server to see if a files is changed for all ZIP files. This time will add up so quick and users will see downloading screen every time, even though it is not downloading. So please keep this in mind. This could annoy powers users and could create a perception that, the application is downloading every time.
  • If you would like to reduce ZIP files in the server side, one option is to run the tool and create version specific complete zip file or group them in such a way you have few ZIP files. Create these ZIP files before hand manually. When you deploy, only deploy these prepackaged ZIP instead of the ones from client bin folder. This will reduce the zip files, reduce the server side look up and also no one accidently step on and remove any dlls.

Use it properly to fit your need. Hope this helps someone.


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s