Deploying winforms program from repository using C#

This example will help you out in cases when you have to deploy a solution in many workstations, without the need of copying the files in any of them manually. The strategy used is to store the compressed program into a repository and give the ability to each workstation for updating the program if necessary and run it, all in one step.

In order to accomplish this we will create two C# console projects with the proper app.config file, to cover the following tasks:

  • Import the new deployment into a repository.
  • Export the new deployment from the repository.
  • Execute the latest deployment.

Note: As for the repository, this example uses the main app database but it’s also suitable for file repository as well.

Import Console program

This console is in charge of importing the new package into the repository, therefore the main operations must be getting the new package, convert it into byte[] and make it available for further use. This console is only suitable for the database approach, as none of this is necessary if you prefer/need to use a ftp or shared folder.

string filePath = (ConfigurationManager.AppSettings["sourcePath"] + "binary.zip");
byte[] fileData = File.ReadAllBytes(filePath);

In the first line we take the sourcePath key from the config file, that is, the path where will be allocated the new program. As for the second one, we convert the zip file into a byte array.

string insertQuery = @"INSERT INTO BINPACKAGES (VERSION,BINARY) VALUES(1,@FileData)";
insertCommand = new SqlCommand(insertQuery, sqlconnection);
SqlParameter sqlParam = insertCommand.Parameters.AddWithValue("@FileData", fileData);
sqlParam.DbType = DbType.Binary;
insertCommand.ExecuteNonQuery();

Finally, fileData is inserted into the BINPACKAGES table, by passing it as sqlparameter as binary type. If you check the source code on github, you’ll see I chose deleting all rows before inserting the program. This’s optional but recommended.

Deploy & Run Console program

This console will compare assembly information between the program from the repository and local one, install the new version if needed and then, run the program in the workstation.

const string query = "SELECT VERSION, BINARY FROM BINPACKAGES";
const string fileName = "binary.exe";
const string binpackage = "binary.zip";
const string PrgmPath = ConfigurationManager.AppSettings["PrgmPath"];
const string newPath = ConfigurationManager.AppSettings["newPath"];
...
...
var newFile = System.IO.Path.Combine(newPath, binpackage);

SqlCommand cmd = new SqlCommand(query, conn);
SqlDataAdapter sda = new SqlDataAdapter(cmd);
DataSet bintable = new DataSet();
sda.Fill(bintable);
foreach (DataRow dr in bintable.Tables[0].Rows){
    File.WriteAllBytes(newFile, (Byte[])dr.Table.Rows[0]["BINARY"]);               
    ZipFile.ExtractToDirectory(newFile, newPath);
}

As you can see, we get the record (bintable DataSet) from the database and extract the latest program into the path configured by the newPath key.

newFile = System.IO.Path.Combine(newPath, fileName);
destFile = System.IO.Path.Combine(PrgmPath, fileName);
            
AssemblyName currentAssemblyName = AssemblyName.GetAssemblyName(destFile);
AssemblyName updatedAssemblyName = AssemblyName.GetAssemblyName(newFile);

Then we must use assembly information to verify if we need to update the program, therefore we extract the AssemblyName property from both exe files.

if (updatedAssemblyName.Version.CompareTo(currentAssemblyName.Version) > 0)
{
    // To copy a folder's contents to a new location:
    // Create a new target folder, if necessary.
    if (!System.IO.Directory.Exists(PrgmPath))
        System.IO.Directory.CreateDirectory(PrgmPath);

    if (System.IO.Directory.Exists(newPath))
    {
        string[] files = System.IO.Directory.GetFiles(newPath);

        // Copy the files and overwrite destination files if they already exist.
        foreach (string s in files)
        {
            // Use static Path methods to extract only the file name from the path.
            fileName = System.IO.Path.GetFileName(s);
            destFile = System.IO.Path.Combine(PrgmPath, fileName);
            System.IO.File.Copy(s, destFile, true);
        }
    }
    else
        Console.WriteLine("Source path does not exist!");
    
}
Process.Start(ConfigurationManager.AppSettings["PrgmPath"] + fileName);

In case the repository program is newer we start deploying it. As the processing is taking place, we need to make sure the temp path exists when exporting the new content, so if it does not we create it and once everything is prepared, all files are overwritten. The last line executes the current program (already updated if it was the case).

App Config sample


     <add key="sourcePath" value="C:\binary\" />
     <add key="newPath" value="C:\Temp\binary\" />
     <add key="PrgmPath" value="C:\myprograms\binary\" />

Final Comments

Of course this’s a rough solution and needs to be improved, or even there’re better approaches, but my intention with this post was to show you one of many ways to achieve tasks like the one proposed here.
I’ll upload both App.Config and Program.cs files (the console programs will be placed in one sample file) on binaryupdate github repo so you can check it also there.

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 )

w

Connecting to %s