六、在代码中使用实体框架与数据库交互

第三章理解 MVC中,我们在构建功能性 ASP 的同时提到了实体框架(EF).NET Core MVC web 应用。 在本章中,我们将深入了解 EF Core 1.0,并介绍在开发过程中你需要了解的关于 EF Core 的知识,也就是 EF7。

值得注意的是,EF Core 可以与不止 ASP 一起使用.NET Core 和关系数据库。 事实上,它也被设计用于与环球 Windows 平台(UWP)应用在 Windows 上 10 和 Windows 桌面应用(Windows Presentation Foundation (WPF)或【显示】WinForms)。 除了 SQL Server, EF Core 还可以与Azure Table StoragePostgreSQLSQLite,甚至NoSQL数据库一起使用。****

****为了本书的目的,我们将主要关注 ASP.NET Core、SQL Server 和新的内存提供程序。

.NET 中的对象关系映射

如果您以前使用过 EF 或任何对象关系映射器(ORM),那么您可以略读本节并继续下一节。 但是,如果您是 ORM 框架的新手,那么本节是为您准备的。

像 EF 这样的 ORM 使您可以很容易地在应用代码中与数据库进行交互。 下面的截图说明了 ORM:

Object-relational mapping in .NET

为什么使用 ORM?

在没有任何 ORM 的情况下构建一个功能齐全的 web 应用是完全可行的。 你可以直接使用 ADO.NET 或原始 SQL 从应用代码与数据库对话。 您可以编写 SQL 代码来创建数据库对象和关系。 您可以通过大量使用存储过程和函数来将所有数据库逻辑保存在数据库中,或者您可以使用像 EF 这样的 ORM 并利用Code First方法。

虽然数据库管理员可能会嘲笑 ORM 的使用,但开发人员应该有信心,他们将获得更多的权力和控制如何创建、操纵和维护数据库实体。 以下是你可以考虑的利弊清单。

使用 ORM 的优点:

  • 公共代码库:您可以在代码中定义所有实体,而不是在应用代码库之外断开数据库创建代码
  • 编译时的好处:如果你在模型代码中犯了某些错误,编译器会捕获它们
  • 更容易的更新:如果你需要更新/重命名模型中的任何东西,你的 IDE 会让它变得轻而易举
  • 无忧约束:您可以使用属性和流畅的代码轻松设置键、关系和其他约束
  • 可管理的维护:在迁移的帮助下,您和您的团队可以根据需要升级/降级您的数据库,并跟踪修订。

使用 ORM 的缺点:

  • 学习曲线:如果你以前没有使用过 ORM,你不可能一天之内就把它添加到现有的应用中。 在更新的项目中开始使用它,在更新现有项目时花点时间。
  • 复杂查询:如果你或你的团队已经精通 SQL 查询,你可能有困难重建复杂查询的结果使用综合语言查询(LINQ)语法。 随着时间的推移,您可能会变得更容易,或者您可能会认为 orm 不适合特定的应用。
  • 性能(有争议):当第一次引入 orm 时,您可能已经考虑了在应用代码和数据库之间添加另一层对性能的影响。 但对于现代 ORM 框架(如 EF Core),性能的改进应该使其值得付出努力,而无需太过担心。

现在我们已经了解了优点和缺点,让我们关注一下为什么您会选择 EF 作为您的 web 应用的 ORM。

为什么是实体框架?

如果你以前使用过 orm,你可能使用过NHibernate或更早版本的 EF。 您可能已经在 Java 应用中使用了Hibernate。 不管你的背景如何,有很多理由说明 EF 对你的 ASP 来说是一个不错的选择.NET Core 应用。

首先,新的 EF 与新的 ASP.NET 不像以前的其他版本。 早期版本的 EF 运行时和工具是与。net 框架一起发布的,但是最近的版本将运行时分离出来,从而通过 NuGet 实现带外发布。 运行时的最新版本可以通过 NuGet 获得,而主要版本与 Visual Studio 版本同步。

实体框架的演变

随着时间的推移,EF 已经有了很大的进步。 代码优先方法和 EF 迁移的引入让许多开发人员注意到这一点。 与此同时,还有很多开发人员还没有与 EF 合作过。

