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:                  }
  22:   
  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.

Lets go back F#

With VS2010, for creating, debugging and running F# projects, you do not need to jump hoops. I was reading F# for Scientists for some time and is an excellent book, this is coming from a person who is neither a scientist nor functional programmer.

Here is our kick start into the world of F#.

Start up your VS2010 and choose F# in the languages

image

I chose F# application and name your project. Type in the following code (under Program.fs)

let message = "Hello World"
let i = 5
let sum x = i + x 
let anonSum = fun x y -> x + y

let main() = 
    printfn "%A" message
    printfn "%A" i
    printfn "%A" (sum 6)
    printfn "%A" (anonSum 6 6)

main()

that is it, now run the program will open up a console output and show the result and close the window automatically. So I hope you checked your result fast enough before windows closing the screen.

We can always add a line in the code for the program to wait for ‘press ANYKEY’ to continue’. I still remember the ‘ANYKEY’ joke, where the use of Window call in support since the user is unable to find ANYKEY in the keyboard. Anyway, before we go into adding key board listener before closing the console window, lets try it differently.

In F# you have an option to try all the code or part of it interactively using F# Interactive, it like trying Linq using LinqPad. To do that, all you have to do is to  select the code you want to try in interactive screen and press Alt + Enter or right click and select ‘Send to Interactive’ option. Which will open ‘F# Interactive’ window and run the code. So if you would have selected all the lines from the above code and did send to interactive your result would be something like the following

image

Lets say you are not a interactive person and want to see the result by running the code, then all you have to do is add a line to wait for user to press any key. Which is accomplished by adding System name space and use ‘Console.Readkey’ like C#. Remember F# is first class citizen in .Net so all name spaces available in other languages available to F# as well. So here is the modified code

open System

let message = "Hello World"
let i = 5
let sum x = i + x 
let anonSum = fun x y -> x + y

let main() = 
    printfn "%A" message
    printfn "%A" i
    printfn "%A" (sum 6)
    printfn "%A" (anonSum 6 6)
    Console.ReadKey(true)

main()

As you can see, now we are using System name space by calling ‘open System’. Also in the main function the last call is to read the keyboard but do not display the result back to the screen. Now if you would run it, you get the following output and the console is patiently waiting for you to press any key.

image

Here is the catch, if you would use the code and try run it interactively, you will see ‘application does not have  a console’ error but the code does run and generate the result. To solve that problem and also that you want to run the program in console mode, change the Console.Readkey to Console.Read, that is it, now if you would the program, it will run same way as before, it waits for key stroke and also if you would select all the code and run it in F# interactive, it will run without throwing any errors. By the way, I did not magically found the solution, the error message in the F# interactive gave me the suggestion to change Console.Readkey to Console.Read.

There are few things I did not go over like what is main, why there is a blue wavy under main, why Readkey to Read worked etc., that is for later discussion.

Digg This

Component One Silverlight FlexGrid Tip 4 (Group Aggregate)

In the previous blog we looked at performing simple grouping. While grouping is interesting, most of the time you group them and to see some king of group aggregation. Component One provides following group aggregates to be performed on any given column definition.

  1. Average
  2. Count
  3. Maximum
  4. Minimum
  5. None
  6. Range
  7. Std
  8. StdPop
  9. Sum
  10. Var
  11. VarPop

Lets all of these in action, we will take our favorite Person class we used in the previous example for simple grouping and add all aggregation on the Age and see what does it create

<c1:C1FlexGrid Name="flexGrid" ItemsSource="{Binding PeopleCollection}" AutoGenerateColumns="False" >
   <c1:C1FlexGrid.Columns>
     <c1:Column Binding="{Binding Name}"/>
     <c1:Column Binding="{Binding Age}" Header="Average" GroupAggregate="Average"/>
     <c1:Column Binding="{Binding Age}" Header="Count" GroupAggregate="Count"/>
     <c1:Column Binding="{Binding Age}" Header="Maximum" GroupAggregate="Maximum"/>
     <c1:Column Binding="{Binding Age}" Header="Minimum" GroupAggregate="Minimum"/>
     <c1:Column Binding="{Binding Age}" Header="None" GroupAggregate="None"/>
     <c1:Column Binding="{Binding Age}" Header="Range" GroupAggregate="Range"/>
     <c1:Column Binding="{Binding Age}" Header="Std" GroupAggregate="Std"/>
     <c1:Column Binding="{Binding Age}" Header="StdPop" GroupAggregate="StdPop"/>
     <c1:Column Binding="{Binding Age}" Header="Sum" GroupAggregate="Sum"/>
     <c1:Column Binding="{Binding Age}" Header="Var" GroupAggregate="Var"/>
     <c1:Column Binding="{Binding Age}" Header="VarPop" GroupAggregate="VarPop"/>
   </c1:C1FlexGrid.Columns>
