Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Using SmartAssembly for Smart Client

0.00/5 (No votes)
24 Dec 2003 1  
A solution that uses pull model to keep assemblies of an application up to date.

Sample Image - maximum width is 600 pixels

Introduction

For organizations that run information systems, it's a common problem to keep all instances of a desktop application up to date with the latest version of executables, libraries, configuration and other files. It always costs great deal of resources (human and time). Ideally, administrators and support engineers would like to deploy the updated files once, to a central server, and have the updated files automatically replicated to each desktop installation. The SmartAssembly is such a solution that uses "pull model" to do so for .NET applications.

.NET framework has provided the support for "Smart Client" applications. You can config <DEPENDENTASSEMBLY> nodes in the configuration file (see MSDN for details) and let the runtime to download the associated assemblies. However, there are some weaknesses:

  1. The configuration is too simplex and has a lack of flexibility.
  2. The download progress is a "blackbox", and not friendly to the end users.
  3. In the case of late binding, if you load an assembly by its weak name (not specify the version, public key and culture), the runtime will open a HTTP connection to check the remote one every time when you load the same assembly. Of course, it will waste a great deal of time if you need to load some assemblies frequently.

In addition, Microsoft has published an Application Block named Updater which also provides a "pull model" solution to automatically download application updates from a central location. But it does not work on-the-fly. By contraries, it frequently checks the remote manifest file to determine whether there is a need to do update. If so, it starts an Updater process and shuts down the user application to download the changed files. After downloading all the changed files, it then starts the user application again.

SmartAssembly is designed to meet the following requirements:

  1. Make the download progress controllable, visible and customizable.
  2. Support break-and-resume downloading.
  3. Provide a framework for supporting compressed file.
  4. Ease the configuration work for deployers.
  5. Provide deployers with flexibility to configure the system.
  6. Provide developers with customizability and extensibility.

Working Principle

There is a set of Resolve events defined in classes AppDomain and Assembly. These events provide developers a chance to resolve assemblies, resources and modules by themselves. SmartAssembly only uses the AssemblyResolve event. When that event is fired, SmartAssembly searches the configuration for the associated codebase URI, then downloads the assembly according to the URI and save it to local, at last loads the assembly and returns it back to AppDomain.

Using SmartAssembly

It's divided into two parts to describe how to use SmartAssembly. At first, it's required to add a piece of code in your application. Then, write the configuration file and deploy your application.

Coding

By the way, there is a CHM file accompanied with the source package. You can refer it for the details about how to use SmartAssembly.

1. Create a SmartAssemblyManager Instance

The class SmartAssemblyConfig is used to create a SmartAssemblyManager instance from a SmartAssembly configuration section. There are three ways to do so.

  1. Load from the application configuration file. By this way, the returned SmartAssemblyManager instance is also SmartAssemblyManager.CurrentManager, and its CurrentDomain property has already been set to AppDomain.CurrentDomain. In contrast, the other two ways just create a new SmartAssemblyManager instance:
    SmartAssemblyManager sam = 
     ConfigurationSettings.GetConfig(Lightning.SmartAssemblyConfig.SectionName) 
     as SmartAssemblyManager;
  2. Load from an individual configuration file for SmartAssembly:
    SmartAssemblyManager sam = SmartAssemblyConfig.Create("c:\test.xml");
  3. Load from a prepared configuration XmlNode:
    // Prepare the configuration XmlNode
    XmlNode section = ...;
    SmartAssemblyManager sam = SmartAssemblyConfig.Create(section);
    if( null != sam ) {
        sam.CurrentDomain = AppDomain.CurrentDomain;
    }

2. Enable SmartAssembly

To enable SmartAssembly, you need to set the CurrentDomain property of the SmartAssemblyManager instance to an AppDomain.

if( null != sam ) {
    sam.CurrentDomain = AppDomain.CurrentDomain;
}

As described above, if you create SmartAssemblyManager instance from the application configuration file, and just want to handle the AppDomain.CurrentDomain, then you can skip this step.

3. Customize the Routine to Download SmartAssembly Configuration File

SmartAssembly configuration file can be an individual file, or even a database record, thus you can download it or get it from the database. SmartAssemblyConfig provides a static delegate for you to do so.

Public static DownloadConfigDelegate DownloadConfigCallback;

4. Customize the Routine to Uncompress a File

SmartAssembly does not provide that function in this version. However, it provides a well defined framework to ease your work. All that you need to do is to develop an ExtractFileInZipDelegate function and handle the following delegate of the SmartAssemblyManager instance, then follow the configuration rules for compressed files (see the Deploying section for details).

public ExtractFileInZipDelegate ExtractFileInZipCallback;

5. Customize Other Routines

SmartAssembly also provides customizability for LoadAssembly, LoadlocalAssembly, DownloadFile and so on.

public event LoadAssemblyEventHandler CustomLoadAssembly;
public event LoadAssemblyEventHandler CustomLoadlocalAssembly;

