练习 - 将 ASP.NET 应用程序连接到 Azure SQL 数据库

已完成

已创建一个数据库。 现在你可以配置和部署一个 Web 应用,学术顾问可以用它与学生讨论课程和学习计划。 该应用使用 System.Data.SqlClient 库来检索和显示课程的详细信息,以及学生完成课程必须通过的模块。

为了节省时间,我们使用预先存在的 Web 应用程序,并演示如何添加将此应用连接到数据库的代码。 下图显示了此应用程序的主要组件:

High-level view of the application structure.

要配置 Web 应用程序,请执行以下操作:

  • 创建一个类,其中包含数据库中每个模块的课程名称、模块标题和顺序。
  • 创建一个数据访问控制器类,用于从数据库中检索信息。
  • 编辑 Web 应用程序中索引页面背后的代码来创建数据访问控制器对象并获取数据。
  • 编辑索引页以显示数据。

部署并运行现成的 Web 应用

  1. 将工作目录更改为 education 文件夹。

    cd ~/education
    
  2. 运行以下命令以构建和部署初始 Web 应用程序。

    WEBAPPNAME=educationapp-$RANDOM
    az webapp up \
        --resource-group <rgn>[Sandbox resource group]</rgn> \
        --location centralus \
        --sku F1 \
        --name $WEBAPPNAME
    
  3. 部署 Web 应用程序后,输出会显示带有网站 URL 的 App_url。 在新标签页中打开此网站。

    The education web app running. Currently, no data is displayed.

    你希望 Web 应用显示课程列表和组成每个课程的模块。 目前,该应用不会检索或显示此数据。 因此,需要更新代码,以便从数据库中获取数据并显示数据。

向 Web 应用添加代码以检索数据

