设计 Azure SQL 数据库,并使用 C# 和 ADO.NET 进行连接

Azure SQL 数据库是 Azure 中的关系数据库即服务 (DBaaS)。 本教程介绍如何将 Azure 门户、ADO.NET 与 Visual Studio 结合使用来完成以下操作:

  • 在 Azure 门户中创建数据库
  • 在 Azure 门户中设置服务器级防火墙规则
  • 使用 ADO.NET 和 Visual Studi 连接至数据库
  • 使用 ADO.NET 创建表
  • 使用 ADO.NET 插入、更新和删除数据
  • 查询数据 ADO.NET

如果还没有 Azure 订阅,可以在开始前创建一个免费帐户

先决条件

已安装 Visual Studio Community 2017、Visual Studio Professional 2017 或 Visual Studio Enterprise 2017

登录到 Azure 门户

登录到 Azure 门户

创建空的 SQL 数据库

创建 Azure SQL 数据库时,会使用定义好的一组计算和存储资源。 数据库在 Azure 资源组Azure SQL 数据库逻辑服务器中创建。

按照以下步骤创建空的 SQL 数据库。

  1. 在 Azure 门户的左上角单击“创建资源”。

  2. 从“新建”页中选择“数据库”,然后从“新建”页的“SQL 数据库”中选择“创建”。

    创建空数据库

  3. 如上图所示,在“SQL 数据库”窗体中填写以下信息:

    设置       建议的值 说明 
    数据库名称 mySampleDatabase 有关有效的数据库名称,请参阅 Database Identifiers(数据库标识符)。
    订阅 你的订阅 有关订阅的详细信息,请参阅订阅
    资源组 MyResourceGroup 有关有效的资源组名称,请参阅 Naming rules and restrictions(命名规则和限制)。
    选择源 空白数据库 指定应创建空白数据库。
  4. 单击“服务器”,为新数据库创建并配置新服务器。 使用以下信息填写“新建服务器”窗体:

    设置       建议的值 说明 
    服务器名称 任何全局唯一名称 如需有效的服务器名称,请参阅 Naming rules and restrictions(命名规则和限制)。
    服务器管理员登录名 任何有效的名称 有关有效的登录名,请参阅 Database Identifiers(数据库标识符)。
    密码 任何有效的密码 密码必须至少有 8 个字符,且必须包含以下类别中的三个类别的字符:大写字符、小写字符、数字以及非字母数字字符。
    位置 任何有效的位置 中国东部、中国东部 2、中国北部、中国北部 2

    创建数据库 - 服务器

  5. 单击“选择”。

  6. 单击“定价层”,指定服务层、DTU 数和存储量。 浏览相关选项,了解适用于每个服务层的 DTU 数和存储量。

  7. 对于本教程,请选择“标准”服务层,然后使用滑块选择“100 DTU (S3)”和“400”GB 存储。

    创建数据库 - s1

  8. 若要使用“附加存储”选项,请接受预览版条款。

    Important

    * 超出所包括存储量的存储大小为预览版,需额外付费。 有关详细信息,请参阅 SQL 数据库定价

  9. 选择服务器层、DTU 数和存储量后,单击“应用”。

  10. 选择空白数据库的“排序规则”(就本教程来说,请使用默认值)。 有关排序规则的详细信息,请参阅 Collations(排序规则)

  11. 单击“创建”预配数据库。 大约需要一分半的时间才能完成预配。

  12. 在工具栏上,单击“通知”可监视部署过程。

    通知

创建服务器级防火墙规则

SQL 数据库服务在服务器级别创建一个防火墙。除非创建了防火墙规则来为特定的 IP 地址打开防火墙,否则会阻止外部应用程序和工具连接到服务器或服务器上的任何数据库。 按照以下步骤为客户端 IP 地址创建 SQL 数据库服务器级防火墙规则,并只允许通过针对你的 IP 地址打开的 SQL 数据库防火墙建立外部连接。

Note