</c1:C1FlexGrid>

 

All of them are self explanatory on what it does. The default behaviour is None. If you do not have a group aggregate for a column, it will default it to None so that no group aggregation will not happen.

image

Component One Silverlight FlexGrid Tip – 2 (Change Selection mode)

FlexGrid provides a cool feature which allows programmers to control what to select when a user click on a cell.  There are four different selection modes available.

The default behavior

* allows user to select a single single if the user click on a particular cell.

image

* You select multiple cells by holding left mouse and dragging the arrow key.

image

* You can select an entire row by clicking on the row selector

image

* You can select range of rows by holding left click and selecting rows. Other options is to use to select multiple rows by holding Shift key.

image

If you do not like the default behavior, then flex grid does exposes a property to change the default selection mode.

image

Cell:

As the name suggests, if you choose Cell Selection mode, you can not select a row, even by clicking on left hand row selector. You can not also select range of cells. It is purely forcing user to select only a one cell at a time.

CellRange:

This is same as Cell selection except, it allows users to select multiple cells or cell range. This selection is always contiguous.

Row:

As the name suggests, when a user click anywhere on a row, the whole row will be selected for you. Only one row will be selected at a time.

RowRange:

Same as Row except, users are allowed to select multiple rows.

You can use CellRange to achieve what RowRange does but not vice versa.

 ListBox:

This is similar to RowRange except, you can select non contiguous rows by holding control key (Ctrl).

image

As you can see I selected first two rows from group 0 and then holding Ctrl key I selected middle two rows of group 1. When I did that, it preserved the first group selection for me. If you would use any other option, the selection must be contiguous.

As you can see non-contiguous cell selection option is not available out of the box. For sure, you can achieve this in code behind with little code.

Component One Silverlight FlexGrid Tip – 1 (Collapse to group level)

If you have a grid which have bunch of groups, You might want to collapse the grid to root level at the start of the program or expand all the groups.  You can achieve this by using the FlexGrid method call ‘CollapseGroupsToLevel(int groupLevel)’.

Whenever you need to work on collapsing to a specific level from external trigger, I would recommend to identify the collection depth before hand so that you know what is deepest groups you have. So if you requirement to show the grid collapsed on start up, then you call

grid.CollapseGroupsToLevel(0)

image

By default, if you do not collapse to a specific level then grid by default will expand to all groups.

If you want to expand the group to say a specific level then you can do it in the XAML code behind as

grid.CollapseGroupsToLevel(expandLevel); //where expandLevel is set to which ever group level user want to expand to.

Naming Conventions in C#

Last week I had a chance to talk to one of my friend about the naming convention they are using for their projects. He went to on to great details on how they have standards in naming convention. In my opinion, developers shouldn’t think too much about standards before writing every single line of code. The standards should be natural for development which help maintenance rather than taking developers productivity down since they have to think twice before writing code.

One thing really stick with me was the rules they use for naming convention. They have very elaborate rules on class name, method name etc.,

I like the following simple rules for naming variables;

(Names should be meaningful and easily understandable.)

Use first letter capital for for all Methods/Properties.

Use _ as the first letter for private variables. I have seen people use m_ or my as prefix for private variables.

Use first letter lower case for all method parameters.

This goes with another idea of using properties instead of private variables. There was a discussion on this topic so I am not going to go into details.I prefer to use Properties rather than private variables. By making private variables with _, it will be easy to add a FxCop rule during file check in to see if anyone violated the rule easily.

Everyone uses different rules that suits their development.

Unit Testing in Silverlight – Continuing the path

In my previous blog I talked about few starter pointers for Silverlight Unit Testing. Hopefully you have converted all your Silverlight application fully testable by now. Now comes the next question, by default when you run the Silverlight test, it launches the web page and perform the test. It is all good and well as long as you are in your development environment, what if you want to run your tests as a part of your Continuous Integration (CI). There is a solution for that.

Fortunately I follow Jeremy Likness blog and some time back he wrote a blog about a open source project called Statlight. So today’s tip, if you want to run your Silverlight test as a part of CI, download and run Statlight. Please read Jeremy’s blog, he explains how to run Statight, so I am not going to duplicate that effort.