Software Maintenance

Using Callable Action Lists Part II

In my previous post I used a callable action list along with Xml Update Actions to read all of the database connection strings identified in an app.config file, then iterate and update each one of the databases during an application update. The approach I took to accomplish the goal only used built-in Update Actions that are available to me in the Actions Palette. I purposefully chose not to use dynamic code actions (custom update actions) to make a point of what we can accomplish without resorting to writing our own code. However, what can be accomplished with a little custom code is extremely powerful, so now I’m going to revisit the objective, removing the intent of relying only on built-in actions.

Identifying the Databases to Update

In this scenario, the local databases that need to be updated during our application update are listed in the application configuration file. This is very convenient, especially when I want to use built-in actions to find the information. But what if the databases were not so conveniently discoverable? What if you first had to connect to a database server and search through all of the databases on the server for a specific naming convention? What if the databases were passed into the update process from the host application? Using a dynamic code action, scenarios like this can be easily handled.

Custom Update Actions and Shared Properties

The feature duo that makes what might initially sound difficult to accomplish during an application update magically easy using AppLife Update are Custom Actions and Shared Properties. Custom Actions are simply classes that inherit from an UpdateAction base class and implements at a minimum, an Execute method and a Rollback method. Shared Properties are a collection of objects that are scoped to the context of the update and can be accessed from any update action.

For our purposes, we want a custom update action that will read the list of databases from an application configuration file. The C#/VB.NET Code action lets me write this custom action directly within the update creation software.

Note:  Custom Actions can also be created in Visual Studio and compiled to an assembly.  Custom Action assemblies can be added to an update project through the Project..Settings dialog, or added the Custom Actions folder located within the AppLife Update install directory.

Here is the code:

Code Snippet

1:  using System;  
2:  using System.Collections.Generic;  
3:  using System.Text;  
4:  using Kjs.AppLife.Update.Engine.Core;  
5:  using System.Xml;  
6:  using System.IO;  
7:  namespace DynamicCodeActions {  
8:    public class DynamicAction1 : UpdateAction {  
9:      public override void Execute(UpdateContext context) {  
10:        //Read connection strings from app.config  
11:        XmlDocument configDoc = new XmlDocument();  
12:        configDoc.Load(Path.Combine(context.ApplicationDirectory, "CallActionListExample.exe.config"));  
13:        XmlNodeList connStrings = configDoc.SelectNodes("/configuration/connectionStrings/*");  
14:        List<string> connectionStrings = new List<string>();  
15:        foreach(XmlNode node in connStrings) {  
16:          connectionStrings.Add(node.Attributes["connectionString"].Value);  
17:          context.Log.WriteLine(string.Format("Added connStr: {0}", node.Attributes["connectionString"].Value));  
18:        }  
19:        if(connectionStrings.Count > 0) {  
20:          context.SharedProperties.Add("ConnectionStrings", connectionStrings);  
21:          context.SharedProperties.Add("ExecuteDBUpdate", true);  
22:          context.SharedProperties.Add("CurrentConnectionString", "");  
23:        } else {  
24:          context.SharedProperties.Add("ExecuteDBUpdate", false);  
25:        }  
26:      }  
27:      public override void RollbackExecute(UpdateContext context) {  
28:        //Add code here to undo work that was performed in the Execute  
29:        //method. The method is not performed if the Execute method  
30:        //is not completed.  
31:        context.SharedProperties.Remove("ConnectionStrings");  
32:        context.SharedProperties.Remove("ExecuteDBUpdate");  
33:      }  
34:    }  
35:  }  

Notice that through the context parameter, the code can access the Shared Properties collection as well as other properties, such as the physical path to the host application. This code simply opens the application configuration file and reads the database connection strings. These strings are then added to a generic List of strings, and that List is added to the Shared Properties collection. If the list is not empty, another Shared Property is added that will be used in a conditional statement.

Manipulating the List from other Actions

With the list of database connection strings in the Shared Properties collection, we can call the recursive callable update action list to update the databases. This in-memory list takes the place of the copied app.config file used in the original post. From within the Update Databases action list, we can read and manipulate the Shared Properties collection with other custom update actions.

Read the Next Connection String

Remove the Item after the Database is Updated

Conclusion