现在向应用程序添加代码以检索数据库中的课程数据。

  1. 在 Cloud Shell 中,转到 education/Models 文件夹。

    cd ~/education/Models
    

    此文件夹包含两个文件:CoursesAndModules.csDataAccessController.cs

  2. 使用代码编辑器打开 CoursesAndModules.cs 文件。

    code CoursesAndModules.cs
    

    此文件包含一个名为 CoursesAndModules 的空类。

    namespace CoursesWebApp.Models
    {
        public class CoursesAndModules
        {
            // TODO: Define the CourseName, ModuleTitle, and Sequence read-only properties
    
            // TODO: Create a constructor that initializes the fields behind the properties
        }
    }
    
  3. 使用以下代码替换 // TODO: Define the CourseName, ModuleTitle, and Sequence read-only properties 注释。

    public string CourseName { get; }
    public string ModuleTitle { get; }
    public int Sequence { get; }
    

    此代码定义了一组只读字段,它们包含 Web 应用显示的每一行的数据。

  4. 使用以下构造函数替换 // TODO: Create a constructor that initializes the fields behind the properties 注释。

    public CoursesAndModules(string courseName, string moduleTitle, int sequence)
    {
        this.CourseName = courseName;
        this.ModuleTitle = moduleTitle;
        this.Sequence = sequence;
    }
    

    此构造函数使用要显示的数据填充字段。 完整文件应包含以下代码。

    namespace CoursesWebApp.Models
    {
        public class CoursesAndModules
        {
            public string CourseName { get; }
            public string ModuleTitle { get; }
            public int Sequence { get; }
    
            public CoursesAndModules(string courseName, string moduleTitle, int sequence)
            {
                this.CourseName = courseName;
                this.ModuleTitle = moduleTitle;
                this.Sequence = sequence;
            }
        }
    }
    
  5. 按 Ctrl+S 保存文件,并按 Ctrl+Q 关闭代码编辑器。

  6. 使用代码编辑器打开 DataAccessController.cs 文件。

    code DataAccessController.cs
    

    此文件包含一个名为 DataAccessController 的类。 此类包含用于连接数据库和检索课程及模块数据的数据访问逻辑。 它使用此数据来填充 CoursesAndModules 对象的列表。

    using Microsoft.Extensions.Options;
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace CoursesWebApp.Models
    {
        public class DataAccessController
        {
            // TODO: Add your connection string in the following statements
            private string connectionString = "<Azure SQL Database Connection String>";
    
            // Retrieve all details of courses and their modules
            public IEnumerable<CoursesAndModules> GetAllCoursesAndModules()
            {
                List<CoursesAndModules> courseList = new List<CoursesAndModules>();
    
                // TODO: Connect to the database
                //using ()
                {
                    // TODO: Specify the Transact-SQL query to run
    
                    // TODO: Execute the query
    
                    // TODO: Read the data a row at a time
    
                    // TODO: Close the database connection
                }
                return courseList;
            }
        }
    }
    
  7. 使代码编辑器保持打开状态,然后切换到 Azure 门户。

  8. 在 Azure 门户菜单上,选择“SQL 数据库”,然后选择你的数据库。 此时将显示“coursedatabaseNNN”的“SQL 数据库”。

  9. 在左侧菜单窗格的“设置”下,选择“连接字符串”。 将 ADO.NET 连接字符串复制到剪贴板。

    The connection string pane in the Azure portal.

  10. 返回到代码编辑器。 使用剪贴板中的值替换 connectionString 变量的值。 在连接字符串中,使用值 azuresql 替换文本 User ID。 将文本 {your_password} 替换为此帐户的密码。

    private string connectionString = "Server=tcp:courseservernnn.database.windows.net,1433;Initial Catalog=coursedatabasennn;Persist Security Info=False;User ID=azuresql;Password=<password>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
    
  11. 在注释 //TODO: Connect to the database 之后,使用以下代码替换注释掉的 using 语句。

    using (SqlConnection con = new SqlConnection(connectionString))
    

    此代码创建一个新的 SqlConnection 对象,它使用连接字符串连接到数据库。

  12. 使用下面的语句替换 // TODO: Specify the Transact-SQL query to run 注释。

    SqlCommand cmd = new SqlCommand(
        @"SELECT c.CourseName, m.ModuleTitle, s.ModuleSequence
        FROM dbo.Courses c JOIN dbo.StudyPlans s
        ON c.CourseID = s.CourseID
        JOIN dbo.Modules m
        ON m.ModuleCode = s.ModuleCode
        ORDER BY c.CourseName, s.ModuleSequence", con);
    cmd.CommandType = CommandType.Text;
    

    SqlCommand 对象包含一个 Transact-SQL (T-SQL) 语句,该语句检索所有课程和模块的数据。 它使用 dbo.StudyPlan 表中的信息将它们联结起来。

  13. 使用以下代码替换 // TODO: Execute the query 注释。

    con.Open();
    SqlDataReader rdr = cmd.ExecuteReader();
    

    这些语句开启与数据库的连接并运行 T-SQL 语句。 可以使用 SqlDataReader 对象一次提取一行结果。

  14. 使用下面的代码块替换 // TODO: Read the data a row at a time 注释。

    while (rdr.Read())
    {
        string courseName = rdr["CourseName"].ToString();
        string moduleTitle = rdr["ModuleTitle"].ToString();
        int moduleSequence = Convert.ToInt32(rdr["ModuleSequence"]);
        CoursesAndModules course = new CoursesAndModules(courseName, moduleTitle, moduleSequence);
        courseList.Add(course);
    }
    

    此块循环访问 SqlDataReader 对象中返回的行。 代码提取每行中的字段中的数据,并使用它们填充新 CoursesAndModules 对象。 然后将此对象添加到列表中。

  15. 使用下面的语句替换 // TODO: Close the database connection 注释。

    con.Close();
    

    此语句关闭与数据库的连接并释放持有的资源。

    完成的类应包含以下代码,该代码包含数据库的连接字符串。

    using Microsoft.Extensions.Options;
    using System;
    using System.Collections.Generic;
    using System.Data;
    using System.Data.SqlClient;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace CoursesWebApp.Models
    {
        public class DataAccessController
        {
            // Add your connection string in the following statements
            private string connectionString = "Server=tcp:courseserver101.database.windows.net,1433;Initial Catalog=coursedatabase101;Persist Security Info=False;User ID=azuresql;Password=<password>;MultipleActiveResultSets=False;Encrypt=True;TrustServerCertificate=False;Connection Timeout=30;";
    
            // Retrieve all details of courses and their modules    
            public IEnumerable<CoursesAndModules> GetAllCoursesAndModules()
            {
                List<CoursesAndModules> courseList = new List<CoursesAndModules>();
    
                // Connect to the database
                using (SqlConnection con = new SqlConnection(connectionString))
                {
                    // Specify the Transact-SQL query to run
                    SqlCommand cmd = new SqlCommand(
                        @"SELECT c.CourseName, m.ModuleTitle, s.ModuleSequence
                        FROM dbo.Courses c JOIN dbo.StudyPlans s
                        ON c.CourseID = s.CourseID
                        JOIN dbo.Modules m
                        ON m.ModuleCode = s.ModuleCode
                        ORDER BY c.CourseName, s.ModuleSequence", con);
                    cmd.CommandType = CommandType.Text;
    
                    // Execute the query
                    con.Open();
                    SqlDataReader rdr = cmd.ExecuteReader();
    
                    // Read the data a row at a time
                    while (rdr.Read())
                    {
                        string courseName = rdr["CourseName"].ToString();
                        string moduleTitle = rdr["ModuleTitle"].ToString();
                        int moduleSequence = Convert.ToInt32(rdr["ModuleSequence"]);
                        CoursesAndModules course = new CoursesAndModules(courseName, moduleTitle, moduleSequence);
                        courseList.Add(course);
                    }
    
                    // Close the database connection
                    con.Close();
                }
                return courseList;
            }
        }
    }
    
  16. 保存文件并关闭“代码”编辑器。

向 Web 应用添加代码以显示数据

现在,应用程序可以检索课程数据。 此时,请更新应用程序以向用户显示数据。

  1. 在 Cloud Shell 中,转到 education/Pages 文件夹。

    cd ~/education/Pages
    

    此文件夹包含 Web 应用用于显示信息的 .cshtml 页面和代码文件。

  2. 使用代码编辑器打开 Index.cshtml.cs 文件。

    code Index.cshtml.cs
    

    此文件包含索引页显示时运行的代码。 该代码定义了一个 CoursesAndModulesModel 类。 索引页采用这种模式来显示课程和模块的细节。 在此文件中,需要添加使用 DataAccessController 对象来提取该数据的代码。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using CoursesWebApp.Models;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    
    namespace CoursesWebApp.Pages
    {
        public class CoursesAndModulesModel : PageModel
        {
            // TODO: Create a DataAccessController object
    
            // TODO: Create a collection for holding CoursesAndModules object
    
            public void OnGet()
            {
                // TODO: Retrieve the data using the DataAccessController object and populate the CoursesAndModules object
            }
        }
    }
    
  3. Index.cshtml.cs 中,用以下代码替换注释 // TODO: Create a DataAccessController object 以创建新的 DataAccessController 对象。

    DataAccessController dac = new DataAccessController();
    
  4. 使用以下代码替换 // TODO: Create a collection for holding CoursesAndModules object 注释。

    public List<CoursesAndModules> CoursesAndModules;
    
  5. OnGet 方法中,使用以下代码替换 // TODO: Retrieve the data using the DataAccessController object and populate the CoursesAndModules object 注释。 此代码利用 DataAccessController 对象并用数据库中的数据填充列表。

    CoursesAndModules = dac.GetAllCoursesAndModules().ToList();
    

    完成的文件应包含以下代码。

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    using CoursesWebApp.Models;
    using Microsoft.AspNetCore.Mvc;
    using Microsoft.AspNetCore.Mvc.RazorPages;
    
    namespace CoursesWebApp.Pages
    {
        public class CoursesAndModulesModel : PageModel
        {
            // Create a DataAccessController object
            DataAccessController dac = new DataAccessController();
    
            // Create a collection for holding CoursesAndModules object
            public List<CoursesAndModules> CoursesAndModules;
    
            public void OnGet()
            {
                // Retrieve the data using the DataAccessController object and populate the CoursesAndModules object
                CoursesAndModules = dac.GetAllCoursesAndModules().ToList();
            }
        }
    }
    
  6. 保存文件并关闭代码编辑器。

  7. 使用代码编辑器打开 Index.cshtml 文件。

    code Index.cshtml
    

    此文件包含索引页的显示逻辑。 它指定 CoursesAndModulesModel 作为数据源。 已添加的代码会创建并填充此模型。

    页面使用 HTML 数据显示模型中的数据。 目前,该页面仅显示表标题。 但表正文 (<tbody>) 为空。

    <h2>Courses and Modules</h2>
    <div>
        <table class="table">
            <thead>
                <tr>
                    <th>
                        Course Name
                    </th>
                    <th>
                        Modules
                    </th>
                    <th>
                        Sequence
                    </th>
                </tr>
            </thead>
            <tbody>
                <!-- TODO: Display the data from the CoursesAndModules collection -->
            </tbody>
        </table>
    </div>
    
  8. 使用以下标记替换 <!-- TODO: Display the data from the CoursesAndModules collection --\> 注释。

    @foreach(var courseAndModule in Model.CoursesAndModules)
    {
    <tr>
        <td>
            @Html.DisplayFor(courseName => courseAndModule.CourseName)
        </td>
        <td>
            @Html.DisplayFor(moduleTitle => courseAndModule.ModuleTitle)
        </td>
        <td>
            @Html.DisplayFor(sequence => courseAndModule.Sequence)
        </td>
    </tr>
    }
    

    此代码遍历模型中的行,并在每个字段中输出数据。

    完成的 Index.cshtml 文件应包含以下代码。

    @page
    @model CoursesAndModulesModel
    @{
        ViewData["Title"] = "Home page";
    }
    
    <h2>Courses and Modules</h2>
    <div>
        <table class="table">
            <thead>
                <tr>
                    <th>
                        Course Name
                    </th>
                    <th>
                        Modules
                    </th>
                    <th>
                        Sequence
                    </th>
                </tr>
            </thead>
            <tbody>
                @foreach(var courseAndModule in Model.CoursesAndModules)
                {
                <tr>
                    <td>
                        @Html.DisplayFor(courseName => courseAndModule.CourseName)
                    </td>
                    <td>
                        @Html.DisplayFor(moduleTitle => courseAndModule.ModuleTitle)
                    </td>
                    <td>
                        @Html.DisplayFor(sequence => courseAndModule.Sequence)
                    </td>
                </tr>
                }
            </tbody>
        </table>
    </div>
    
  9. 保存文件并关闭代码编辑器。

部署和测试更新的 Web 应用

完整配置应用程序以检索课程数据并向用户显示后,可部署更新的版本。

  1. 在 Cloud Shell 中,返回 education 文件夹。

    cd ~/education
    
  2. 运行以下命令以生成和部署更新的 Web 应用。

    az webapp up \
        --resource-group <rgn>[Sandbox resource group]</rgn> \
        --name $WEBAPPNAME
    
  3. 部署新的 Web 应用后,选择应用的链接。 现在应会显示一个课程和模块列表,其中包含存储在数据库中的数据。

    Screenshot of the education web app running, showing the data.