通过端口 1433 进行的 SQL 数据库通信。 如果尝试从企业网络内部进行连接,则该网络的防火墙可能不允许经端口 1433 的出站流量。 如果是这样,则无法连接到 Azure SQL 数据库服务器,除非 IT 部门打开了端口 1433。

  1. 部署完成后,在左侧菜单中单击“SQL 数据库”,然后在“SQL 数据库”页上单击“mySampleDatabase”。 此时会打开数据库的概览页,显示完全限定的服务器名称(例如 mynewserver20170824.database.chinacloudapi.cn),并且会提供进行进一步配置所需的选项。

  2. 在后续的快速入门中,请复制此完全限定的服务器名称,将其用于连接到服务器及其数据库。

    服务器名称

  3. 单击工具栏上的“设置服务器防火墙”。 此时会打开 SQL 数据库服务器的“防火墙设置”页。

    服务器防火墙规则

  4. 在工具栏上单击“添加客户端 IP”,将当前的 IP 地址添加到新的防火墙规则。 防火墙规则可以针对单个 IP 地址或一系列 IP 地址打开端口 1433。

  5. 单击“保存”。 此时会针对当前的 IP 地址创建服务器级防火墙规则,在逻辑服务器上打开 端口 1433。

  6. 单击“确定”,然后关闭“防火墙设置”页。

现在可以使用之前创建的服务器管理员帐户通过 SQL Server Management Studio 或其他所选工具从此 IP 地址连接到 SQL 数据库服务器及其数据库。

Important

默认情况下,所有 Azure 服务都允许通过 SQL 数据库防火墙进行访问。 在此页上单击“关”即可对所有 Azure 服务执行禁用操作。

SQL Server 连接信息

在 Azure 门户中获取 Azure SQL 数据库服务器的完全限定服务器名称。 请使用 SQL Server Management Studio 通过完全限定的服务器名称连接到服务器。

  1. 登录到 Azure 门户
  2. 从左侧菜单中选择“SQL 数据库”,并单击“SQL 数据库”页上的数据库。
  3. 在数据库的“Azure 门户”页的“概要”窗格中,找到并复制“服务器名称”。

    连接信息

C# 程序示例

本文的后续部分介绍了一个 C# 程序,该程序使用 ADO.NET 将 Transact-SQL 语句发送到 SQL 数据库。 该 C# 程序执行以下操作:

  1. 使用 ADO.NET 连接到 SQL 数据库
  2. 创建表
  3. 通过发出 T-SQL INSERT 语句为表填充数据
  4. 使用联接来更新数据
  5. 使用联接来删除数据
  6. 使用联接来选择数据行
  7. 关闭连接(这样会从 tempdb 中删除任何临时表)。

C# 程序包含:

  • 连接到数据库所需的 C# 代码。
  • 返回 T-SQL 源代码的方法。
  • 两个将 T-SQL 提交到数据库的方法。

编译和运行

此 C# 程序在逻辑上是一个 .cs 文件。 但这里将此程序以物理方式划分成多个代码块,使每个块更易于查看和理解。 若要编译和运行此程序,请执行以下操作:

  1. 在 Visual Studio 中创建 C# 项目。
    • 项目类型应该是“控制台”应用程序,源自如下所示的层次结构:“模板”>“Visual C#”>“Windows 经典桌面”>“控制台应用(.NET Framework)”。
  2. Program.cs 文件中,擦除小的起始代码行。
  3. 将下述每个块按此处所示顺序复制并粘贴到 Program.cs 中。
  4. 在 Program.cs 的 Main 方法中编辑以下值:

    • cb.DataSource
    • cd.UserID
    • cb.Password
    • InitialCatalog
  5. 验证是否已引用程序集 System.Data.dll。 若要进行验证,请在“解决方案资源管理器”窗格中展开“引用”节点。

  6. 若要在 Visual Studio 中生成程序,请单击“生成”菜单。
  7. 若要在 Visual Studio 中运行程序,请单击“启动”按钮。 报表输出显示在 cmd.exe 窗口中。

Note

可以选择编辑 T-SQL,以便向表名添加前导 #,从而在 tempdb 中以临时表的形式创建这些表。 在没有测试数据库可用时,可以通过这种方法进行演示。 关闭连接时,会自动删除临时表。 就临时表来说,不会强制外键的任何 REFERENCE。

C# 块 1:通过 ADO.NET 进行连接

using System;
using System.Data.SqlClient;   // System.Data.dll 
//using System.Data;           // For:  SqlDbType , ParameterDirection