Using Update Actions and Shared Properties during an application update allows you to very easily accomplish complicated processing on deployed clients. The C# / VB.NET update action lets you add your own code logic to your update, and using Shared Properties, your code can easily interact with built-in actions as well as other custom actions.

Download Example AppLife Update Project

Using Callable Action Lists to Update Multiple Databases During a Client Update

The recently released AppLife Update 4.5 includes many new features, one of which is the ability to create independent, callable action lists. By combining callable action lists with Shared Properties and applying conditional statements to update actions we can more easily accomplish advanced updating activity. One scenario which we can use to demonstrate this functionality is when an application uses multiple local databases. In order to update the installed application, all of the databases that are present must be updated. In this scenario an update will need to be able to:

  1. Identify all of the databases that are present.
  2. Iterate each of the local databases, applying the necessary change script to each one.
  3. Update the application assemblies.

For this example, the list of databases will be discovered by inspecting the application configuration file. Each database connection string is listed in the connectionStrings segment of the configuration file. For each of the listed databases, we’ll connect to the database, start a transaction, run a SQL script, commit the transaction and close the connection.

Note that all of the update actions and logic is packaged into a single, stand-alone update package. The update process embedded into the application discovers the new update, downloads it, and then initiates it. Once initiated, the Update Engine executes the update actions that perform the work. This example focuses completely on the update actions. No mention is made of the update process integrated into the application.

Identify and Manage the Local Database List

To read the databases that are present and manage the list as the databases are updated, I am going to read an item from the connectionStrings configuration element, update the database identified within it, and then remove the element from the configuration file. Using this method, I can accomplish all of the work using built-in update actions. This is not the only option available. Using Dynamic Code Actions offers more flexibility and an alternative approach. I’ll perform the same work using Dynamic Code actions in my next blog post.

Now, I mentioned that as we update the databases I am going to remove the element from the config file. We don’t want to modify the actual application configuration file so the first thing that I am going to do is copy the config file to the working update directory. This is a directory created by the update engine and it is deleted when the update ends. I’ll accomplish this by using a Run command line update action. This action includes a helper feature that allows us to use common directories to define two path variables. The Application Directory is the physical path of the application that launched the update. This is where the application configuration file resides. The Update Package Directory is the physical path to the temporary working directory that is created for this update by the update engine. I define <path1> to be the path to the application configuration file, and I define <path2> to be the path of my copy of the config file. I’ll call it config.xml. With the two paths specified, I’ll use them in the actual command line that copies the config file:

copy “<path1>” “<path2>”

To read the entries in the config file, I’ll use a Read Xml Node action. To use this action we specify an Xml file to read, define an XPath expression to identify the node value we are interested in, and then provide a name of a Shared Property to hold the string value of the Xml Node. The Shared Property can then be accessed and used by other actions as well as in conditional statements. I’ll be doing both with this Shared Property.

Here is what the config file section looks like:

<configuration>

<connectionStrings>

<add name=”Db1″ connectionString=”Data Source=.\SQLEXPRESS;Initial Catalog=TestDb1;Integrated Security=True”/>

<add name=”Db2″ connectionString=”Data Source=.\SQLEXPRESS;Initial Catalog=TestDb2;Integrated Security=True”/>

</connectionStrings>

</configuration>

I am interested in the connectionString, so my XPath expression is /configuration/connectionStrings/add/@connectionString

If multiple nodes are found, the action is configured to read the first node, and if no node is present a default value of None is assigned. We’ll use this in a conditional statement. If a node is present, a Shared Property with a key of ConnStr will be populated with a local database connection string. Next we’ll use a callable action list to update this database using SQL Server update actions, remove the connection string from our working config file, and recursively call itself until all of the databases represented in the config file are updated. If the ConnStr Shared Property does not equal the value “None”, we call the action list.

Update the Database

The databases I am updating are SQL Server databases. Therefore I am using the SQL Server Update Actions that are also new to AppLife Update 4.5. Note that there are separate provider-Independent database actions available that can be used for updating any type of database. Using the SQL Server Actions, for each database we are going to:

  1. Open a Database Connection. I’ll configure the action to place the database in single-user mode.
  2. Begin a SQL Server Transaction.
  3. Execute a database query.
  4. Commit the SQL Server transaction
  5. Close the SQL Connection

