Skip to main content

Restore Database during Application Deployment

In applications developed in .NET for data storage is often used MS SQL Server. During distribution of the application, one of the frequently asked questions is Database installation method on the Target Machine (the PC or workstation your project will be installed on).
The method described in this paper allows execute operation Restore Database at installation stage of application.

Application and Database Development

Start with creating an application that works with database.
Create a sample database with name “SampleDatabase”.
Create a sample application that connects to the created database and retrieves data.
(“SampleApplication” is in the file attached to the paper.)

Create Installer

Now let’s create an installer.
  1. First of all, we need to create a Backup for the Database in “SQL Server Management Studio Express” by selecting required base and clicking “Back Up…” clip_image001.jpg
    1. Then select path and backup copy filename. In this case it is “SampleDatabase.bakclip_image002.jpg
  2. Next we create Custom Action.
    1. Although standard actions are sufficient to execute an installation in most cases, custom actions enable the author of an installation package to extend the capabilities of standard actions by including executables, dynamic-link libraries, and script.
    2. Create a new project using the Class Library template, then in the Name box, type “SampleInstallLib”, in the Solution Name box, type “SampleInstallApp”, click OK. clip_image003.jpg
      The project is added to Solution Explorer.
    3. On the Project menu, choose Add Class, and then in the Add New Item dialog box, select Installer Class. Accept the default name of Installer1.cs. Click Add. clip_image004.jpg
    4. Delete the default Class1.cs object from the SampleInstallLib project by expanding the Solution Explorer, right click on the Class1.cs object and select the Delete option.
    5. Then open Installer1.cs for editing. Adding references to:
      Microsoft.SqlServer.ConnectionInfo
      Microsoft.SqlServer.Smo
      System.Windows.Forms
      You will need the following namespaces for the code above to work:
      using System.Data.SqlClient;
      using System.IO;
      using System.Security.AccessControl;
      using System.Windows.Forms;
      using Microsoft.SqlServer.Management.Common;
      using Microsoft.SqlServer.Management.Smo;
      Add the next code:
      public void RestoreDatabase(String databaseName, String filePath, 
       String serverName, String userName, String password,
      String dataFilePath, String logFilePath)
      {
          Restore sqlRestore = new Restore();
      
          BackupDeviceItem deviceItem = new BackupDeviceItem
         (filePath, DeviceType.File);
          sqlRestore.Devices.Add(deviceItem);
          sqlRestore.Database = databaseName;
      
          ServerConnection connection;
          // for Windows Authentication
          if(userName == "")
          {
              SqlConnection sqlCon = new SqlConnection
       (@"Data Source="+serverName+@"; Integrated Security=True;");
              connection = new ServerConnection(sqlCon);    
          }
          // for Server Authentication
          else
          connection = new ServerConnection(serverName, userName, password);
          
          Server sqlServer = new Server(connection);
      
          Database db = sqlServer.Databases[databaseName];
          sqlRestore.Action = RestoreActionType.Database;
          String dataFileLocation = dataFilePath + databaseName + ".mdf";
          String logFileLocation = logFilePath + databaseName + "_Log.ldf";
          db = sqlServer.Databases[databaseName];
          RelocateFile rf = new RelocateFile(databaseName, dataFileLocation);
      
          sqlRestore.RelocateFiles.Add(new RelocateFile
         (databaseName, dataFileLocation));
          sqlRestore.RelocateFiles.Add(new RelocateFile
        (databaseName + "_log", logFileLocation));
          sqlRestore.ReplaceDatabase = true;
          sqlRestore.Complete += new ServerMessageEventHandler(sqlRestore_Complete);
          sqlRestore.PercentCompleteNotification = 10;
          sqlRestore.PercentComplete += new PercentCompleteEventHandler
          (sqlRestore_PercentComplete);
      
          try
          {
              sqlRestore.SqlRestore(sqlServer);
          }
          catch (Exception ex)
          {
              MessageBox.Show(ex.InnerException.ToString());
          }
      
      
          db = sqlServer.Databases[databaseName];
      
          db.SetOnline();
      
          sqlServer.Refresh();
      }
      Function RestoreDatabase is an open connection to a SQL Server and restores database from backup.
      As a parameter function gets:
      • databaseName – Name of database for which restore operations will be executed
      • filePath – a pass to a file
      • serverName – name of server
      • userName – name of user
      • password – user’s password
      • dataFilePath – a path that database file will have
      • logFilePath – a path that log file will have
      In case Windows Authentication is needed instead of user name, a blank line should be input, a password should be ignored in this case.
      Add next code for the function Commit:
      public override void Commit(System.Collections.IDictionary savedState)
      {
          // The code below changes the TARGETDIR permission 
          // for a Windows Services running under the 
          // NT AUTHORITY\NETWORK SERVICE account.
          try
          {
              DirectorySecurity dirSec = Directory.GetAccessControl
          (Context.Parameters["TargetDir"]);
              FileSystemAccessRule fsar = new FileSystemAccessRule
          (@"NT AUTHORITY\NETWORK SERVICE" 
                                            , FileSystemRights.FullControl
                                            , InheritanceFlags.ContainerInherit | 
           InheritanceFlags.ObjectInherit
                                            , PropagationFlags.None
                                            , AccessControlType.Allow);
              dirSec.AddAccessRule(fsar);
              Directory.SetAccessControl(Context.Parameters["TargetDir"], dirSec);
          }
          catch (Exception ex)
          {
              MessageBox.Show(ex.Message);
          }
      
          RestoreDatabase(Context.Parameters["databaseName"].ToString(),
      Context.Parameters["filePath"].ToString(),Context.Parameters
       ["serverName"].ToString(),Context.Parameters["userName"].ToString(),
      Context.Parameters["password"].ToString(), Context.Parameters
       ["dataFilePath"].ToString(), 
       Context.Parameters["logFilePath"].ToString());
              
          base.Commit(savedState);
      }
      This function code determines full access to directory setup (“TargetDir”) and all attached folders for user group “NT AUTHORITY\NETWORK SERVICE”. This will allow SQL Server to run Restore for our Database. In case of “NT AUTHORITY\NETWORK SERVICE” access is denied while decompression database the function SqlRestore cause the next exception:
      Restored failed for server '.\sqlexpress' (Microsoft.SqlServer.Express.Smo)
      
      Additional Information:
      System.Data.SqlClient.SqlError: The operating system returned the error '5
       (Access is denied)' while attempting
      'RestoreContainer::ValidateTargetForCreation' on '<path />\SampleDatabase_Log.ldf' 
       (Microsoft.SqlServer.Expres.Smo)
    6. Build SampleInstallLib.
  3. Start with Setup Project creation.
    1. In SampleInstallApp execute File -> Add -> New Project
      Select Other Projects -> Setup and Deployment, using the Setup Project template.
    2. Set application name “SampleInstall” and click Ok. clip_image005.jpg
    3. In Solution Explorer select newly created project, right click and select Properties. In appeared window click Prerequisites. In Prerequisites window, select checks at Windows Installer 3.1 and SQL Server 2005 Express Edition, and fix “Download prerequisites from the same location as my application” position. This will allow enable Windows Installer and SQL Server in installation pack, if there are no applications on target machine it will install them and then our application will be installed.
    4. In Solution Explorer, click “File system editor”. Appeared folder displays file system of the target machine. Let’s make copies of our project file “SampleApplication”, library file “SampleInstallLib.dll”, and backup of our database SampleDatabase.bak. Essential assembly will be automatically included in our project. Now create folder “Database”, the files of our database will be unpacked in it. To create the folder, right click “Application folder” and select Add-> Folder, name it. In the window properties, select AlwaysCreate in true. clip_image006.jpg
    5. Next create Project output. For that, choose Application Folder and right click select Add-> Project Output. In the window, Add Project Output Group select in Project: SampleInstallLib, then select Primary output and click OK. clip_image007.jpg
      Created Primary output will appear in file list.
    6. Then press a button in Solution Explorer and open Custom Actions Editor. And for all four actions, choose Primary output from SamplesInstallLib(Active). For that, right click for the necessary Action and select Add Custom Action. clip_image008.jpg
      As a result, we get the following:
      clip_image009.jpg
      The most recent move we pass parameter/argument list for action Commit. At installation stage, the variable IDictionary savedState (Commit function) gets transmission parameters at the moment of activation event Commit.
      public override void Commit(System.Collections.IDictionary savedState) 
      The parameters are shown in CustomActionData property. Parameters transfer syntax is paramName=”value”.
      For our task, the line is:
      /TargetDir="[TARGETDIR]\" /databaseName="SampleDatabase" 
      /filePath="[TARGETDIR]SampleDatabase.bak" /serverName=".\SQLEXPRESS"
       /userName="" /password="" /dataFilePath="[TARGETDIR]Database\\" 
      /logFilePath="[TARGETDIR]Database\\"
    7. Build the project SampleInstall, now we can execute installation, right click the project in Solution Explorer and select Install. Windows Installer will copy files in the specified folder. If it is copied successfully, the full access will be given to user’s group “NT AUTHORITY\NETWORK SERVICE” to installation directory and database will be unpacked in the Database directory.

References

PS

When you create your own installation packet, you can ignore step 2 and use Custom action instead that is in the archive attached with this article.

Comments

Popular posts from this blog

Creating package in Oracle Database using Toad For Oracle

What are Packages in Oracle Database A package is  a group   of procedures, functions,  variables   and  SQL statements   created as a single unit. It is used to store together related objects. A package has two parts, Package  Specification  and Package Body.

Resolving 'Setup Account Privileges' error while installing SQL Server

A new installation of Microsoft SQL Server 2012 or Microsoft SQL Server 2008 R2 fails You see the following error message when you try to install a new instance of SQL Server 2012 or SQL Server 2008 R2: Rule "Setup account privileges" failed.

Creating Oracle stored Procedures using TOAD for Oracle

In a database management system, a  stored procedure  is a set of Structured Query Language (SQL) statements with an assigned name that's stored in the database in compiled form so that it can be shared by a number of programs. The use of  stored procedures  can be helpful in controlling  access to data, preserving  data integrity  and  improving  productivity.