多年来,EF 团队一直在不断收集开发人员对其产品的反馈。 因此,该团队已经将一个成熟的产品整合在一起,并且随着时间的推移,这个产品会变得越来越好。

如果您有兴趣提供反馈或查看来自其他开发人员的输入,请访问https://data.uservoice.com

出现的一些新特性包括批处理更新、阴影属性、内存提供程序以及处理断开连接数据的改进方法。 我们将在本章中更详细地讨论新特性,通过一个示例项目来说明这些特性是如何结合在一起的。

5 .ef .NET Framework vs . EF Core 1.0

如果你一直使用 EF 6 到现在,你应该花时间了解 EF Core 有什么变化。 EF 的基本概念和用法应该是大家熟悉的,但是这里有一些新的添加(和一些删除),这将影响你学习 EF Core 的学习曲线。

如果您一直在进行代码优先开发,那么您应该已经熟悉了在应用代码中构建自己的模型。 如果您已经使用了 Migrations,那么您还应该熟悉如何通过触发迁移来生成/更新数据库。 但是,如果您已经习惯了可视化数据建模工具和 EDMX 文件,那么您可能需要忘掉一些东西。 EDMX 文件是实体数据模型文件,可以使用早期版本的 EF 可视化地表示数据模型。

EF Core 有什么不同

首先,值得注意的是,不再支持将 EDMX 文件作为创建数据模型的方法。 这并不意味着没有方法可视化地建模您的数据库,或者从数据库优先的方法开始。 相反,如果您愿意,您仍然可以从一个现有的数据库开始,并使用外部工具来生成您的模型类。

也就是说,我们将专注于 EF Core 的代码优先方法。 这很重要,因为它将帮助您更好地控制模型类,这将使代码合并更容易。

EF 以前的版本依赖于ObjectContext作为在代码中将数据库实体作为对象的一种方法。 即使在 EF 4.1 中使用了DbContext API,下面仍然继续依赖于原来的ObjectContext。 在 EF Core 中,ObjectContext将离开我们,但DbContext保留。 代表了一系列设计模式的组合,这些设计模式允许您从应用代码中轻松地使用数据库。

虽然 EF6 可以通过 NuGet 获得,但 EF Core 将其依赖管理提升到与 ASP 保持一致的另一个级别。 净的核心。 您可以选择只使用您需要的部分,包括针对 Core、Commands、Relational(包括迁移)和 SQL Server 的单独包。 如果您包含一个特定的包作为引用,Visual Studio 2015 将根据需要自动包含额外的依赖项。

EF Core 入门

如果你在 Visual Studio 2015 中使用标准的 Web 应用模板创建一个新的 Web 应用项目,你应该已经设置了 EF Core。 您可能还记得第 3 章、了解 MVC中的患者记录 web 应用,在其中我们建立了一个具有数据库连接的基本 web 项目。

如果您从一个空项目开始,您应该在您的project.json配置文件中键入 EF 引用。 在我的患者记录应用中,我有以下参考:

"Microsoft.EntityFrameworkCore.SqlServer": "1.0.0" 

在“解决方案资源管理器”面板中,您可以展开每个引用,以识别自动作为依赖项拉入的相关引用,如下面的截图所示:

Getting started with EF Core

根据添加引用的时间,版本号可能有所不同。 虽然你可以在配置文件中输入任何当前或以前的版本,但你很可能会让 Visual Studio 的智能感知功能用弹出的工具提示建议来指导你。

Startup.cs文件中,应该在ConfigureServices()方法中调用AddEntityFrameworkStores(),然后调用添加特定的数据库提供程序和要使用的数据库上下文。 要使用 EF Core,首先要确保以下 using 语句出现在你的Startup.cs文件中:

using Microsoft.EntityFrameworkCore; 

在我的患者记录申请中,ConfigureServices()方法如下:

public void ConfigureServices(IServiceCollection services) 
{ 
    // Add framework services. 
    services.AddDbContext<ApplicationDbContext>(options => 
        options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); 

    services.AddIdentity<ApplicationUser, IdentityRole>() 
        .AddEntityFrameworkStores<ApplicationDbContext>() 
        .AddDefaultTokenProviders(); 

    // rest of method removed for brevity 
} 