namespace csharp_db_test
{
   class Program
   {
      static void Main(string[] args)
      {
         try
         {
            var cb = new SqlConnectionStringBuilder();
            cb.DataSource = "your_server.database.chinacloudapi.cn";
            cb.UserID = "your_user";
            cb.Password = "your_password";
            cb.InitialCatalog = "your_database";

            using (var connection = new SqlConnection(cb.ConnectionString))
            {
               connection.Open();

               Submit_Tsql_NonQuery(connection, "2 - Create-Tables",
                  Build_2_Tsql_CreateTables());

               Submit_Tsql_NonQuery(connection, "3 - Inserts",
                  Build_3_Tsql_Inserts());

               Submit_Tsql_NonQuery(connection, "4 - Update-Join",
                  Build_4_Tsql_UpdateJoin(),
                  "@csharpParmDepartmentName", "Accounting");

               Submit_Tsql_NonQuery(connection, "5 - Delete-Join",
                  Build_5_Tsql_DeleteJoin(),
                  "@csharpParmDepartmentName", "Legal");

               Submit_6_Tsql_SelectEmployees(connection);
            }
         }
         catch (SqlException e)
         {
            Console.WriteLine(e.ToString());
         }
         Console.WriteLine("View the report output here, then press any key to end the program...");
         Console.ReadKey();
      }
      }

}

C# 块 2:用于创建表的 T-SQL

      static string Build_2_Tsql_CreateTables()
      {
         return @"
DROP TABLE IF EXISTS tabEmployee;
DROP TABLE IF EXISTS tabDepartment;  -- Drop parent table last.


CREATE TABLE tabDepartment
(
   DepartmentCode  nchar(4)          not null
      PRIMARY KEY,
   DepartmentName  nvarchar(128)     not null
);

CREATE TABLE tabEmployee
(
   EmployeeGuid    uniqueIdentifier  not null  default NewId()
      PRIMARY KEY,
   EmployeeName    nvarchar(128)     not null,
   EmployeeLevel   int               not null,
   DepartmentCode  nchar(4)              null
      REFERENCES tabDepartment (DepartmentCode)  -- (REFERENCES would be disallowed on temporary tables.)
);
";
      }

实体关系图 (ERD)

前述 CREATE TABLE 语句涉及 REFERENCES 关键字,该关键字用于在两个表之间创建外键 (FK) 关系。 如果使用 tempdb,请通过一对前导短划线注释掉 --REFERENCES 关键字。

接下来是一个 ERD,显示了这两个表之间的关系。 #tabEmployee.DepartmentCode 子列中的值仅限 #tabDepartment.Department 父列中存在的值。

ERD,显示外键

C# 块 3:用于插入数据的 T-SQL

      static string Build_3_Tsql_Inserts()
      {
         return @"
-- The company has these departments.
INSERT INTO tabDepartment
   (DepartmentCode, DepartmentName)
      VALUES
   ('acct', 'Accounting'),
   ('hres', 'Human Resources'),
   ('legl', 'Legal');

-- The company has these employees, each in one department.
INSERT INTO tabEmployee
   (EmployeeName, EmployeeLevel, DepartmentCode)
      VALUES
   ('Alison'  , 19, 'acct'),
   ('Barbara' , 17, 'hres'),
   ('Carol'   , 21, 'acct'),
   ('Deborah' , 24, 'legl'),
   ('Elle'    , 15, null);
";
      }

C# 块 4:用于更新-联接的 T-SQL

      static string Build_4_Tsql_UpdateJoin()
      {
         return @"
DECLARE @DName1  nvarchar(128) = @csharpParmDepartmentName;  --'Accounting';


-- Promote everyone in one department (see @parm...).
UPDATE empl
   SET
      empl.EmployeeLevel += 1
   FROM
      tabEmployee   as empl
      INNER JOIN
      tabDepartment as dept ON dept.DepartmentCode = empl.DepartmentCode
   WHERE
      dept.DepartmentName = @DName1;
";
      }

