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

动态连接字符串

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.88/5 (30投票s)

2014 年 4 月 6 日

CPOL

7分钟阅读

viewsIcon

89275

downloadIcon

3032

您想在运行时自定义数据库连接字符串吗?方法如下。

引言

这是一个“通过示例展示”的解决方案,用于动态定义数据库连接字符串。在这个示例中,我使用了 Visual Studio 2010、Microsoft SQL Server 和 C#,但其他组合也是可能的。

Microsoft 建议您提供用户凭据,以便应用程序连接到数据库。另一种方法是提供 SQL Server 授权,但这部分信息将保存在您的代码或“*.config”文件中(需要加密以隐藏敏感数据等等……)。如果您想在开发阶段使用自己的凭据,而发布产品则通过用户干预获取值,这可能吗?是的,本文将告诉您如何实现!

我相信这个示例很简单,任何对 Visual Studio 和 C# 有基本了解的人都能轻松理解和操作。

从这里开始

首先在 Visual Studio 2010 中创建一个新的 C# 项目。将项目命名为 `ConnectionStringSample`。

然后我们将数据源添加到应用程序。在我们的示例应用程序中,我们将使用 SQL Server 的 `NorthWind` 示例数据库,但对于任何其他数据库,步骤都将相同。只需在“数据源”窗口中点击“**添加新数据源**”。

这将带我们进入“**数据源配置向导**”。按照下面的图片选择并继续

步骤 1

第二步

**步骤 3** 有更多细节,所以我们第一次点击“**新建连接**”按钮,然后会弹出“**添加连接**”窗口

在这里我们将设置我们的 SQL Server、数据库和必要的凭据。顶部的第一个按钮([数据源:] **更改**)是选择服务器类型的入口。点击它,我们会看到以下窗口

所有可用的 **.NET Framework 数据提供程序** 都可与我们的示例一起使用!

最后,如果所有选择和设置都正确完成,我们将看到与**步骤 3** 中所示外观类似的窗口。点击下一步,我们得到

**步骤 4**。现在,这个名字我们应该记住一段时间,所以简单记下它!

**步骤 5**。我们几乎完成了这个虚拟过程。我们只选择 `Products` 数据表作为示例,然后点击“完成”按钮。现在,“**数据源**”窗口应该看起来像这样

我们现在选择表单设计器窗口,然后在“**数据源**”窗口中选择“**Products**”字样。我们将“**Products**”拖放到我们的表单上,然后会得到类似下一张图片的内容

这是 Visual Studio 的奇迹之一,或者说如何在不编写一行代码的情况下创建具有数据管理功能的应用程序!是的,这是真的,这是一个完全可用的应用程序!

现在我们来处理连接字符串。“**解决方案资源管理器**”现在显示如下所示

双击 `Settings.settings` 对象,会得到以下图像

在这里,我们将从连接字符串中删除几乎所有信息(敏感或不敏感),使其内容最简化,如“`Value`”列所示。现在保存更改,并在 `Settings.settings` 编辑器的工具栏上,点击“**查看代码**”。这将创建一个名为 *Settings.cs* 的新文件。

这是一个非常敏感且关键的文件。它就像整个项目的基础!设计器中打开的每个表单都使用此文件。如果此文件不正确,您将无法编辑您的表单!目前,请勿更改它!

现在让我们从 Visual Studio 自动生成的代码中获取一些信息。我们将搜索自动生成的代码中的连接字符串。我们之前简要记下了“数据源向导”使用的连接字符串变量,现在是时候使用它了。在 *NorthwndDataSet.Designer.cs* 中搜索字符串“`NorthwndConnectionString`”。

[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Data.Design.TypedDataSetGenerator", "4.0.0.0")]
private void InitConnection()
 {
 this._connection = new global::System.Data.SqlClient.SqlConnection();
 this._connection.ConnectionString
  = global::ConnectionStringSample.Properties.Settings.Default.NorthwndConnectionString;
 }

我们可能会在多个地方找到它,但代码中显示的那一个正是我们想要的。将光标放在找到的字符串“`NorthwndConnectionString`”上并按下 **F12**(上下文 -> 转到定义),将带我们到文件 *Settings.Designer.cs*,在那里我们可以看到类似以下内容

[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.SpecialSettingAttribute( global::System.Configuration.SpecialSetting.ConnectionString )]
[global::System.Configuration.DefaultSettingValueAttribute( "Persist Security Info=True" )]
public string NorthwndConnectionString
  {
  get
     {
     return ( ( string ) ( this["NorthwndConnectionString"] ) );
     }
  }

现在我们有点进展了!我们的连接字符串 (`NorthwndConnectionString`) 是一个只读属性!好吧,在一个属性的后面,总是(几乎?)有一个变量。我们的变量是什么?我们需要的变量是

this["NorthwndConnectionString"]

太好了,我们有了它,我们想把它放在一个我们可以操作它的地方!这个地方就是文件 *Settings.cs*。将代码放在 *Settings.cs* 的构造函数中会很有效,但它必须是自给自足的,也就是说,不能使用其他类或函数中的代码,主要只能是使用常量值初始化变量。所以我们需要一些其他东西来帮助我们实现我们做出的“动态”承诺。查看类顶部的注释 [来自文件 *Settings.cs*,**internal sealed partial class Settings**],我们可以看到我们可以使用一些事件,编写我们的事件处理程序。所有事件中最“方便”的似乎是 `SettingsLoaded` 事件。所以我们在空构造函数的底部添加一行 ;-),以引入我们的事件处理程序。

