第三章 使用数据库
使用EF Core可以简化数据库的操作,但是它无法让开发人员晓得它是如何工作的,以及应用程序的操作是如何转换成SQl命令的,特别是在出现意外结果时。在本章中,我将向你展示Visual Studio提供的用于检查数据库的工具,以及如何执行不同类型的SQL命令。使用EF Core虽然不要求你是SQL方面的专家,但当你没有得到期望的结果时,掌握SQL知识会很有帮助的。
选择数据库产品及其Provider包
在选择数据库服务器时很难出错,因为所有可用的选择都是好产品。不管你是喜欢商业产品还是开源产品,也不管你是想运行自己的服务器还是使用云
如果您已经有了一个数据库服务器,可能是因为公司购买标准,或者是网站经营许可的原因,反正就是你要使用的数据库服务器。EF Core消除了数据库服务器之间的差异,所以你使用哪个数据库服务并不重要。除非你有特定需求,否则不应该与公司的技术标准对抗,
如果您还没有数据库服务器,那么就有很多不错的选择。Microsoft SQL Server是我最常用的数据库服务。因为它有广泛的价格方案,包括为开发人员和小型项目提供的零成本选择,以及Azure云上的托管版本。对于本书中的示例,我使用零配置的SQL server开发人员版本——即LocalDB。
如果您喜欢开源,那么MySQL是一个很好的选择,尽管它归Oracle所有,Oracle商业化和开源产品兼而有之。MariaDB是MySQL项目的一个分支,不属于Oracle,但旨在与MySQL兼容。还有许多供应商提供MySQL或其衍生品的托管/云服务器,包括Amazon Web服务和Microsoft Azure。
一旦选择了数据库服务器,就可以选择与EF Core一起使用的数据库Provider程序。微软在https://docs.microsoft.com/en-us/ef/core/providers维护了最流行数据库的Provider列表。大多数Provider程序包都是免费的,但是也有一些商业化产品可供使用。如果你要用Oracle数据库(这个是商业产品,而不是刚才说的MySQL),那么你将需要使用第三方产品,因为Oracle没有为EF Core提供Provider包。
3.1 准备本章
在本章中,我继续使用在第2章中创建的Partylnvites工程。为准备本章,打开一个新的PowerShell窗口或命令行,导航到partyinvite工程目录(包含bower.json的目录)。并运行清单3-1所示的命令。这些命令删除和重建应用程序使用的数据库,这将有助于确保您获得本章示例的预期结果。
Listing 3-1. Resetting the Example Application Database
dotnet ef database drop --force
dotnet ef database update
将清单3-2所示的配置语句添加到appsettings.json文件中。这些语句禁用了除EF Core之外其它所有. net包的日志记录,这将使您更容易理解示例。
Listing 3-2. Configuring Logging Messages in the appsettings.json File in the PartyInvites Folder { "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=PartyInvites" }, "Logging": { "LogLevel": { "Default": "None", "Microsoft.EntityFrameworkCore": "Information" } } }
这个日志配置将让您看到EF Core生成的消息,这些消息显示发送到数据库的SQL命令,并防止它们在其他消息流中丢失。在工程目录中执行dotnet run命令启动应用程序,并使用浏览器窗口导航到http://localhost:5000。单击RSVP Now按钮,使用表3-1所示的值创建四个RSVP响应。

添加响应之后,导航到http://localhost:5000/home /listresponses,您将看到如图3-1所示的列表。

3.2 探索数据库
Visual Studio包含一组工具,允许您探索所使用的数据库。从Visual Studio工具菜单选择连接到数据库,将显示如图3-2所示的对话框,该对话框允许您选择要连接的数据库服务器类型。

在本书中,所有示例都使用LocalDB。LocalDB是Microsoft SQL Server的一个版本,在第2章中作为Visual Studio workload的一部分进行安装的,它被设计为专供开发人员使用。它实现了SQL Server所有关键功能。但是它不需要配置,也不需要够买授权。从Visual Studio提供的选项列表中选择Microsoft SOL Server并单击Continue按钮。
下一个对话框指定要使用的数据库服务器和详细信息,如图3-3所示。在服务器名称中输入(localdb)\MSSOLLocalDB,并为“选择或输入数据库名称”字段选择Partyinvite,如图3-3所示