C# 块 5:用于删除-联接的 T-SQL

      static string Build_5_Tsql_DeleteJoin()
      {
         return @"
DECLARE @DName2  nvarchar(128);
SET @DName2 = @csharpParmDepartmentName;  --'Legal';


-- Right size the Legal department.
DELETE empl
   FROM
      tabEmployee   as empl
      INNER JOIN
      tabDepartment as dept ON dept.DepartmentCode = empl.DepartmentCode
   WHERE
      dept.DepartmentName = @DName2

-- Disband the Legal department.
DELETE tabDepartment
   WHERE DepartmentName = @DName2;
";
      }

C# 块 6:用于选择行的 T-SQL

      static string Build_6_Tsql_SelectEmployees()
      {
         return @"
-- Look at all the final Employees.
SELECT
      empl.EmployeeGuid,
      empl.EmployeeName,
      empl.EmployeeLevel,
      empl.DepartmentCode,
      dept.DepartmentName
   FROM
      tabEmployee   as empl
      LEFT OUTER JOIN
      tabDepartment as dept ON dept.DepartmentCode = empl.DepartmentCode
   ORDER BY
      EmployeeName;
";
      }

C# 块 6b:ExecuteReader

此方法旨在运行 T-SQL SELECT 语句,该语句通过 Build_6_Tsql_SelectEmployees 方法生成。

      static void Submit_6_Tsql_SelectEmployees(SqlConnection connection)
      {
         Console.WriteLine();
         Console.WriteLine("=================================");
         Console.WriteLine("Now, SelectEmployees (6)...");

         string tsql = Build_6_Tsql_SelectEmployees();

         using (var command = new SqlCommand(tsql, connection))
         {
            using (SqlDataReader reader = command.ExecuteReader())
            {
               while (reader.Read())
               {
                  Console.WriteLine("{0} , {1} , {2} , {3} , {4}",
                     reader.GetGuid(0),
                     reader.GetString(1),
                     reader.GetInt32(2),
                     (reader.IsDBNull(3)) ? "NULL" : reader.GetString(3),
                     (reader.IsDBNull(4)) ? "NULL" : reader.GetString(4));
               }
            }
         }
      }

C# 块 7:ExecuteNonQuery

如果操作需修改表的数据内容而不返回任何数据行,则会调用此方法。

      static void Submit_Tsql_NonQuery(
         SqlConnection connection,
         string tsqlPurpose,
         string tsqlSourceCode,
         string parameterName = null,
         string parameterValue = null
         )
      {
         Console.WriteLine();
         Console.WriteLine("=================================");
         Console.WriteLine("T-SQL to {0}...", tsqlPurpose);

         using (var command = new SqlCommand(tsqlSourceCode, connection))
         {
            if (parameterName != null)
            {
               command.Parameters.AddWithValue(  // Or, use SqlParameter class.
                  parameterName,
                  parameterValue);
            }
            int rowsAffected = command.ExecuteNonQuery();
            Console.WriteLine(rowsAffected + " = rows affected.");
         }
      }
   } // EndOfClass
}

C# 块 8:控制台的实际测试输出

此部分捕获程序发送到控制台的输出。 多个测试运行仅 GUID 值存在差异。

[C:\csharp_db_test\csharp_db_test\bin\Debug\]
>> csharp_db_test.exe

=================================
Now, CreateTables (10)...

=================================
Now, Inserts (20)...

=================================
Now, UpdateJoin (30)...
2 rows affected, by UpdateJoin.

=================================
Now, DeleteJoin (40)...

=================================
Now, SelectEmployees (50)...
0199be49-a2ed-4e35-94b7-e936acf1cd75 , Alison , 20 , acct , Accounting
f0d3d147-64cf-4420-b9f9-76e6e0a32567 , Barbara , 17 , hres , Human Resources
cf4caede-e237-42d2-b61d-72114c7e3afa , Carol , 22 , acct , Accounting
cdde7727-bcfd-4f72-a665-87199c415f8b , Elle , 15 , NULL , NULL

[C:\csharp_db_test\csharp_db_test\bin\Debug\]
>>

后续步骤

本教程介绍了基本数据库任务,例如创建数据库和表、负载和查询数据,以及将数据库还原到以前的时间点。 你已了解如何:

  • 创建数据库
  • 设置防火墙规则
  • 使用 Visual Studio 和 C# 连接至数据库
  • 创建表
  • 插入、更新和删除数据
  • 查询数据

请转到下一教程,了解数据迁移。