To open the connection, I set the connection string directly, and then use the ConnStr Shared Property we already defined. For many built-in actions, you can use Shared Properties that are strings by using a $SharedPropertyKey$ syntax. Wherever this is used, the Shared Property string value is expanded in-line during update execution. Notice that the connection itself is added to the Shared Properties collection using the defined key. Here the key is named SqlConection. We’ll use this Shared Property in the other database actions.

With a SQL Connection open, I’ll start a transaction using the Begin SQL Server Transactionaction. To use this action we specify the connection to use and provide a Shared Property key name for the transaction. The transaction Isolation Level can also be specified. If the update fails, this transaction will be rolled back automatically unless the transaction is committed by a Commit SQL Server transaction action.

With the transaction started, it can be used by one or more Run SQL Server query actions. In this example, I’ll just add a table.

Next, I’ll commit the SQL transaction and close the database connection. Taking this approach means that should any of the database updates fail, the database changes made in all previously updated databases would not be rolled back because they have already been committed. I chose this approach for simplicity, however the update could be refactored to maintain all transactions open until all of the databases have been updated, then iterate the transactions and commit each one. Using this alternate approach, should any SQL query fail, all databases would roll back their changes.

Closing the connection by specifying the Shared Property key…

Remove the Database from the Working Xml File

With the designated database updated, I am going to remove the connection string from the working Xml file using a Delete Xml Node action. To use this action, I specify the Xml file and as XPath expression identifying the node of interest, just like we did when defining the connection string. The node we are interested in is the connection string entry with the attribute value that matches the connection string of the database that we just updated.

With the node removed from the working file, we’ll again use a Read Xml Node action to read the next connection string in the file. If there is another connection string available, we’ll assign it to the ConnStr Shared Property. Otherwise, just like before, if no connection strings are left the value of the Shared Property will be set to “None”.

And now as long as the value of the ConnStr Shared property is not “None” we’ll call this same action list again, updating the next database in the list.

This process is repeated for all of the databases identified in the connectionStringsconfiguration element.

Replace the Application Assemblies

To complete this update, I’ll use an Add & Replace Files action to replace the main application executable.

Summary

By using the new callable action list feature and the Shared Properties collection, we are able to read the list of local databases from the application configuration file and iteratively update each one. The new features of AppLife Update 4.5 provide more power and flexibility while maintaining the robust, transactable operation of the AppLife updating engine. In a follow-up blog post, I’ll use Dynamic Code actions and custom C# code instead of Xml Actions to read the connection strings and use a collection object instead of a temporary file to manage the databases.

Download Example Project

View Execution Log

Terminal Services Application Updating

The AppLife Update solution is a perfect fit for maintaining applications that operate in multi-user terminal services environments. In a terminal services installation, there are likely many users simultaneously running your application. Application maintenance solutions must account for the fact that there can be multiple instances of an application running at one time as any running instance will maintain a lock on files and assemblies that must be replaced during an update process. The AppLife Update Solution ensures that all running instances of an application are shutdown prior to starting an update, and this is accomplished through a built-in Inter-process communications (IPC) feature. The IPC employed within AppLife Update communicates across all users and will shut down all instances of an application running in a terminal services environment, making the solution a perfect fit for maintaining terminal services hosted applications.

Out of the box, AppLife Update will shut down all instances of an application and allow you to update a terminal services application. There is however some customizations that can be applied to an application that is known to be targeting terminal services that will improve the users application updating experience.


Terminal Services Application Updating with AppLife Update

User Experience Customizations

The AppLife Update API provides an opportunity for the integrating developer to interact with the IPC process and update initiation (starting) procedure. We can use this opportunity to improve the user experience. For the instance that initiated the update, we’ll display an indeterminate progress dialog as all instances are being shut down, and for the non-initiating instances, we’ll display an informative dialog for a short period of time indicating that the application is about to be closed for maintenance. Further enhancements could allow terminal services users to cancel the update process, or perform additional work before the shutdown.

 

Code Snippet