前面的代码包含以下术语和方法调用:

  • Microsoft.EntityFrameworkCore:此命名空间提供对 EF Core 的访问
  • AddDbContext<>():这增加了一个db上下文,称为ApplicationDbContext
  • UseSqlServer():配置 SQL Server 数据库连接

数据库上下文位于其自己的类文件ApplicationDbContext.cs中,位于您的Data文件夹中。 在这里您将添加一个或多个DbSet条目来表示您的实体。

数据库连接字符串可以作为文本值存在于appsettings.json文件中,该文件位于项目位置的根目录中。 这个连接字符串还可以作为特定环境的环境变量存在,无论它是开发机器、内部服务器还是云环境。

设置好引用和配置文件之后,现在就可以在代码中使用 EF 了。

还有什么新鲜事吗?

在我们进入剩下的代码之前,让我们看看 EF Core 还有什么新的地方:

  • Migrations History:在 EF 的以前版本中,每次新的迁移(即数据库修订)都会在数据库中的一个特殊表中生成一个新条目。 EF Core 中仍然存在这样一个表,它被称为__EFMigrationsHistory,但是它被简化了,除了产品版本之外,只包含一个字母数字迁移 ID。
  • 快照:迁移历史表用于包含每次迁移的快照,但它已被移动到Data文件夹中的Migrations子文件夹(如ApplicationDbContextModelSnapshot.cs)中的代码文件中。 实际的文件名可能会根据数据库上下文的名称而有所不同。
  • 批量更新:EF Core 根据应用代码生成 SQL 语句来运行数据库命令。 在设置数据库上下文时,可以配置最大批处理大小。
  • 阴影属性:您可以使用阴影属性在数据库上下文中从OnModelCreating()方法中动态扩展您的模型。 这些更改将在您的数据库中进行,但不会影响实际的模型文件。
  • 内存提供程序:与其直接与实际的数据库交互,你可以选择使用内存提供程序。 这在设置和运行单元测试时也很有用。
  • 断开连接的数据:有一些改进的方法可以处理断开连接的数据,这应该可以让开发人员更容易地在断开连接的数据场景中添加和更新数据。

代码优先的数据库设计方法和关系

那么到底什么是代码优先的方法呢? 正如它听起来的那样。 可以在代码中将数据库对象建模为实体类。 要建立这些对象之间的关系,可以定义一个类以包含其他类作为成员变量。 如果您已经有一个现有的数据库,您可以在您的代码中创建一个实体模型来表示一些(或全部)数据库对象。

从我们从第 3 章了解 MVC的病人记录例子中,我们已经有了一个代表人的模型类。 在这一章中,我们将增加一个RobotDoctor班到我们的项目中,为一个未来医院与机器人医生建立一个电脑化系统。 然后我们将在代码中建立HumansRobotDoctors之间的关系,使每个机器人医生可以分配给数据库中的一个或多个人类患者。

更新模型

Models文件夹的示例项目从第 3 章,理解 MVC【显示】,让我们添加一个新的模型类来表示一个RobotDoctor``Human然后更新现有的类来添加字段识别每一个人类与机器人医生的关系。 我们还必须更新上下文文件ApplicationDbContext.cs,为每一组实体使用单独的DbSet

要添加RobotDoctor类,请执行以下步骤:

  1. 在“解决方案资源管理器”面板中,右键单击Models文件夹。
  2. 在上下文菜单中点击添加|新项目
  3. 将新类命名为RobotDoctor.cs
  4. RobotDoctor类添加以下代码:

    ``` using System.ComponentModel.DataAnnotations;

    namespace PatientRecords.Models { public class RobotDoctor { [Display(Name = "Robot Doctor ID")] public int RobotDoctorId { get; set; }

        [Display(Name = "Model Number")] 
        public int ModelNumber { get; set; }
    
        [Display(Name = "Preferred Name")] 
        public string PreferredName { get; set; } 
    }
    

    }

    ```

将使用RobotDoctorId整数字段作为主键。 其余字段将分别存储和显示型号和首选名称。 Display属性将用作字段的友好文本标签。

要更新 Human 类,请遵循以下步骤:

  1. Models文件夹中打开Human.cs类文件。
  2. 在类的底部添加以下字段:
[Display(Name = "Robot Doctor")] 
public int RobotDoctorId { get; set; } 
public RobotDoctor RobotDoctor { get; set; } 

Human类中的RobotDoctorId整数字段将作为外键引用RobotDoctor类中相应的字段。 对象将允许将每个Human分配给特定的RobotDoctorDisplay属性将作为一个友好的文本标签用于字段。

要更新数据库上下文,请遵循以下步骤:

  1. Data文件夹中打开ApplicationDbContext.cs类文件。
  2. 在班级底部为RobotDoctors增加一个新的DbSet
  3. 通过将单词Human复数化为Humans来更新HumansDbSet,如下代码所示:

命名为RobotDoctorsDbSet将代表存储在数据库中的一组机器人医生。 命名为HumansDbSet将表示存储在数据库中的Humans集合。 虽然没有必要将其复数化,但这一微小的变化应该有助于阐明其目的。

更新控制器

当我们将ApplicationDbContext类中命名为HumanDbSet更新为Humans时,我们的HumanController仍然保留对旧名称的引用。 有两种方法可以解决这个问题:你可以在控制器文件中手动重命名HumanHumans的所有引用,或者你可以使用 Visual Studio 内置的重命名功能。

使用重命名功能,您可以点击DbSet名为Human在您的代码(重命名之前Human``Humans),然后用一套快速跟踪击键:Ctrl + R+【显示】。 这将允许您在引用新值的任何地方输入新值。 你将有机会通过选中相应的复选框来预览你的更改,如下图所示:

*Updating the controllers

在我们更新其余的控制器方法之前,请确保以下 using 语句位于HumanController类的顶部:

using System.Threading.Tasks; 
using System.Collections.Generic; 
using System; 

以下命名空间对于我们将在控制器中执行的任务是有用的:

  • 命名空间将允许我们使用异步方法。
  • Collections.Generic命名空间允许我们使用IEnumerable列表。
  • 命名空间System将允许我们使用String方法。

为了确保在Humans列表中包含每个HumanRobotDoctor数据,更新HumanController类的Index()方法以包含以下代码:

public async Task<IActionResult> Index() 
{ 
    var humans = _context.Humans.Include(h => h.RobotDoctor); 
    return View(humans); 
} 

在前面的代码中,对数据库上下文中名为HumansDbSet的调用和RobotDoctor数据由Include()方法拉出。 然后,该方法将人员集返回到相应的视图。 然后,视图负责遍历一个列表Humans以显示给用户。

为了确保我们在其Details页面中包含特定HumanRobotDoctor数据,更新Details()方法以包含以下代码:

public async Task<ActionResult> Details(int? id) 
{ 
    Human human = await _context.Humans 
        .Include(h => h.RobotDoctor) 
        .SingleOrDefaultAsync(h => h.ID == id); 
    if (human == null) 
    { 
        return NotFound(); 
    } 
    return View(human); 
} 

注意,命名为HumansDbSet后面是对Include()的调用,以确保RobotDoctor数据也包含在内。 在此过程中,我们还通过包括以下内容来利用异步方法调用:

  • 方法定义中的async关键字
  • Task<ActionResult>返回类型而不是ActionResult
  • SingleOrDefaultAsync()而不是Single()
  • 方法调用中的await关键字

接下来,让我们更新Create()方法的HttpGet版本,以便在显示一个创建新Humans的入口表单之前获得RobotDoctors列表。 由于HttpGet是控制器方法的默认谓词,因此该方法不需要一个属性来指示其HttpVerb。 用以下代码替换Create():

public IActionResult Create() 
{ 
    ViewBag.RobotDoctors = GetListOfRobotDoctors(); 
    return View(); 
} 

private IEnumerable<SelectListItem> GetListOfRobotDoctors(int selected = -1) 
{ 
    var tmp = _context.RobotDoctors.ToList();   

    // Create authors list for <select> dropdown 
    return tmp 
        .OrderBy(rb => rb.ModelNumber) 
        .Select(rb => new SelectListItem 
        { 
            Text = String.Format("{0}: {1}", rb.ModelNumber, rb.PreferredName), 
            Value = rb.RobotDoctorId.ToString(), 
            Selected = rb.RobotDoctorId == selected 
        }); 
} 