this.SettingsLoaded += [press TAB]

Visual Studio 自动化甚至会为我们完成大部分打字工作。将代码写到等号,然后按两次 TAB 键。事件和事件处理程序都已输入!

现在,为了实现本文的目的,再做一点设计。我们需要另一个表单,让用户输入他/她的敏感数据,就像下一张图片中的那样

这样,用户或负责安装的人员将能够设置重要数据,让应用程序连接到数据库服务器。当然,这些数据必须保存在某个地方(某种 .ini 文件,或注册表等)。

我们还需要一种方法将连接数据从一个表单传递到另一个表单。因此,我们添加一个 `public` 和 `static` 新类来承载我们的数据,如下所示

using System;

namespace ConnectionStringSample
   {
   public static class GlobalData
      {
      public static String strConnectionUserName = "sa";
      public static String strConnectionPassword = "123456";
      public static String strConnectionServerName = "localhost";
      public static String strConnectionDatabaseName = "Northwnd";
      }
   }

这个文件我们称之为 *GlobalData.cs*。您应该注意到,我们为所有变量设置了默认值,即使它们是无效值。

现在是关键文件 *Settings.cs*。完整文件如下

namespace ConnectionStringSample.Properties
   {

   // This class allows you to handle specific events on the settings class:
   //  The SettingChanging event is raised before a setting's value is changed.
   //  The PropertyChanged event is raised after a setting's value is changed.
   //  The SettingsLoaded event is raised after the setting values are loaded.
   //  The SettingsSaving event is raised before the setting values are saved.
   internal sealed partial class Settings
      {
      public Settings()
         {
         // // To add event handlers for saving and changing settings, uncomment the lines below:
         // this.SettingChanging += this.SettingChangingEventHandler;
         // this.SettingsSaving += this.SettingsSavingEventHandler;

         this.SettingsLoaded += new System.Configuration.SettingsLoadedEventHandler( Settings_SettingsLoaded );
         }

      void Settings_SettingsLoaded( object sender, System.Configuration.SettingsLoadedEventArgs e )
         {
         this["NorthwndConnectionString"] = "Data Source=" + GlobalData.strConnectionServerName + ";"
                                          + "Initial Catalog=" + GlobalData.strConnectionDatabaseName + ";"
                                          + "Persist Security Info=True;"
                                          + "User ID=" + GlobalData.strConnectionUserName + ";"
                                          + "Password=" + GlobalData.strConnectionPassword + ";";
         }

      private void SettingChangingEventHandler( object sender, System.Configuration.SettingChangingEventArgs e )
         {
         // Add code to handle the SettingChangingEvent event here.
         }

      private void SettingsSavingEventHandler( object sender, System.ComponentModel.CancelEventArgs e )
         {
         // Add code to handle the SettingsSaving event here.
         }
      }
   }

重要的新行是这些粗体字。这将完成任务!

为了完整起见,我们还需要另一个表单作为调度器。一开始,我们将调用 `ConnectionData` 表单(*frmConnectionData.cs* - 前面显示的 `frmConnect` 表单),当此表单关闭时,它将调用允许用户浏览和编辑数据库数据的表单(*frmDataGridEditor.cs*,以前是 *Form1.cs*)。

此时,我们的解决方案如下图所示

为了完成上述文本的后续要点,进行了一些小的更改,例如在 *Program.cs* 中启动表单以及在 *frmMain.cs* 中添加了一些代码以切换表单。完整的项目(源代码)可供下载(适用于 Visual Studio 2010 的 C# 和 VB.NET)。

如果用户输入的连接数据无效,他/她将得到一个空网格。

结论

尽管“**数据源配置向导**”是一个为程序员提供高级数据库管理能力的强大工具,但仍然需要更进一步,那就是“**连接字符串**”以及如何在运行时更改/更新它。强大的 `**Settings**` 类和相关的源文件 *Settings.cs* 可以帮助我们解决这个问题。我们还查看了“**数据源配置向导**”自动生成的代码。通过这种方式,我们发现了连接字符串内部使用的变量。上述两者的结合就是我们正在寻找的解决方案。

历史

  • 20140524:添加了结论段落,并提供了包含源代码的单个压缩文件供下载(包括 VB.NET 和 C#),因此当使用 CodeProject 的“**浏览代码**”功能时,两个项目将同时可用。
  • 20140408:添加了 VB.NET 源代码供下载
  • 20140406:第一版(包括 C# 代码供下载)
© . All rights reserved.