小贴士: 连接LocalDB所需的字符串可能会引起混淆。第一部分是用括号括起来的localdb。然后是单个\字符,后面跟着MSSQLLocalDB,但是没有下划线,即:(LocalDB)\MSSOLLocalDB。但是当您在appsetting.json配置文件中指定数据库连接字符串时,需要一个额外的\字符。因为反斜杠需要被转义一下。也就是配置文件中要写成:(localdb)\\MSSQLLocalDB。
单击OK按钮,Visual Studio将连接到数据库服务,并在“服务器资源管理器”窗口中显示详细信息。(如果窗口未打开,请在菜单栏选择“视图>服务器资源管理器”)。在Data Connections部分,你将看到Partvinvite条目,您可以展开该条目查看数据库的详细信息,如图3-4所示。

3.2.1 检查数据库表
如果展开Tables,将看到两个条目。_EFMigrationsHistory表用于跟踪已应用到数据库的migrations,以使其与应用程序的数据模型保持同步。我将在第13章中详细解释migrations迁移是如何工作的,这个表并不在本章讲述。Responses表用于存储应用程序的GuestResponse对象。在Server Explorer中展开此表,您可以看到表中的列(字段)信息,如图3-5所示。

你能看到应用程序怎样用EF Core创建存储了GuestResponse对象的表的细节。表名取自上下文类中的属性名,列(字段)的名称对应于GuestResponse实体类定义的属性。这些决定是EF Core默认遵循的约定,我将在第2部分中解释如何重写该约定。
.NET数据类型必须能转换为数据库支持的类型。EF Core创建数据库时会决策出应该使用什么数据库类型。您可以右击Server Explorer窗口中的Response表,从弹出菜单中选择Open Table Definition来检查它的决策。Visual Studio会新打开一个包含表结构细节的编辑器面板,如图3-6所示。

您可以看到GuestResponse实体类的每个属性对应什么SQL数据类型。EF Core依赖数据库Provider来选择最恰当的SQL数据类型,并且可以因库而异。对于SQL Server,您可以看到Id属性将被存储为SQL的bigint,即对应于.NET的long。而WillAttend属性将被存储为bit,对应于.NET的可空布尔类型(即bool?)。其他属性将被存储为nvarchar (MAX),它对应于. net的string。
小贴士:对于大多数项目,EF Core决策选择的数据类型是完全可以接受的,但是您可以使用第21章中描述的特性来指定其他类型。
3.2.2 检查数据库上下文
右键单击Server Explorer窗口中的表,并从弹出菜单中选择Show table Data,以查看表中的数据。Visual Studio将查询数据库并显示结果,如图3-7所示

Visual Studio用来显示数据的表格是可编辑的,这意味着您可以更新现有的数据值或向表中添加新行,而不需要写SQL语句。
将表3-2中所示的内容输入到表格底部行的各字段中,然后按Tab键添加新行。您无法为Id字段输入值,因为当数据被添加到数据库时是由数据库自动分配一个值的。

按Tab键时,Visual Studio将向数据库添加新数据。浏览器导航到http://localhost:5000/home/listresponses,可以看到应用程序中多了新数据,如图3-8所示。

3.3 理解SQL
理解EF Core如何把应用程序操作转换为数据库命令很有用。虽然使用EF Core不要求你是SQL方面的专家,但是了解基础知识会很有帮助,当你没有获得预期执行结果的时候你能分析出来发生了什么。在下面的小节中,我将描述基本的SQL命令,并解释EF Core如何使用这些命令。
3.3.1查询数据
SQL的基本功能是查询,它从数据库检索数据。如果您导航到http://localhost:5000/home /listresponses并检查应用程序日志,您将能够看到SQL命令,EF Core用它们从数据库获取数据,如下所示:

如果您熟悉LINQ,您看了就能明白这些SQL命令的作用,但是为了理解Entity Framework Core在做什么,有必要进一步研究一下。
3.3.1.1手动查询数据库
右击表并从弹出菜单中选择New query,可以使用Server Explorer窗口直接查询数据库。在使用SQL Server时,我更喜欢使用Visual Studio的另一个功能,即从工具>SOL Server菜单中选择New Ouery。当您看到如图3-9所示的Connect对话框窗口时,在服务器名称中输入(localdb)\MSSOLLocalDB,将数据库名称保留为<default>,然后单击Connect按钮。