在前面的代码中,Create()方法之后添加了新的私有方法来完成获取数据的工作。 然后,Create()方法返回默认视图Create.cshtml,以显示一个输入表单。

接下来,我们还需要更新Create()方法的HttpPost版本。 该方法将在方法定义之上特别具有HttpPost属性。 用下面的代码更新这个Create()方法:

[HttpPost] 
[ValidateAntiForgeryToken] 
public async Task<ActionResult> Create([Bind("SocialSecurityNumber", "DateOfBirth", "FirstName", "LastName", "RobotDoctorId")] Human human) 
{ 
    if (ModelState.IsValid) 
    { 
        _context.Humans.Add(human); 
        await _context.SaveChangesAsync(); 
        return RedirectToAction("Index"); 
    } 
    return View(human); 
} 

另一件要注意的事情是Create()方法参数列表中的Bind属性。 该属性帮助我们准确地确定希望将哪个模型属性绑定到相应的表单字段。

最后,让我们用以下代码更新Edit()方法,从HttpGet版本开始。 与Create()方法一样,HttpGet版本不需要在其上面有任何属性来指示控制器方法使用的 HttpVerb:

public async Task<IActionResult> Edit(int? id) 
{ 
    if (id == null) 
    { 
        return NotFound(); 
    } 

    Human human = _context.Humans.Single(m => m.ID == id); 
    if (human == null) 
    { 
        return NotFound(); 
    } 

    ViewBag.RobotDoctors = GetListOfRobotDoctors(); 
    return View(human); 
} 

对这个Edit()方法的主要更改是添加了一个名为RobotDoctors的动态ViewBag属性,与Create()方法中所看到的类似。 它使用相同的私有方法来获取RobotDoctors列表。 它将Human模型返回到视图Edit.cshtml,视图显示一个条目表单来编辑现有的Human条目。

最后,让我们更新Edit()方法的HttpPost版本。 用以下代码替换此方法,以确保Human模型更新为已传递的数据:

[HttpPost] 
[ValidateAntiForgeryToken] 
public async Task<IActionResult> Edit(int id, [Bind("SocialSecurityNumber", "DateOfBirth", "FirstName", "LastName", "RobotDoctorId")] Human human) 
{ 
    if (id != human.ID) 
    { 
        return NotFound(); 
    } 

    if (ModelState.IsValid) 
    { 
        human.ID = id; 
        _context.Humans.Attach(human); 
        _context.Entry(human).State = EntityState.Modified; 
        await _context.SaveChangesAsync(); 
        return RedirectToAction("Index"); 
    } 
    return View(human); 

} 

为了通知 EF Core 修改的状态,将编辑条目的State属性设置为EntityState.Modified。 如果存在任何验证错误,则通过传入更新的Human模型,将用户定向到相同的Edit.cshtml视图。 如果没有验证错误,则将用户重定向到Index.cshtml视图,以显示Human条目的当前列表。

Delete()方法不需要任何额外的更改,因为实体代码生成负责正确删除每个实体。 当使用适当的控制器方法删除选定的Human时,它将显示一个由Delete()方法的HttpGet版本准备的确认屏幕。 一旦确认,Delete()方法的HttpPost版本将在调用SaveChanges()以相应更新数据库之前负责实际的删除。 在删除操作之后,用户被重定向到Index方法以查看Human条目列表。

更新视图

要完成编码更改,必须更新Human控制器的Views。 这些文件包括在Views文件夹中的Human子文件夹中的以下.cshtml文件:

  • 指数
  • 编辑
  • 细节
  • 创建

Index.cshtml文件中,添加以下表头<th>块,显示机器人医生的首选名称的标签,就在 SSN 标签之后:

<th> 
    @Html.DisplayNameFor(model => model.RobotDoctor.PreferredName) 
</th> 

foreach内循环遍历项目Model,<td>添加一个表数据块显示名字,SSN 之后字段,用于存储一个独特的社会安全号码。 这应该正好在编辑细节删除链接之前:

<td> 
    @Html.DisplayFor(modelItem => item.RobotDoctor.PreferredName) 
</td> 

Edit.cshtml文件中,更新<form>标记以包含额外的标记帮助器属性:

<form  
    asp-controller="Human"  
    asp-action="Edit"  
    method="post"  
    asp-route-id="@Model.ID"> 

这些属性的用途有很多:

  • asp-controllerasp-action:这表示将表单提交到的控制器名称和方法
  • method:表示提交表单的方法,通常为POST
  • asp-route-id:表示要与提交关联的 ID 值

在表单验证摘要的下方,删除隐藏的 ID 字段:

<input type="hidden" asp-for="ID" /> 

相反,用下面的代码替换它:

<div class="form-group"> 
    <label  
        asp-for="RobotDoctorId"  
        class="col-md-2 control-label"></label> 
    <div class="col-md-10"> 
        <select  
                asp-for="RobotDoctorId"  
                asp-items="@ViewBag.RobotDoctors"></select> 
    </div> 
</div> 

前面的代码显示了RobotDoctorId字段的标签,后面是RobotDoctors的下拉框。 您可能还记得,正在多个控制器方法中设置ViewBag.RobotDoctors的值。

Details.cshtml文件中,在<dt>/<dd>对块中添加以下描述列表条目,以显示每个机器人医生的编号、型号和首选名称的标签和字段,就在 SSN 标签的右侧:

<dt> 
    @Html.DisplayNameFor(model => model.RobotDoctor.RobotDoctorId) 
</dt> 
<dd> 
    @Html.DisplayFor(model => model.RobotDoctor.RobotDoctorId) 
</dd> 
<dt> 
    @Html.DisplayNameFor(model => model.RobotDoctor.ModelNumber) 
</dt> 
<dd> 
    @Html.DisplayFor(model => model.RobotDoctor.ModelNumber) 
</dd> 
<dt> 
    @Html.DisplayNameFor(model => model.RobotDoctor.PreferredName) 
</dt> 
<dd> 
    @Html.DisplayFor(model => model.RobotDoctor.PreferredName) 
</dd> 

前面的条目应该在关闭</dl>列表之前添加。

Edit视图类似,您必须使用新标签和下拉菜单更新Create视图,以显示RobotDoctors列表。 在表单中的验证摘要下面,添加以下表单组:

<div class="form-group"> 
    <label  
        asp-for="RobotDoctorId"  
        class="col-md-2 control-label"></label> 
  <div class="col-md-10"> 
    <select  
        asp-for="RobotDoctorId"  
        asp-items="@ViewBag.RobotDoctors"></select> 
  </div> 
</div> 

再一次,在适当的控制器方法中设置了ViewBag.RobotDoctors的动态值。 现在已经对模型、控制器和视图进行了所有必要的更改,现在可以使用 EF Migrations 更新数据库了。

EF Code First 迁移,用于数据库版本控制和维护

在 EF 中,您可以使用迁移来促进数据库的创建、升级和降级。 您还可以使用自动生成的版本历史来跟踪更改,并与开发团队的其他成员保持同步。

我们之前已经在我们的示例项目中设置了迁移以便开始工作。 在本节中,我们将添加一个新的迁移,以反映我们在本章中所做的更改。

设置迁移

下面回顾一下我们之前如何为 EF 设置迁移。 再次强调,请记住您的 DNX 版本可能有所不同:

  1. 打开指向项目文件夹位置的命令提示符。
  2. 执行如下命令:
>dotnet restore
>dotnet build
>dotnet ef migrations add Initial
>dotnet ef database update

前面的命令不需要再次运行,因为您的初始迁移已经在第 3 章了解 MVC中创建。 相反,我们将运行额外的命令来创建一个新的迁移来表示对模型和数据库上下文的更改。

添加和删除迁移

当你添加一个新的迁移时,会发生以下几件事:

  • 在初始迁移文件下面的Migrations文件夹中将生成一个新的类文件。 文件名通常以日期/时间戳作为前缀,然后是迁移的名称,例如2016MMDDXXYY_RobotDoctors.cs.
  • 迁移文件的内容将包含一个带有迁移名称的类,例如RobotDoctors。 这个类将包含两个方法Up()Down(),分别帮助数据库的升级和降级。
  • 一个自动生成的快照文件将被更新,以反映数据库模型和关系的当前状态。 文件名通常以数据库上下文的名称作为前缀,例如ApplicationDbContextModelSnapshot.cs
  • 在运行 update 命令之后,将使用适当的更改更新物理数据库。 如果由于某种原因无法成功处理更改,将返回一个或多个错误消息。

