65.9K
CodeProject 正在变化。 阅读更多。
Home

应用程序部署期间恢复数据库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.33/5 (8投票s)

2008 年 10 月 15 日

CPOL

5分钟阅读

viewsIcon

68748

downloadIcon

4285

使用Windows Installer和Visual Studio安装项目在应用程序部署期间恢复数据库

引言

在.NET开发的应用程序中,数据存储通常使用MS SQL Server。在应用程序分发过程中,一个常见的问题是如何在目标计算机(您的项目将安装的PC或工作站)上安装数据库。

本文介绍的方法允许在应用程序安装阶段执行“还原数据库”操作。

应用程序和数据库开发

首先创建一个与数据库交互的应用程序。
创建一个名为“SampleDatabase”的示例数据库。
创建一个连接到已创建数据库并检索数据的示例应用程序。
(“SampleApplication”在随本文提供的文件中。)

创建安装程序

现在,我们来创建一个安装程序。

  1. 首先,我们需要在“SQL Server Management Studio Express”中为数据库创建备份,方法是选择所需的数据库并单击“Back Up…”

    clip_image001.jpg

    1. 然后选择路径和备份副本文件名。在本例中是“SampleDatabase.bak”。

      clip_image002.jpg

  2. 接下来,我们创建一个自定义操作。
    1. 虽然标准操作在大多数情况下足以执行安装,但自定义操作允许安装包的作者通过包含可执行文件、动态链接库和脚本来扩展标准操作的功能。
    2. 使用类库模板创建一个新项目,然后在“Name”框中键入“SampleInstallLib”,在“Solution Name”框中键入“SampleInstallApp”,然后单击OK。

      clip_image003.jpg

      项目已添加到解决方案资源管理器中。

    3. 在“Project”菜单上,选择“Add Class”,然后在“Add New Item”对话框中,选择“Installer Class”。接受默认名称Installer1.cs。单击Add。

      clip_image004.jpg

    4. 通过展开解决方案资源管理器,右键单击Class1.cs对象并选择“Delete”选项,从SampleInstallLib项目中删除默认的Class1.cs对象。
    5. 然后打开Installer1.cs进行编辑。

      添加引用

      Microsoft.SqlServer.ConnectionInfo
      Microsoft.SqlServer.Smo
      System.Windows.Forms

      您将需要以下命名空间才能使上面的代码正常工作

      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;

      添加以下代码

      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();
      }

      函数RestoreDatabase是一个打开与SQL Server的连接并从备份中还原数据库的函数。

      函数获取以下参数

      • databaseName – 将执行还原操作的数据库名称
      • filePath – 文件路径
      • serverName – 服务器名称
      • userName – 用户名
      • password – 用户密码
      • dataFilePath – 数据库文件将具有的路径
      • logFilePath – 日志文件将具有的路径

      如果需要Windows身份验证而不是用户名,则应输入一个空行,在这种情况下将忽略密码。

      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);
      }

      此函数代码确定对目录设置(“TargetDir”)和所有附加文件夹的完全访问权限,授予用户组“NT AUTHORITY\NETWORK SERVICE”。这将允许SQL Server为我们的数据库运行还原。如果数据库解压缩过程中“NT AUTHORITY\NETWORK SERVICE”的访问被拒绝,则SqlRestore函数会引发以下异常

      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 '\SampleDatabase_Log.ldf' 
      	(Microsoft.SqlServer.Expres.Smo)
    6. 生成SampleInstallLib
  3. 开始创建安装项目。
    1. SampleInstallApp中,执行File -> Add -> New Project
      选择Other Projects -> Setup and Deployment,使用Setup Project模板。
    2. 设置应用程序名称为“SampleInstall”并单击OK。

      clip_image005.jpg

    3. 在解决方案资源管理器中,选择新创建的项目,右键单击并选择“Properties”。在出现的窗口中单击“Prerequisites”。在“Prerequisites”窗口中,选择“Windows Installer 3.1”和“SQL Server 2005 Express Edition”的复选框,并固定“Download prerequisites from the same location as my application”选项。这将允许在安装包中启用Windows Installer和SQL Server。如果目标计算机上没有应用程序,它将安装它们,然后安装我们的应用程序。
    4. 在解决方案资源管理器中,单击“File system editor”。出现的文件夹显示目标计算机的文件系统。让我们复制我们的项目文件“SampleApplication”,库文件“SampleInstallLib.dll”,以及我们的数据库备份SampleDatabase.bak。必要的程序集将自动包含在我们的项目中。现在创建一个“Database”文件夹,我们的数据库文件将解压到其中。要创建文件夹,请右键单击“Application folder”并选择Add-> Folder,为其命名。在属性窗口中,选择AlwaysCreatetrue

      clip_image006.jpg

    5. 接下来创建项目输出。为此,选择Application Folder并右键单击选择Add-> Project Output。在“Add Project Output Group”窗口中,在“Project”中选择:SampleInstallLib,然后选择“Primary output”并单击OK。

      clip_image007.jpg

      创建的Primary output将出现在文件列表中。

    6. 然后按解决方案资源管理器中的按钮并打开“Custom Actions Editor”。对于所有四个操作,选择SamplesInstallLibActive)的Primary output。为此,右键单击所需的操作并选择Add Custom Action。

      clip_image008.jpg

      结果,我们得到以下内容

      clip_image009.jpg

      我们最后一步是为Commit操作传递参数/参数列表。在安装阶段,变量IDictionary savedStateCommit函数)会在Commit事件激活时接收传输的参数。

      public override void Commit(System.Collections.IDictionary savedState) 

      参数显示在CustomActionData属性中。参数传递语法为paramName=”value”

      对于我们的任务,该行是

      /TargetDir="[TARGETDIR]\" /databaseName="SampleDatabase" 
      /filePath="[TARGETDIR]SampleDatabase.bak" /serverName=".\SQLEXPRESS"
       /userName="" /password="" /dataFilePath="[TARGETDIR]Database\\" 
      /logFilePath="[TARGETDIR]Database\\"
    7. 构建SampleInstall项目,现在我们可以执行安装了。在解决方案资源管理器中右键单击项目并选择“Install”。Windows Installer会将文件复制到指定文件夹。如果复制成功,将授予用户组“NT AUTHORITY\NETWORK SERVICE”对安装目录的完全访问权限,数据库将解压到Database目录。

参考文献

PS

当您创建自己的安装包时,可以忽略步骤2,而是使用本文档附带的存档中的自定义操作。

历史

  • 2008年10月15日:初次发布
© . All rights reserved.