public event SmartAssemblyEventHandler CustomDownloadFile;
public event SmartAssemblyEventHandler CustomDownloadCompleted;
public event DownloadProgressEventHandler CustomDownloading;
public event SmartAssemblyCancelEventHandler BeforeDownload;
public event SmartAssemblyEventHandler AfterDownload;

Deploying

The deploying work is focused on how to write the configuration file. This section will give you the details to configure SmartAssembly.

1. smartAssemblyConfiguration

This is the root node of the configuration.

<a href="#node_sac"><smartAssemblyConfiguration></a>  
  <a href="#node_sc"><smartConfig></a>
  <a href="#node_cd"><cacheDirectory></a>
  <a href="#node_ns"><netSetting></a>
  <a href="#node_ll"><loadLogic></a>
  <a href="#node_da"><dependentAssembly></a>
    <a href="#node_ai"><assemblyIdentity></a>
    <a href="#node_cb"><codeBase></a>
    <a href="#node_br"><bindingRedirect></a>

Child Element

Description

Required

<smartConfig> Specifies the setting for smart configuration file. N
<cacheDirectory> Specifies the settings for cache directories. Y
<netSetting> Specifies the arguments for network connection. N
<loadLogic> Specifies the logic to load assemblies. N
<dependentAssembly> Encapsulates binding policy and assembly location for each assembly. N

2. smartConfig

Configures the individual SmartAssembly configuration file. If this node exists, SmartAssembly will try to load configuration from that setting and the other nodes will be ignored.

<a href="#node_sac"><smartAssemblyConfiguration></a>
  <a href="#node_sc"><smartConfig></a>

Attribute

Description

Required

Default

href Specifies the URL where SmartAssembly can find the configuration file. N  
filePath Specifies the full file path where SmartAssembly saves the configuration file to. N  
tryLocal Tries the local file if failed to download the remote one. N true

3. cacheDirectory

Settings for local cache directory.

<a href="#node_sac"><smartAssemblyConfiguration></a>
  <a href="#node_cd"><cacheDirectory></a>

Attribute

Description

Required

Default

path Specifies the cache path for SmartAssembly to store assemblies. Y  
type Specifies the type of the cache path. (Enum: KeepNewestOnly, KeepAllVersions, KeepAllByHashCode) N KeepNewestOnly
packageFilePath Specifies the cache path where SmartAssembly stores compressed files. N  
shadowCopyCachePath Specifies the cache directory for ShadowCopy. N  
shadowCopyExtraDirs Specifies the extra directories for ShadowCopy. N  

4. netSetting

Settings for network arguments.

<a href="#node_sac"><smartAssemblyConfiguration></a>
  <a href="#node_ns"><netSetting></a>

Attribute

Description

Required

Default

agentName Specifies the agent name for a HttpRequest. N  
bufferSize Specifies the size of the download buffer. N 4096
timeout Specifies the the time-out value for a HttpRequest. N -1

5. loadLogic

The logics to load assemblies.

<smartAssemblyConfiguration>
  <loadLogic>

Attribute

Description

Required

Default

notCheckRemoteWhileMatched Specifies whether to check the remote file (assembly) even when the local one matched with the request completely. N true
downloadNewerOnly Specifies whether to do download only when the remote file is newer than local one. N false
tryLatestVersionWhileNotMatch Specifies whether to use the highest version that is configured in the configuration file automatically. N true
smartMatch Specifies whether to use "Smart Match" technique. N true
loadSymbols Specifies whether to try downloading the symbol file(s) associated the assemblies that are to be resolved. N false
symbolsFileExtName Specifies the extension of symbol file(s) that will be used to calculate the URI. N pdb
loadMethod Specifies the method to load Assembly. (Enum: Directly, ViaMemoryStream, ShadowCopy) N Directly
identifiedByName Specifies whether to use the weak name to identify an assembly. N false
workOffline Specifies whether to work in offline mode. N false
baseHref Specifies the base URI, which will be used to calculate the URI of the assemblies. N  
defaultZipFile Specifies the default compressed file name, which will be used to calculate the URI of the assemblies that have not been configured. N  

6. dependentAssembly

Encapsulates binding policy and assembly location for each assembly. Use one <dependentAssembly> element for each assembly.

<a href="#node_sac"><smartAssemblyConfiguration></a>
  <a href="#node_da"><dependentAssembly></a>
      <a href="#node_ai"><assemblyIdentity></a>
      <a href="#node_cb"><codeBase></a>
      <a href="#node_br"><bindingRedirect></a>

Child Element

Description

Required

<assemblyIdentity> Contains identifying information about the assembly. This element must be included in each <dependentAssembly> element. Y
<codeBase> Specifies where the runtime can find a shared assembly if it is not installed on the computer. N
<bindingRedirect> Redirects one assembly version to another or even to another assembly. N
6.1. assemblyIdentity