1:  private void checkForUpdatesToolStripMenuItem_Click_1(object sender,  
2:        EventArgs e) {  
3:      if(updateController1.ShowCheckForUpdateDialog(this,  
4:        ErrorDisplayLevel.ShowExceptionMessage) == DialogResult.OK) {  
5:        if(updateController1.ShowDownloadUpdateDialog(this,  
6:          ErrorDisplayLevel.ShowExceptionMessage) == DialogResult.OK) {  
7:          //Launch the update from another thread  
8:          //To better accomodate terminal services  
9:          //installations, the UI will remain  
10:          //responsive as all of the other  
11:          //instances of the application are closed.  
12:          BackgroundWorker worker = new BackgroundWorker();  
13:          worker.DoWork += new DoWorkEventHandler(worker_DoWork);  
14:          worker.RunWorkerAsync(updateController1);  
15:          ApplicationUpdateInitiatedDialog dlg =  
16:          new ApplicationUpdateInitiatedDialog(true);  
17:          dlg.ShowDialog(this);  
18:        }  
19:      }  
20:    }  
21:    private void worker_DoWork(object sender, DoWorkEventArgs e) {  
22:      //Since the ApplyUpdate method call is a  
23:      //blocking call, and we want the UI to remain  
24:      //response as the user waits for all instances  
25:      //to close, we'll initiate the update on a  
26:      //background thread.  
27:      UpdateController controller = e.Argument as UpdateController;  
28:      if(controller != null) {  
29:        controller.ApplyUpdate(ApplyUpdateOptions.AutoClose);  
30:      }  
31:    }  
32:    private void updateController1_UpdateStarting(object sender,  
33:        UpdateStartingEventArgs e) {  
34:      //An update has been an initated and this  
35:      //application will shutdown. This dialog will  
36:      //inform any other users that the application  
37:      //is about to shutdown on them. Showing this  
38:      //dialog is import for Terminal Services  
39:      //installations. Note: It is possible to  
40:      //allow other users to cancel the ongoing  
41:      //update process  
42:      //This dialog is shown for 5 seconds  
43:      //before it is closed automatically  
44:      if(!e.IsInitiatingController) {  
45:        ApplicationUpdateInitiatedDialog dlg =  
46:      new ApplicationUpdateInitiatedDialog(false);  
47:        dlg.ShowDialog(this);  
48:      }  
49:    }  
50:  }  

 

Prevent Users from Restarting During the Update

During the actual update, the application files must be unlocked, and remain unlocked throughout the process. If a terminal services users attempts to start the application using their application shortcuts, the application assemblies could become locked and prevent the update form succeeding. To prevent this, we’ll make the first action taken during the update be to rename/remove the launching executable. If the launching executable is to be replaced during the update process, we’ll perform this action as the last step of the update. By taking this action, terminal services users will not be able to launch the application during the update process from existing shortcuts.

Download Terminal Services Example Project

No IT Needed

When deploying custom software applications at the enterprise level it is almost always necessary to coordinate the release of maintenance updates with each customer’s IT department. This is sometimes required for QA and software validation, but most often it’s because the IT department holds the responsibility of pushing software updates out to the individual PCs.

For you, the software vendor, you need maintenance patches to be installed quickly, sometimes very quickly, to ensure optimal user experience. For them, the IT dept, your software maintenance patch is just another of many tasks in the work queue. It won’t help to plead with them how important your release is. They support all departments, and every department thinks their needs are greater than the other guys, so they’ve heard it all before. This leaves you, the software vendor, potentially way down on the IT task queue. Fortunately for you, AppLife Update provides the tools to keep your deployed applications maintained without relying on the IT guy.

What are those tools, you ask? Why it’s the AppLife Update solution, consisting of:

  • Update Controller
    This .Net control integrates into your application to manage discovery, download, and initiation of your maintenance updates.
  • Update Permissions Elevation Windows Service
    This Windows Service deploys with your application and securely applies your updates under the privileges of the local system account. Using the service your application, running under a limited rights user, can still maintain your installation without administrative support.
    **Initial deployment requires administrative privileges.
  • Update Engine
    The update engine is downloaded by the Update Controller and performs the update on the deployed client. The update engine can execute the Windows Installer packages that you are already creating to update your software. In addition, it can do so much more. There are many built-in update actions to help you maintain your deployed software, and you can even write your own update actions in C# or Vb.Net

With these tools, and a day’s time, you can cast away those IT guy ties and take control of your application maintenance requirements, saving you and your customer time and money.

Implementing a Silent Updating Process