小贴士:连接到数据库后,连接设置将保存在Connect对话框窗口的History中,将来可以用于连接无需重新输入详细信息。单击Connect按钮,Visual Studio将连接到数据库服务器并打开一个新的编辑面板。请将清单3-3所示的SQL命令输入编辑器。
1 --Listing 3-3. A Basic SQL Query 2 USE PartyInvites 3 SELECT * FROM Responses
单击编辑器窗格左上角的绿色箭头,或右击窗口然后从弹出菜单中选择Execute来执行命令。Visual Studio将把SOL发送到数据库服务器执行并显示结果,如图3-10所示。

因为这是你直接执行的第一个SOL命令,所以我将分解它的每个部分并解释它的含义。这是第一部分:
![]()
该命令选中Partyinvite数据库。数据库服务器可以管理许多数据库,选择要使用的数据库非常重要。(在创建连接或使用查询窗口顶部的下拉列表时,可以选择数据库,但我更喜欢在SQL语句中显式地进行选择)。清单3-3中的SQL的第二部分是实际的查询,如下所示:
![]()
SELECT关键字表示SQL查询。FROM关键字指定所需数据的表名。星号指定查询应该返回表中所有列的值。这个查询告诉数据库服务器: “将Response表中的所有列都给我”。该查询生成表3-3所示的结果

3.3.1.2 过滤数据(where)
默认情况下,查询将返回表中的所有行。对于存储少量数据的应用程序(例如示例项目)来说,这是完全合理的,但是在大多数实际项目中,你一般只会希望查询数据的子集。WHERE关键字用于从数据库表中选择特定的行。输入SQL如清单3-4所示,进入Visual Studio查询窗口,查看如何工作。
结构化查询语言约定
您将注意到本章中,SQL关键字使用大写字母。这不是必需的,但是这样做是一种常见的实践,它可以帮助使复杂的SQL更容易阅读。SQL的世界充满了约定和偏好——就像c#开发一样——您将不可避免地遇到坚持某种风格的数据库管理员和开发人员。数据库服务器擅长解析SQL,我的建议是采用您和您的团队认为易于阅读的风格,即使这意味着编写的命令倾向于c#编码约定,而不是与SQL相关的约定。
1 --Listing 3-4. Querying for Selected Rows 2 USE PartyInvites 3 SELECT * FROM Responses 4 WHERE WillAttend = 1
WHERE关键字后面跟着一个表达式,该表达式将匹配应该包含在结果中的行。在本例中,我已经指定将WillAttend值为1的行包含在结果中,并过滤掉其他行,执行此命令,您将看到匹配的Response,如表3-4所示

3.3.1.3 选择和排序列
查询中的星号(*字符)会使结果集中包含所有列。一般你不会总是需要查询所有列,还有你可能会希望在结果集中把列排个序。SQL语句可以选择查询哪些列,如清单3-5所示。
--Listing 3-5. Selecting and Ordering Columns USE PartyInvites SELECT Id, Name, Email FROM Responses WHERE WillAttend = 'true'
在这个SQL中,我指定只查询Id. Name和Email列的值。执行查询时,您将看到表3-5所示的结果。其他列的值被排除在外。请注意,结果是按照我指定的列的顺序显示的

3.3.1.4 行排序
对结果集行排序可以使用关键字Order By指定。在清单3-6中,我使用ORDER BY子句告诉数据库服务器,我希望按Email列的值进行行排序。
1 --Listing 3-6. Ordering Results 2 USE PartyInvites 3 SELECT Id, Name, Email FROM Responses 4 WHERE WillAttend = 'true' 5 ORDER BY Email
执行查询时,结果将按Email列的值排序,如表3-6所示

3.3.1.5 检查EF Core查询
有许多SQL查询功能,但是前几节中所示的功能足以让我们了解从数据库请求数据。EF Core生成的查询中有一些细微的差异值得解释。在清单3-7中,我更改了Home控制器的ListResponses Action方法中使用的LINQ查询,使其对所请求的数据更具选择性。
1 //Listing 3-7. Changing the LINQ Query in the HomeController.cs File in the Controllers Folder 2 using Microsoft.AspNetCore.Mvc; 3 using PartyInvites.Models; 4 using System.Linq; 5 namespace PartyInvites.Controllers 6 { 7 public class HomeController : Controller 8 { 9 private DataContext context; 10 public HomeController(DataContext ctx) => context = ctx; 11 public IActionResult Index() => View(); 12 public IActionResult Respond() => View(); 13 [HttpPost] 14 public IActionResult Respond(GuestResponse response) 15 { 16 context.Responses.Add(response); 17 context.SaveChanges(); 18 return RedirectToAction(nameof(Thanks), 19 new { Name = response.Name, WillAttend = response.WillAttend }); 20 } 21 public IActionResult Thanks(GuestResponse response) => View(response); 22 23 public IActionResult ListResponses() => 24 View(context.Responses 25 .Where(r => r.WillAttend == true) 26 .OrderBy(r => r.Email)); 27 } 28 }
在project目录下执行dotnet run命令启动应用程序,并导航到http://localhost:5000/home /listresponses。显示给用户的HTML只包含参加聚会的人的答复,按照他们的电子邮件地址排序,如图3-11所示。