要添加新的迁移,请在项目文件夹位置的命令提示符中运行以下命令:

>dotnet restore
>dotnet build
>dotnet ef migrations add RobotDoctors
>dotnet ef database update

这将生成一个新的迁移,如前所述。 如果您得到任何数据库冲突错误,您可能必须首先删除Humans表中的记录,然后重新运行数据库更新命令。 现在可以检查数据库以检查新的更改。 为了快速找到数据库,使用SQL Server Object Explorer面板钻到你的数据库并检查表和字段。

后退一步,您可以使用remove命令删除之前的迁移。 完整的命令列表如下:

  • add:这将添加一个新的迁移
  • apply:这将把迁移应用到数据库
  • list:这将显示迁移列表
  • script:这将生成一个 SQL 脚本列表,而不需要实际更新数据库
  • remove:这将删除之前的迁移

值得注意的是,脚本命令类似于 EF 以前版本中的脚本参数。 不再将此特性隐藏在参数中(如果忘记包含它,可能会意外覆盖数据库),现在有单独的命令来更新数据库或只生成 SQL 脚本,而不更改任何内容。

运行应用

现在已经使用新的迁移完成了所有必要的数据库更改,现在可以运行应用了。 在 Visual Studio 2015 中构建解决方案并运行应用。 这将从Home控制器启动默认的 web 浏览器,主Index页面,如下截图所示:

Running your application

在顶部菜单中,点击Humans链接,可查看Human控制器的Index页面。 这将显示一个空列表,因为此时我们还没有创建任何新的Humans。 如果您在进入此步骤之前已经创建了自己的条目,请在继续之前删除您的条目。

下图为Human控制器Index界面:

Running your application

为了确保我们有一些示例数据,打开RobotDoctors表,并使用 Visual Studio 中的SQL Server 对象管理器手动添加一些条目到数据库中。 下面的截图显示了RobotDoctorIdModelNumberPreferredName的一些示例值:

| RobotDoctorId (auto) | ModelNumber | PreferredName | | 1 | 90210 | X 博士 | | 2 | 12345 | 芝加哥博士 |

我们可以写一些额外的代码来添加种子数据到你的数据库中,但我们将把这一步保存到第 7 章依赖注入和健壮 Web 应用的单元测试中。 最好使用一个测试框架来添加种子数据,而不是将其添加到应用代码中。

回到您的 web 浏览器,单击左上角区域中的Create New链接,查看HumanControllerCreate视图。 选择一个机器人医生,然后完成并提交表单,创建一个新的人类条目。 如果有任何验证错误,将提示您在提交完成之前更正它们。 一旦提交,你应该被重定向到Index页。 Create视图如下截图所示:

Running your application

在下面的截图中,在Index页面上,你应该看到标签为EditDetails,andDelete的链接。 让我们试试这些链接来编辑条目,查看条目的详细信息,或删除现有条目:

Running your application

如果您单击Edit链接,您现在应该看到先前添加条目的可编辑详细信息,如下面的截图所示。 更改一些值并提交表单。 与创建新条目类似,您将被提示修复任何验证错误,或者在提交时返回Index页面:

Running your application

如果您单击Details链接,您应该看到先前添加条目的不可编辑详细信息,如下面的截图所示。 您可以选择再次返回Index页面或单击Edit查看可编辑视图:

Running your application

如果您点击Delete链接,您将看到一个确认页面,询问您是否要删除该条目,如下面的截图所示。 点击删除按钮继续或返回Index页面:

Running your application

总结

在这一章中,我们讨论了 EF Core,它是一个用于 web 应用的现代 ORM 工具。 出于本书的目的,我们只讨论了 EF Core 与 ASP 的关系。 净的核心。 关于 EF Core 的更多信息,您可以查看官方文档https://ef.readthedocs.org

在下一章中,您将涵盖依赖注入反转控制单元测试。 这将为您提供良好的应用设计基础,以确保您拥有健壮的、可测试的应用,这些应用可以放心地轻松更新。*