Contains identifying information about the assembly.

<a href="#node_sac"><smartAssemblyConfiguration></a>
  <a href="#node_da"><dependentAssembly></a>
     <a href="#node_ai"><assemblyIdentity></a>

Attribute

Description

Required

Default

name The name of the assembly. Y  
publicKeyToken A hexadecimal value that specifies the strong name of the assembly. N  
culture A string that specifies the language and country/region of the assembly. N  
notCheckRemoteWhileMatched   N true
6.2. codeBase

Specifies where the common language runtime can find an assembly.

<a href="#node_sac"><smartAssemblyConfiguration></a>
  <a href="#node_da"><dependentAssembly></a>
    <a href="#node_cb"><codeBase></a>

Attribute

Description

Required

Default

version Specifies the version of the assembly the codebase applies to. The format of an assembly version number is major.minor.build.revision. Valid values for each part of the version number are 0 to 65535 N  
href Specifies the URL where the runtime can find the specified version of the assembly. N  
6.3. bindingRedirect

Redirects one assembly version to another or even to another assembly.

<a href="#node_sac"><smartAssemblyConfiguration></a>
  <a href="#node_da"><dependentAssembly></a>
      <a href="#node_br"><bindingRedirect></a>

Attribute

Description

Required

Default

oldVersion

Specifies the version of the assembly that was originally requested. There are three possible formats:

  • Single: n.n.n.n
  • Range: n.n.n.n - n.n.n.n or n.n.* (same as n.n.0.0 - n.n.65535.65535)
  • Collection: n.n.n.n; n.n.n.n; n.n.n.n; ...
N  
newVersion

Specifies the version of the assembly or a new assembly to use instead of the originally requested version. The format of a new assembly should like this:

NewAsm, Version=1.0.0.0, Culture=en-us, PublicKeyToken=

N  

A Completed Example

<configuration>
  <!-- Declaring custom config  section parsing class -->
  <configSections>
    <section name="smartAssemblyConfiguration" 

      type="Lightning.SmartAssemblyConfig, SmartAssembly"/>
  </configSections>
  
  <smartAssemblyConfiguration>
    <!--smartConfig temp="%ComAppData%\Assembly\config.xml"
           href="http://localhost/Assembly/Test.xml" /-->
    
    <cacheDirectory path="%ComAppData%\Assembly\Origin"

            type="KeepAllVersions"

            packageFilePath="%ComAppData%\Assembly\ZipFiles" />
    <netSetting agentName="Smart Assemlbly Manager"

          bufferSize="8182" timeout="-1"

          proxyMode="" />
    <loadLogic notCheckRemoteWhileMatched="true"

          downloadNewerOnly="true"

          tryLatestVersionWhileNotMatch="true"

          smartMatch="true"

          loadSymbols="true"

          loadMethod="ShadowCopy"

          identifiedByName="true"

          workOffline="false"

          baseHref="http://localhost/Assembly/" />

    <dependentAssembly>
      <assemblyIdentity name="MainControl" publicKeyToken="" culture="" />
      <codeBase version="" href="old/MainControl.dll"/>
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="SubControl1"

                publicKeyToken="a9061ec0e85bbecf"

                culture="" />
      <codeBase version="0.0.1.0" href="old/SubControl1.dll"/>
      <codeBase version="2.1.0.0" href="new/SubControl1.dll"/>
      <bindingRedirect oldVersion="0.0.0.1; 0.0.0.2" newVersion="0.0.1.0" />
      <bindingRedirect oldVersion="1.0.*" newVersion="2.1.0.0" />
      <bindingRedirect oldVersion="1.1.0.0 - 2.0.0.0" newVersion="2.1.0.0" />
    </dependentAssembly>
    <dependentAssembly>
      <assemblyIdentity name="SubControl2" publicKeyToken="" culture="" />
      <codeBase version="" href="old/SubControl2.dll"/>
    </dependentAssembly>
    
    <dependentAssembly>
      <assemblyIdentity name="MainControl.resource" publicKeyToken="" culture="" />
      <bindingRedirect oldVersion="*" 

        newVersion="MainControl.resource, Version=, Culture=, PublicKeyToken=" />
    </dependentAssembly>
    </smartAssemblyConfiguration>
</configuration>

About the Demo

There are three projects in the demo solution:

SmartAssembly SmartAssembly library.
Test The demo application which can be executed stand alone or be loaded by IE (see the readme.txt for details.)
TestControl The demo assembly which will be "SmartAssemblied".

Points of Interest

There are some tips for deploying, for example:

  • Deciding where to set the cache directory to, to AppBaseDir, to AppData or to ComAppData.
  • How to package the assemblies if you use compressed file(s), as an application, as a package or some other strategies?

License

This article has no explicit license attached to it, but may contain usage terms in the article text or the download files themselves. If in doubt, please contact the author via the discussion board below.

A list of licenses authors might use can be found here.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here