如果你检查应用程序的日志,将看到EF Core根据LINQ查询生成的SQL语句。

您可以看到查询的结构与我在前几节中构建的模式相同。使用方括号([和]字符)允许列名包含特殊字符,比如空格。对于我示例中的数据模型,原本不需要加方括号(我示例中名称就没用特殊字符)。但是EF Core无论如何都会这样做,以保证不出问题。AS关键字用于创建别名。EF Core使用AS关键字和r来引用Response表。别名在一个简单的查询中体现不出什么好处,但在连接多个表的复杂查询中会很有用的。
3.3.1.6 理解查询参数
在检查EF Core创建的查询时,您可能会看到另一个差异。为了演示,我更改了ListResponses Action方法使用的查询,如清单3-8所示。
1 //Listing 3-8. Changing the LINQ Query in the HomeController.cs File in the Controllers Folder 2 using Microsoft.AspNetCore.Mvc; 3 using PartyInvites.Models; 4 using System.Linq; 5 namespace PartyInvites.Controllers 6 { 7 public class HomeController : Controller 8 { 9 private DataContext context; 10 public HomeController(DataContext ctx) => context = ctx; 11 public IActionResult Index() => View(); 12 public IActionResult Respond() => View(); 13 [HttpPost] 14 public IActionResult Respond(GuestResponse response) 15 { 16 context.Responses.Add(response); 17 context.SaveChanges(); 18 return RedirectToAction(nameof(Thanks), 19 new { Name = response.Name, WillAttend = response.WillAttend }); 20 } 21 public IActionResult Thanks(GuestResponse response) => View(response); 22 public IActionResult ListResponses(string searchTerm = "555-123-5678") => 23 View(context.Responses 24 .Where(r => r.Phone == searchTerm) 25 .OrderBy(r => r.Email)); 26 } 27 }
LINQ查询使用Where方法筛选GuestResponses对象,即它的电话值匹配搜索参数。如果启动应用程序,导航到http://localhost:5000/home /listresponses,并检查应用程序生成的日志,你将看到如何将LINQ查询转换为SQL语句

上面@字符用于标识SQL语句中的参数。当EF Core处理变量时会使用参数,并且这些参数允许数据库服务识别类似的查询,并通过相同的方式处理它们来提高性能。参数化查询也可以防止SQL注入攻击,即用户输入的一个数据值被当做SQL语句的一部分解释执行了。
使用参数化查询是个好主意——特别是因为Entity Framework Core不知道是否可以信任数据值的来源——但是它确实会令生成的SQL语句更难以理解。因为默认情况下,参数的值不会显示在日志消息中。这在大多数情况下并不是问题,因为调试时关注点应该是SQL语句的结构,但是在清单3-9中,我更改了示例应用程序的配置文件,以便EF Core将参数值包含在日志中。
小心:不要在生产环境中启用此选项,因为敏感数据最终会出现在应用程序的日志文件中。
1 //Listing 3-9. Enabling Parameter Value Logging in the Startup.cs File in the PartyInvites Folder 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Threading.Tasks; 6 using Microsoft.AspNetCore.Builder; 7 using Microsoft.AspNetCore.Hosting; 8 using Microsoft.AspNetCore.Http; 9 using Microsoft.Extensions.DependencyInjection; 10 using Microsoft.EntityFrameworkCore; 11 using Microsoft.Extensions.Configuration; 12 using PartyInvites.Models; 13 namespace PartyInvites 14 { 15 public class Startup 16 { 17 public Startup(IConfiguration config) => Configuration = config; 18 public IConfiguration Configuration { get; } 19 public void ConfigureServices(IServiceCollection services) 20 { 21 services.AddMvc(); 22 string conString = Configuration["ConnectionStrings:DefaultConnection"]; 23 services.AddDbContext<DataContext>(options => 24 { 25 options.EnableSensitiveDataLogging(true); 26 options.UseSqlServer(conString); 27 }); 28 } 29 30 public void Configure(IApplicationBuilder app, IHostingEnvironment env) 31 { 32 app.UseDeveloperExceptionPage(); 33 app.UseStatusCodePages(); 34 app.UseStaticFiles(); 35 app.UseMvcWithDefaultRoute(); 36 } 37 } 38 }
调用EnableSensitiveDatalogging方法告诉EF Core在日志中包含SQL参数值。使用dotnet run命令重新启动应用程序,并导航到http:// localhost: 5000/home/listresponses。在应用程序生成的日志中,EF Core将在查询之前立即显示的消息中包含SQL参数值。

