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

使用安装程序类轻松部署 SQL Server 数据库

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.91/5 (45投票s)

2005年4月6日

CPOL

3分钟阅读

viewsIcon

254549

downloadIcon

8799

使用 System.Configuration.Install 和 VS.NET 安装项目部署 MS SQL Server 数据库。

Sample Image - sqlscriptinstall.jpg

引言

如果您制作了一个使用 SQL Server 数据库的应用程序,该数据库需要位于客户端服务器上,那么 VS.NET 安装项目的帮助不大。您可以选择 InstallShild 或其他产品,这将使事情变得容易,但成本会更高。因此,为了寻找免费的解决方案,我在 MSDN 上找到了一篇关于使用 Installer 类和自定义操作来实现此目的的文章。该代码是用 VB.NET 编写的,因此本文将其移植到 C#,并添加了一些我发现有用的新功能。您需要做的就是创建一个派生自 System.Configuration.Install 的类,并将两个名为 *install.txt* & *uninstall.txt* 的嵌入式资源添加到解决方案中。 *install.txt* 将包含您的数据库的 SQL 脚本,而 *uninstall.txt* 将包含删除脚本。对于数据库脚本,我使用的是 Microsoft 团队为 ASP.NET InSQL 会话状态制作的 ASPstate 脚本

< sessionState
mode ="SQLServer"
stateConnectionString ="tcpip=127.0.0.1:42424"
sqlConnectionString ="data source=aleph;User ID=ASPsession;Password=ASPsession;"
cookieless ="false"
timeout ="60"
/>

为了使我的 Web 应用程序在客户端服务器上工作,我需要使用我的应用程序和 SQL 脚本制作一个 MSI。 为了在安装时运行脚本,我制作了一个名为 ScriptInstall 的 * .dll *,其代码如下所示。

Installer 类代码

首先,我们声明一个 string,它将具有默认值,并且可以被 Install 方法覆盖

string conStr="packet size=4096;integrated security=SSPI;"+
        "data source=\"(local)\";persist security info=False;"+
        "initial catalog=master";

我使用两个函数,它们将返回连接字符串和脚本内容

private static string GetScript(string name)
{
    Assembly asm = Assembly.GetExecutingAssembly();
    Stream str = asm.GetManifestResourceStream(
                    asm.GetName().Name+ "." + name);
    StreamReader reader = new StreamReader(str);
    return reader.ReadToEnd();
}
private static string GetLogin(string databaseServer,
                 string userName,string userPass,string database)
{
    return "server=" + databaseServer + 
     ";database="+database+";User ID=" + userName +
     ";Password=" + userPass;
}

然后我使用两个函数,它们将在 SQL Server 上运行 *install.txt* 和 *uninstall.txt*。 ExecuteSQL 具有一个 regex,它在 GO 之后拆分脚本,以便我可以使用 SQLCommand 逐个执行,我这样做是因为如果 SQL 脚本包含“GO”,ADO.NET 将抛出异常。

private static void ExecuteSql(SqlConnection sqlCon)
{
    string[] SqlLine;
    Regex regex = new Regex("^GO",RegexOptions.IgnoreCase | RegexOptions.Multiline);
    
    string txtSQL = GetScript("install.txt");
    SqlLine = regex.Split(txtSQL);

    SqlCommand cmd = sqlCon.CreateCommand();
    cmd.Connection = sqlCon;

    foreach(string line in SqlLine)
    {
        if(line.Length>0)
        {
            cmd.CommandText = line;
            cmd.CommandType = CommandType.Text;
            try
            {
                cmd.ExecuteNonQuery();
            }
            catch(SqlException)
            {
                //rollback
                ExecuteDrop(sqlCon);
                break;
            }
        }
    }
}
private static void ExecuteDrop(SqlConnection sqlCon)
{    
    if(sqlCon.State!=ConnectionState.Closed)sqlCon.Close();
    sqlCon.Open();
    SqlCommand cmd = sqlCon.CreateCommand();
    cmd.Connection = sqlCon;
    cmd.CommandText = GetScript("uninstall.txt");
    cmd.CommandType = CommandType.Text;
    cmd.ExecuteNonQuery();
    sqlCon.Close();
}

有了这些函数,我们现在可以覆盖 Install(IDictionary stateSaver)Uninstall(IDictionary savedState)。在 Install 方法中,除了在服务器上运行 SQL 脚本之外,我还保存用户提交的连接数据。以明文形式保存连接字符串是危险的,因此我使用 RijndaelManaged 对其进行加密。您也可以在源代码中找到该类。我保存连接字符串是因为卸载时需要它来删除数据库 ASPstate。

public override void Install(IDictionary stateSaver)
{
    base.Install (stateSaver);

    if(Context.Parameters["databaseServer"].Length>0 &&
        Context.Parameters["userName"].Length>0 &&
        Context.Parameters["userPass"].Length>0)
    {
        conStr = GetLogin(
            Context.Parameters["databaseServer"],
            Context.Parameters["userName"],
            Context.Parameters["userPass"],
            "master");

        RijndaelCryptography rijndael = new RijndaelCryptography();
        rijndael.GenKey();
        rijndael.Encrypt(conStr);
        //save information in the state-saver IDictionary
        //to be used in the Uninstall method
        stateSaver.Add("key",rijndael.Key);
        stateSaver.Add("IV",rijndael.IV);
        stateSaver.Add("conStr",rijndael.Encrypted);
    }

    SqlConnection sqlCon = new SqlConnection(conStr);

    sqlCon.Open();
    ExecuteSql(sqlCon);
    if(sqlCon.State!=ConnectionState.Closed)sqlCon.Close();
}

public override void Uninstall(IDictionary savedState)
{
    base.Uninstall (savedState);

    if(savedState.Contains("conStr"))
    {
        RijndaelCryptography rijndael = new RijndaelCryptography();
        rijndael.Key = (byte[])savedState["key"];
        rijndael.IV = (byte[])savedState["IV"];
        conStr = rijndael.Decrypt((byte[])savedState["conStr"]);            
    }

    SqlConnection sqlCon = new SqlConnection(conStr);

    ExecuteDrop(sqlCon);
}

设置项目

现在 Installer 类已完成,我可以创建一个安装项目并添加主要输出。在用户界面编辑器中,选择“安装”下的“开始”节点。在“操作”菜单上,选择“添加对话框”。在“添加对话框”对话框中,选择“文本框 (A)”对话框,然后单击“确定”以关闭对话框。在“操作”菜单上,选择“上移”。重复此操作,直到“文本框 (A)”对话框位于“安装文件夹”节点上方。像这样编辑“文本框 (A)”窗体的属性

Textboxes(A) propeties

转到自定义操作编辑器,并将主要输出添加到安装和卸载节点。单击安装节点上的主要输出并编辑属性。在 CustomActionData 中,键入以下内容

/databaseServer=[EDITA1] /userName=[EDITA2] /userPass=[EDITA3]

我使用 Context.ParametersInstaller 类中获取文本框的值。

conStr = GetLogin(
        Context.Parameters["databaseServer"],
        Context.Parameters["userName"],
        Context.Parameters["userPass"],
        "master");

现在一切都完成了。只需构建这两个项目,您就可以安装数据库了。我希望您会发现此代码有用,可以向代码中添加很多东西,例如更安全地存储 Rijndael 的密钥和 IV,或者在 ExecuteSql 事务中使用。期待您的评论。

历史

  • 版本 1.0
© . All rights reserved.