For many applications, it is not desirable to leave the decision of whether or not to update an application to the end-user. In fact, it is sometimes desirable to craft an updating process that operates silently, with no user interaction at all. AppLife Update makes it very easy to implement such an update process. In this blog post, I’ll walk through implementing a silent updating process.

The Process

When our demo application starts up, we’ll check for updates asynchronously in the background. If an update is available, we’ll asynchronously download the update in the background. To make the process the least intrusive to the user, our process won’t take any action when the download completes. Since the downloaded update package is cached locally as it downloads, it will be available the next time the application is restarted. Upon application startup, we’ll check to see if an update is fully downloaded and if so, go ahead and apply the update. If the update is not yet fully downloaded, the download will pick up where it left off.

Handling Unexpected Errors

Many things can go wrong as we check for and download updates over a network, and since our process is being performed silently, we’ll need to handle any errors that occur and decide what to do about it.

We’ll also need to be able to identify errors during the silent update execution. When an update is applied, our application is shutdown so that the application assemblies can be replaced. When the update process finishes, the application is restarted automatically. During startup, we’ll look at the results of the last update to see if any errors occurred. If any errors occurred, we can inform the user or send an administrative notice to support.

Now that we have a plan, we can go forward with implementation.

Implementation

With the desired behavior identified, we’ll implement this updating process using AppLife Update. To start, we’ll create a new Windows Forms application and add an Update Controller to the main form from the AppLife Update toolbox palette.

The Update Controller application programming interface (API) will provide all of the functionality we need to accomplish our identified update process. Once the update controller is added to your form, go ahead and set up a new project using the control smart tag. This will create an AppLife Update project that you’ll use to create and publish updates for your application.

Note: The Update Controller does not need to reside on a form. You can create an Update Controller and configure it programmatically in any .Net application.

Now that we have an Update Controller in the application and a project setup, we’ll implement our desired update process.

Checking For Updates

To check for updates, we’ll use the CheckForUpdateAsync method on the Update Controller. This check is performed asynchronously, and when the check completes, the CheckForUpdateCompleted event is raised.

   1: using Kjs.AppLife.Update.Controller;  
   2:    
   3:    
   4: public Form1() {  
   5:   InitializeComponent();  
   6:    
   7:   //check for updates  
   8:   updateController1.CheckForUpdateCompleted +=   
   9:    new CheckForUpdateCompletedEventHandler(  
  10:      updateController1_CheckForUpdateCompleted);  
  11:   updateController1.CheckForUpdateAsync();  
  12: }  
  13:    
  14: void updateController1_CheckForUpdateCompleted(object sender,  
  15:   CheckForUpdateCompletedEventArgs e) {  
  16:   if(e.Result == true) {   
  17:     //An update is availabe  
  18:    
  19:   }  
  20: }  

After the check completes, we can check to see if the update is already downloaded by inspecting the IsDownloaded property of the update. If the update is downloaded, we can go ahead and apply the update. If it is not, we’ll initiate the download. The download will pick up from where any previous instance left off. When the download process completes, the DownloadUpdateCompleted event will be raised. We won’t take any action when the download completes, but we will use this event for error handling later. We are not showing download progress in this implementation, but there is also a DownloadUpdateProgressChanged event to monitor download progress.

 1: public Form1() {  
   2:   InitializeComponent();  
   3:    
   4:   //check for updates  
   5:   updateController1.CheckForUpdateCompleted +=   
   6:    new CheckForUpdateCompletedEventHandler(  
   7:     updateController1_CheckForUpdateCompleted);  
   8:   updateController1.DownloadUpdateCompleted +=  
   9:     new AsyncCompletedEventHandler(  
  10:     updateController1_DownloadUpdateCompleted);  
  11:   updateController1.CheckForUpdateAsync();  
  12: }  
  13:    
  14: void updateController1_DownloadUpdateCompleted(object sender,  
  15:    AsyncCompletedEventArgs e) {  
  16:   //Download completed. Ready to apply on next startup.  
  17: }  
  18:    
  19: void updateController1_CheckForUpdateCompleted(object sender,  
  20:    CheckForUpdateCompletedEventArgs e) {  
  21:   if(e.Result == true) {   
  22:     //An update is available  
  23:     if(updateController1.CurrentUpdate.IsDownloaded == true) {  
  24:       //the update is already downloaded, go ahead and apply it.  
  25:     } else {   
  26:       //start the download  
  27:       updateController1.DownloadUpdateAsync();  
  28:     }  
  29:   }  
  30: }  