3.3.2 存储和更新数据
在大多数应用程序中,查询是最常见的命令类型,但是存储新数据和更新已存在数据也很重要。INSERT命令用于向表中插入新数据行,清单3-10所示的SQL命令将新行添加到Response表中。
1 //Listing 3-10. Inserting Data into a Table 2 USE PartyInvites 3 INSERT INTO Responses(Name, Email, Phone, WillAttend) 4 VALUES ('Joe Dobbs', 'joe@example.com', '555-888-1234', 1)
INSERT INTO关键字之后是要为其提供值的列的列表。然后使用VALUES关键字,然后是需要存储在数据库中的值,这些值按照与列列表相同的顺序表示。清单中的命令添加了一个新行,存入Name、Email、Phone、WillAttend列的值。Id列不能指定值,EF Core将Id列配置为数据库服务在插入记录时自动生成值
小贴士:注意,字符串在SQL中使用单引号( ' 字符)来标记,而不是c#使用的双引号( " 字符)。当您执行清单3-10中的命令时,您将看到以下消息:

数据库服务通过指示更改了多少行来响应修改数据库的命令。要查看EF Core使用的INSERT命令,请导航到http:// localhost: 5000,单击RSVP Now按钮,并使用表3-7中的值创建答复。

如果检查应用程序在数据库中存储新数据时生成的日志,你将会看到使用了相同基本风格的INSERT命令,但是有一些不同。

SET NOCOUNT ON命令禁用反馈受影响的行数。它对像本章这样的简单命令影响不大,但是对于更复杂的操作,它可以提高性能。您可以看到Entity Framework Core使用的INSERT命令类似于清单3-10中的命令,但是具有参数化的值。
插入之后,Entity Framework Core立即向数据库服务器发送另一个命令。如下所示:

Entity Framework Core使用此命令检查受INSERT命令影响的行数,并确定数据库服务分配给Id列的惟一值。获取新插入数据的Id值对于执行后续操作非常重要,即使在示例应用程序中没有啥后续操作,EF Core也会执行该查询。
更新现有的数据
UPDATE命令用于修改数据库中已存在的数据,并可用于在一次操作中选择修改多行。清单3-11所示的SQL命令修改Responses表中所有WillAttend值为1的行,并更改它们的Phone值。(这本身不是个特别有用的更改,但它确实演示了UPDATE的基本结构)。
1 --Listing 3-11. Updating Existing Data 2 USE PartyInvites 3 UPDATE Responses 4 SET Phone='404-204-1234' 5 WHERE WillAttend = 1
UPDATE关键字后面跟着表名,SET关键字指定要更改的列和值。在WHERE后面跟着一个选择要修改的行的表达式。当您执行此命令时,数据库服务器将反馈受影响的行数。响。
![]()
执行清单3-12所示的命令来查询响应表中的所有行。
1 --Listing 3-12. Querying for All Rows 2 USE PartyInvites 3 SELECT * FROM Responses
表3-8显示了这个查询的结果,并突出显示了清单3-11中执行的更新的效果。

3.3.3 删除数据
最后一个命令是从数据库中删除数据的命令:DELETE命令。必须小心使用这个命令,因为它有一个WHERE子句,允许一条命令选择要删除的多行记录,而且很容易意外地从数据库中删除超出预期的数据。该命令如清单3-13所示,将从Responses表中删除所有WillAttend值为0的行。
1 --Listing 3-13. Deleting Data 2 USE PartyInvites 3 DELETE FROM Responses 4 WHERE WillAttend = 0
执行此命令,然后重复执行清单3-12中的查询,以确认已从数据库中删除了被拒绝的邀请。示例应用程序中不支持删除数据,但是您将在后面的章节中看到DELETE命令,包括我在下一章开始构建的SportsStore应用程序的一部分。
3.4 连接数据
数据库可以包含表之间的关系,EF Core用该关系跟踪对象之间的关系。这个功能是基于本章前面描述的INSERT和UPDATE命令构建的,但是依赖于一种更复杂类型的查询,称为Join(连接),它合并来自多个表的数据。Join会令人困惑,您用上了EF Core就不需要再理解它们,但是具备基本的知识还是有用的。
注意:如果您没有立即搞懂本节,请不要担心。一旦你读完第14章EF Core使用Join的功能,你就找到了感觉。该功能还将在第15和16章中进行深入描述。
3.4.1 准备数据库
为了演示Join连接是如何工作的,我需要向数据库中添加另一个表。执行清单3-14所示的命令,该命令将创建一个名为Preferences的表。
1 --Listing 3-14. Creating a New Table in the Database 2 USE PartyInvites 3 DROP TABLE IF EXISTS Preferences 4 CREATE TABLE Preferences ( 5 Id bigint IDENTITY, 6 Email nvarchar(max), 7 NutAllergy bit, 8 Teetotal bit, 9 ResponseId bigint, 10 )
DROP TABLE命令告诉数据库服务删除Preferences表(如果它已经存在),这意味着您可以重复执行清单3-14所示的命令,而不会导致错误。
CREATE TABLE命令用于创建一个新表。指定了表名,以及表将包含的列的列表和每个列的类型。清单3-14中的命令创建的表拥有Id、Email、NutAllergy和Teetotal列。Id列已经配置了IDENTITY关键字,这使得数据库服务负责创建唯一值(而且是非空的),并将之作为主键。清单3-15所示的命令将新行插入Responses表和Preferences表。
1 --Listing 3-15. Storing Related Data 2 USE PartyInvites 3 INSERT INTO Responses(Name, Email, Phone, WillAttend) 4 VALUES ('Dave Habbs', 'dave@example.com', '555-777-1234', 1) 5 INSERT INTO Preferences (Email, NutAllergy, Teetotal) 6 VALUES ('dave@example.com', 0, 1)
第一个INSERT命令向Responses表添加一行新记录。第二个INSERT命令向Preferences表添加一行新记录。执行清单3-16所示的命令查询这些表并查看新数据。
1 --Listing 3-16. Querying for the New Data 2 USE PartyInvites 3 SELECT * FROM Responses 4 SELECT * FROM Preferences
第一个SELECT命令查询数据库中Responses表的所有行,并包括表3-9中显示的行,表3-9是由清单3-15中的第一个INSERT命令创建的。

第二个SELECT命令查询数据库中Preferences表的所有行,并生成表3-10所示的结果。注意,Preferences表中的行的ResponseId属性与表3-9中所示的行的Id值相一致(外键-主键关系)。

执行一个连接
连接有不同的类型(即内连接、外连接,外连接又分左连接、右连接和全连接),但是EF Core使用的是内连接(inner join),两个表中都有的列值才选择该行。清单3-17包含一个查询,该查询对Responses和Preferences表执行内连接。
1 --Listing 3-17. Performing an Inner Join 2 USE PartyInvites 3 SELECT Responses.Email, Responses.Name, Preferences.NutAllergy, Preferences.Teetotal 4 FROM Responses 5 INNER JOIN Preferences ON Responses.Email = Preferences.Email
SELECT命令要求数据库服务从两张表中选择列,INNER JOIN关键字用于指定行记录的筛选条件即:Responses表中的行应该与Preferences表中的行具有相同的Email值。结果是,清单3-16中创建的两行数据值合并成一个结果,如表3-11所示。

这是一个简单的连接,但它展示了基本的机制,并将帮助您理解EF Core如何从存储复杂数据模型的数据库中获取数据。
3.5 本章小结
在本章中,我向您展示了如何使用Visual Studio检查用于存储应用程序数据的数据库。我还向您展示了如何执行基本的SQL命令,以便您了解EF Core如何使用数据库,您可以检查是否得到了预期的结果。在下一章中,我将开始创建一个更复杂、更实际的应用程序:SportsStore。
浙公网安备 33010602011771号