Applying the Update

With the update downloaded, we can apply the update. We want to apply the update silently, so we’ll pass in a parameter that prevents the update window from being displayed. This method call will close the application, launch the update process, and restart after the update completes.

   1: void updateController1_CheckForUpdateCompleted(object sender,   
   2:    CheckForUpdateCompletedEventArgs e) {  
   3:   if(e.Result == true) {   
   4:     //An update is availabe  
   5:     if(updateController1.CurrentUpdate.IsDownloaded == true) {  
   6:       //the update is already downloaded, go ahead and apply it.  
   7:       updateController1.ApplyUpdate(ApplyUpdateOptions.NoUpdateWindow);  
   8:     } else {   
   9:       //start the download  
  10:       updateController1.DownloadUpdateAsync();  
  11:     }  
  12:   }  

That’s it! We have implemented a silent update process, and in a perfect world we’d be done. But we don’t live in a perfect world, and unexpected events occur. Since this process is completely silent to the user, we’ll need to prepare for error conditions programmatically and take appropriate actions.

Error Handling

Handling errors that occur during the update check and download process is straightforward. The asynchronous event handlers can check for errors that occurred and look at the specifics of the error to aid in determining how to handle the error. If you are using the http protocol for updating, the inner exception is probably a WebException class, which provides additional error information. For instance, this information could be used to ignore connectivity errors. For this demonstration, we’ll display a message box if any errors occur during the update check or download process.

   1: void updateController1_DownloadUpdateCompleted(object sender,  
   2:    AsyncCompletedEventArgs e) {  
   3:   //Download completed. Ready to apply on next startup.  
   4:   if(e.Error != null) {  
   5:     //An error occurred.  
   6:     if(e.Error is DownloadException) {  
   7:       //A download exception is thrown when something unexpected happens.  
   8:       //The InnerException property will hold the exception that occurred.  
   9:       string errorMsg = "";  
  10:       if(e.Error.InnerException != null) {  
  11:         errorMsg = e.Error.InnerException.Message;  
  12:       } else {  
  13:         errorMsg = e.Error.Message;  
  14:       }  
  15:       MessageBox.Show(  
  16:        string.Format("An error occurred downloading an update: {0}",  
  17:         errorMsg));  
  18:     }  
  19:   }  
  20: }  
  21:    
  22: void updateController1_CheckForUpdateCompleted(object sender,  
  23:    CheckForUpdateCompletedEventArgs e) {  
  24:   if(e.Error != null) {  
  25:     //An error occurred.  
  26:     if(e.Error is DownloadException) {   
  27:       //A download exception is thrown when something unexpected happens.  
  28:       //The InnerException property will hold the exception that occurred.  
  29:       string errorMsg = "";  
  30:       if(e.Error.InnerException != null){  
  31:         errorMsg = e.Error.InnerException.Message;  
  32:       }else{  
  33:         errorMsg = e.Error.Message;  
  34:       }  
  35:       MessageBox.Show(  
  36:      string.Format("An error occurred checking for updates: {0}",  
  37:        errorMsg));   
  38:     }  
  39:   }  
  40:        …  
  41:   

Detecting a Failed Update Execution

As an update is executed, the application being updated is shutdown. When an update is executed with a user interface, any errors that occur would be presented to the user through the visual updating interface. Since we are applying this update silently, we will need to inform the user of any errors that occur another way. After the update completes, the application is restarted. We can use methods of the Update Controller to easily investigate the results of the last update that was applied. We’ll use this method to look up what happened, and inform the user of any errors that occurred.

   1: InitializeComponent();  
   2:    
   3:   //check for updates  
   4:   updateController1.CheckForUpdateCompleted +=   
   5:    new CheckForUpdateCompletedEventHandler(  
   6:     updateController1_CheckForUpdateCompleted);  
   7:   updateController1.DownloadUpdateCompleted +=   
   8:    new AsyncCompletedEventHandler(  
   9:     updateController1_DownloadUpdateCompleted);  
  10:     
  11:   //Read last update results  
  12:   LastUpdate lastUpdate = updateController1.ReadLastUpdateInformation();  
  13:   if(lastUpdate != null &&  
  14:    lastUpdate.Result == UpdateResult.Failure &&   
  15:    lastUpdate.StartingVersion == updateController1.Version) {  
  16:     //An update failed. Prompt the user.  
  17:     if(MessageBox.Show(string.Format(  
  18:    "An unexpected error occurred while silently applying an update:  
  19:   {0}\n\nWould you like to retry the update?",  
  20:     lastUpdate.ErrorText), "Update Error", MessageBoxButtons.YesNo) ==   
  21:     DialogResult.Yes) {   
  22:       //launch an interactive update  
  23:       updateController1.UpdateInteractive(this);  
  24:     }  
  25:   } else {   
  26:     updateController1.CheckForUpdateAsync();  
  27:   }  

Conclusion

Using the AppLife Update API, you can quickly and easily implement a non-interactive silent updating process that can be used in any .Net application. The application checks for, and downloads updates asynchronously. When the application launches and an update has been fully downloaded in a previous session, the update is applied silently. If any errors occur, the user is notified. The example can be easily extended and improved as necessary to fit specific updating requirements.

How to Hide the Error When Out of Network Range

All of the visual updating controls that ship with AppLife Update show an error icon and message whenever an update check fails.  Sometimes though this isn’t the most desirable behavior.  For instance, if your application runs on a laptop that is frequently moving in and out of network coverage.  In a situation like this, an update check failure is expected and you probably don’t want to display a concerning error icon to your end user.

To address this, we can look at the results of an update check and check to see why the error occurred.  If the issue is a network connection error, and we expect spotty network connectivity, we can choose to hide the control, instead of displaying an error to the user.

By listening for the CheckForUpdateCompleted event of the Update Controller, information about the error can be inspected and we can hide the control if a connectivity issue is discovered.

   1: private void updateController1_CheckForUpdateCompleted(object sender,  
   2:    Kjs.AppLife.Update.Controller.CheckForUpdateCompletedEventArgs e) {  
   3:   if(e.Error != null &&  
   4:     e.Error.InnerException != null &&  
   5:     e.Error.InnerException is WebException) {  
   6:     WebException ex = e.Error.InnerException as WebException;  
   7:     if(ex.Status == WebExceptionStatus.NameResolutionFailure ||  
   8:       ex.Status == WebExceptionStatus.ConnectFailure) {  
   9:     //There is connectivity issue.   
  10:     //Hide the update control, instead of showing an error.  
  11:       this.updateDisplay1.Visible = false;  
  12:     }  
  13:   }  
  14: }  

Another Alternative

It might be more preferable to display the control but show that no updates are available. We can accomplish this as well by including with the application a Director.Xml file that contains no updates.  Just copy the Director.Xml file from your publish location and remove all Versionnodes. With this file present, instead of hiding the control, we can change the update location to the local application path and initiate another update check.  This time no updates will be found and the control will indicate this.

   1: Kjs.AppLife.Update.Controller.CheckForUpdateCompletedEventArgs e) {  
   2: if(e.Error != null &&  
   3:   e.Error.InnerException != null &&  
   4:   e.Error.InnerException is WebException) {  
   5:   WebException ex = e.Error.InnerException as WebException;  
   6:   if(ex.Status == WebExceptionStatus.NameResolutionFailure ||  
   7:     ex.Status == WebExceptionStatus.ConnectFailure) {  
   8:     //There is connectivity issue.   
   9:     //Start an update check that will not find an update.  
  10:     string appDir = Path.GetDirectoryName(Application.ExecutablePath);  
  11:     this.updateController1.UpdateLocation = appDir;  
  12:     this.updateController1.CheckForUpdateAsync();  
  13:   }  
  14: }  

With no network connectivity, this is what is displayed to the user.

These techniques can be used with any of the visual updating controls and provides a method

Application Updating by File Patching or Replacement

There are many update actions available in AppLife Update to manipulate files on deployed systems during a software update. You’ll see in the list of update actions, that there are actions to replace files, and also actions that patch files and folders. When would you choose a patching action over a replacement action? What’s the difference anyways?

Replacing Files

Update actions that add & replace files will place the complete and intact file into the update package during the update build process, and then as the update package is executed on a deployed client, the intact file is extracted from the update package and replaces the existing previous version.

Patching Files

Actions that patch a file or folder do not place the entire file in an update. Instead, during the update build process a difference file is generated by comparing an earlier revision of a file with the current revision and this difference file is added to the update package. Difference files are often much smaller than a complete file, which results in a significantly smaller update package. As the update is executed on the deployed system, the difference file is combined with the earlier version to reproduce the current version file.

Advantages & Disadvantages

Replacing files makes an update far less dependent on the specific version of the application being updated on the deployed client. For many applications, using replacement file actions allows their update to successfully update any previous version. In this scenario, once a new version update is published, previous updates are no longer necessary and can be removed from the update server.

The advantage of patching files is that update packages can be significantly smaller in physical size.

The disadvantages of both types are converse to their advantages. Replacing files results in much larger update packages. The disadvantage of patching actions is very stringent versioning requirements.

So when would you choose one over the other? Here are a few factors that can contribute to this decision.

1. Are specific file versions reliably present?
If the specific version of a target file cannot be guaranteed based on the installed version number of the application, patching cannot be used. The difference file must be combined with the exact base file used to create it. If that exact file is not present on the deployed system, the new file cannot be created during the update process and the action will fail.  This situation can occur when end users might manipulate the files and assemblies in the application installation directory.

2. If the files to update are physically small, the number of clients to update is small, or network bandwidth is not a concern, consider using file replacement actions.
In general, file replacement actions are more robust due to the lack of specific file versioning requirements. In addition, because updates can target many previous versions, clients that do not regularly update their software as updates are published will apply fewer updates.

3. To minimize update package size, use patching actions
Applications with many deployed clients, or physically large files should consider using patching actions. A Patch Folder action can operate recursively on an entire folder structure, making it easy to patch the entire application with a single update action. When using patching actions, it is necessary to build your updates to target specific previous versions.

4. Consider combining both patching and replacing
For some applications, certain files can be safely patched while others cannot. Both file action types can be used in a single update.

Wrapping it up, with AppLife Update you have a choice to patch files or replace files during an update. Patching creates much smaller update packages, but requires strict adherence to versioning. Replacing files creates larger update packages but is far more forgiving and allows a single update to target multiple previous versions. So choose the right strategy or your application and start updating!

Application Updates and Mayhem

Have you seen the Allstate mayhem commercials? They are pretty good. If you haven’t seen them, take a few seconds and watch the video below. At this point, you may be asking yourself what does that have to do with software. But then again if you’ve been around awhile, you may not be… These spots have a lot in common with software because in software, mayhem is everywhere.

As a software developer, if you are not finding bugs, you’re fixing them. Mayhem. As a user, unexpected behaviors often occur as you go about using the software you use. Mayhem. And as an administrator, you spend a lot of time working around unexpected issues. Mayhem.

And so it is with maintaining deployed applications, or automatic application updating. In a perfect world, an application update would never fail. Most don’t. And most houses don’t catch fire either, yet we buy insurance just in case. Around here, we put a lot of effort into ensuring that when a software update is applied, one of two outcomes will result. A successful update, or a complete rollback. We prefer the former, but plan for the later. And for most actions rolling back, and preparing for the rollback that hopefully will never occur, requires more development work than the execution of the action. Let’s look at files. Protection from mayhem is almost all of the work involved in file actions. The first thing we do is verify that our update process has permissions to replace the file. If we don’t, there is no sense in proceeding. We can stop before doing anything, preventing mayhem. Then we back up the file, including the assigned permissions, just in case mayhem happens later on, we can restore the original file. Such is the case with all of the built-in updating actions. With the exception of the Install .Net 4.0 Framework action, they either complete their work or rollback completely. For that action, mayhem is better avoided by leaving the framework successfully installed than to remove it during a rollback.

Our Adjuster

When mayhem happens, insurance companies like Allstate will send out an adjuster to determine the cause and extent of the damage. Our adjuster equivalent is the update action log files. When mayhem occurs, there are few things worse than having to tell your boss, “I don’t know why that happened”, because that means you also don’t know the answer to the inevitable next question. “How are we going to prevent it from happening again?” Update action logs tells the story of what happened (or didn’t happen) during an update, and with an update log you’ll know what happened and be able to determine how to prevent it from happening again.

If your house burns down, we can’t help much. Hopefully you have Allstate for that. But for protecting your software updates from mayhem, look to AppLife Update. You’ll be in good hands.

Scroll to Top