教程:了解高级方案 - ASP.NET MVC 和 EF CoreTutorial: Learn about advanced scenarios - ASP.NET MVC with EF Core

之前的教程中,已经以每个类一张表的方式实现了继承。In the previous tutorial, you implemented table-per-hierarchy inheritance. 本教程将会介绍在掌握开发基础 ASP.NET Core web 应用程序之后使用 Entity Framework Core 开发时需要注意的几个问题。This tutorial introduces several topics that are useful to be aware of when you go beyond the basics of developing ASP.NET Core web applications that use Entity Framework Core.

在本教程中,你将了解:In this tutorial, you:

  • 执行原始 SQL 查询Perform raw SQL queries
  • 调用查询以返回实体Call a query to return entities
  • 调用查询以返回其他类型Call a query to return other types
  • 调用更新查询Call an update query
  • 检查 SQL 查询Examine SQL queries
  • 创建抽象层Create an abstraction layer
  • 了解自动更改检测Learn about Automatic change detection
  • 了解 EF Core 源代码与开发计划Learn about EF Core source code and development plans
  • 了解如何使用动态 LINQ 简化代码Learn how to use dynamic LINQ to simplify code

先决条件Prerequisites

执行原始 SQL 查询Perform raw SQL queries

使用 Entity Framework 的优点之一是它可避免你编写跟数据库过于耦合的代码One of the advantages of using the Entity Framework is that it avoids tying your code too closely to a particular method of storing data. 它会自动生成 SQL 查询和命令,使得你无需自行编写。It does this by generating SQL queries and commands for you, which also frees you from having to write them yourself. 但有一些特殊情况,你需要执行手动创建的特定 SQL 查询。But there are exceptional scenarios when you need to run specific SQL queries that you have manually created. 对于这些情况下, Entity Framework Code First API 包括直接传递 SQL 命令将到数据库的方法。For these scenarios, the Entity Framework Code First API includes methods that enable you to pass SQL commands directly to the database. 在 EF Core 1.0 中具有以下选项:You have the following options in EF Core 1.0:

  • 使用DbSet.FromSql返回实体类型的查询方法。Use the DbSet.FromSql method for queries that return entity types. 返回的对象必须是DbSet对象期望的类型,并且它们会自动跟踪数据库上下文中除非你手动关闭跟踪The returned objects must be of the type expected by the DbSet object, and they're automatically tracked by the database context unless you turn tracking off.

  • 对于非查询命令使用Database.ExecuteSqlCommandUse the Database.ExecuteSqlCommand for non-query commands.

如果需要运行该返回类型不是实体的查询,你可以使用由 EF 提供的 ADO.NET 中使用数据库连接。If you need to run a query that returns types that aren't entities, you can use ADO.NET with the database connection provided by EF. 数据库上下文不会跟踪返回的数据,即使你使用该方法来检索实体类型也是如此。The returned data isn't tracked by the database context, even if you use this method to retrieve entity types.

在 Web 应用程序中执行 SQL 命令时,请务必采取预防措施来保护站点免受 SQL 注入攻击。As is always true when you execute SQL commands in a web application, you must take precautions to protect your site against SQL injection attacks. 一种方法是使用参数化查询,确保不会将网页提交的字符串视为 SQL 命令。One way to do that is to use parameterized queries to make sure that strings submitted by a web page can't be interpreted as SQL commands. 在本教程中,将用户输入集成到查询中时会使用参数化查询。In this tutorial you'll use parameterized queries when integrating user input into a query.

调用查询以返回实体Call a query to return entities

DbSet<TEntity> 类提供了一种方法,可用于执行返回 TEntity 类型实体的查询。The DbSet<TEntity> class provides a method that you can use to execute a query that returns an entity of type TEntity. 若要查看实现细节,你需要更改院系控制器中 Details 方法的代码。To see how this works you'll change the code in the Details method of the Department controller.

在 DepartmentsController.cs 的 Details 方法中,使用 FromSql 方法调用替换检索院系的代码,如以下突出显示的代码所示:In DepartmentsController.cs , in the Details method, replace the code that retrieves a department with a FromSql method call, as shown in the following highlighted code:

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

    string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
    var department = await _context.Departments
        .FromSql(query, id)
        .Include(d => d.Administrator)
        .AsNoTracking()
        .FirstOrDefaultAsync();

    if (department == null)
    {
        return NotFound();
    }

    return View(department);
}

为了验证新代码是否正常工作,请选择“院系”选项卡,然后选择其中某一院系的“详细信息” 。To verify that the new code works correctly, select the Departments tab and then Details for one of the departments.

院系详细信息

调用查询以返回其他类型Call a query to return other types

之前你在“关于”页面创建了一个学生统计信息网格,显示每个注册日期的学生数量。Earlier you created a student statistics grid for the About page that showed the number of students for each enrollment date. 可以从学生实体集中获取数据 (_context.Students) ,使用 LINQ 将结果投影到EnrollmentDateGroup视图模型对象的列表。You got the data from the Students entity set (_context.Students) and used LINQ to project the results into a list of EnrollmentDateGroup view model objects. 假设你想要 SQL 本身编写,而不使用 LINQ。Suppose you want to write the SQL itself rather than using LINQ. 需要运行 SQL 查询中返回实体对象之外的内容。To do that you need to run a SQL query that returns something other than entity objects. 在 EF Core 1.0 中,执行该操作的另一种方法是编写 ADO.NET 代码,并从 EF 获取数据库连接。In EF Core 1.0, one way to do that is write ADO.NET code and get the database connection from EF.

HomeController.cs 中,将About方法替换为以下代码:In HomeController.cs , replace the About method with the following code:

public async Task<ActionResult> About()
{
    List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
    var conn = _context.Database.GetDbConnection();
    try
    {
        await conn.OpenAsync();
        using (var command = conn.CreateCommand())
        {
            string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
                + "FROM Person "
                + "WHERE Discriminator = 'Student' "
                + "GROUP BY EnrollmentDate";
            command.CommandText = query;
            DbDataReader reader = await command.ExecuteReaderAsync();

            if (reader.HasRows)
            {
                while (await reader.ReadAsync())
                {
                    var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(0), StudentCount = reader.GetInt32(1) };
                    groups.Add(row);
                }
            }
            reader.Dispose();
        }
    }
    finally
    {
        conn.Close();
    }
    return View(groups);
}

添加 using 语句:Add a using statement:

using System.Data.Common;

运行应用并转到“关于”页面。Run the app and go to the About page. 显示的数据和之前一样。It displays the same data it did before.

“关于”页面

调用更新查询Call an update query

假设 Contoso University 管理员希望在数据库中执行全局更改,例如更改每门课程的学分。Suppose Contoso University administrators want to perform global changes in the database, such as changing the number of credits for every course. 如果该大学提供了大量课程,那么将所有课程作为实体来检索并单独更改就非常低效。If the university has a large number of courses, it would be inefficient to retrieve them all as entities and change them individually. 在本节中,你将实现一个网页,使用户能够指定一个系数,通过该系数即可更改所有课程的学分,而你将通过执行 SQL UPDATE 语句来完成更改。In this section you'll implement a web page that enables the user to specify a factor by which to change the number of credits for all courses, and you'll make the change by executing a SQL UPDATE statement. 网页的外观类似于下图:The web page will look like the following illustration:

“更新课程学分”页面

在 CoursesController.cs 中,为 HttpGet 和 HttpPost 添加 UpdateCourseCredits 方法:In CoursesController.cs , add UpdateCourseCredits methods for HttpGet and HttpPost:

public IActionResult UpdateCourseCredits()
{
    return View();
}
[HttpPost]
public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
{
    if (multiplier != null)
    {
        ViewData["RowsAffected"] = 
            await _context.Database.ExecuteSqlCommandAsync(
                "UPDATE Course SET Credits = Credits * {0}",
                parameters: multiplier);
    }
    return View();
}

当控制器处理 HttpGet 请求时,ViewData["RowsAffected"]中不会返回任何东西,并且在视图中显示一个空文本框和提交按钮,如上图所示。When the controller processes an HttpGet request, nothing is returned in ViewData["RowsAffected"], and the view displays an empty text box and a submit button, as shown in the preceding illustration.

当单击 Update 按钮时,将调用 HttpPost 方法,且从文本框中输入的值获取乘数。When the Update button is clicked, the HttpPost method is called, and multiplier has the value entered in the text box. 代码接着执行 SQL 语句更新课程,并向视图的ViewData返回受影响的行数。The code then executes the SQL that updates courses and returns the number of affected rows to the view in ViewData. 当视图获取RowsAffected值,它将显示更新的行数。When the view gets a RowsAffected value, it displays the number of rows updated.

在“解决方案资源管理器”中,右键单击“Views/Courses”文件夹,然后依次单击“添加”和“新建项”。In Solution Explorer , right-click the Views/Courses folder, and then click Add > New Item.

在“添加新项”对话框中,在左侧窗格的“已安装”下单击“ASP.NET Core”,单击“Razor 视图”,并将新视图命名为“UpdateCourseCredits.cshtml” 。In the Add New Item dialog, click ASP.NET Core under Installed in the left pane, click Razor View , and name the new view UpdateCourseCredits.cshtml.

Views/Courses/UpdateCourseCredits.cshtml 中,将模板代码替换为以下代码:In Views/Courses/UpdateCourseCredits.cshtml , replace the template code with the following code:

@{
    ViewBag.Title = "UpdateCourseCredits";
}

<h2>Update Course Credits</h2>

@if (ViewData["RowsAffected"] == null)
{
    <form asp-action="UpdateCourseCredits">
        <div class="form-actions no-color">
            <p>
                Enter a number to multiply every course's credits by: @Html.TextBox("multiplier")
            </p>
            <p>
                <input type="submit" value="Update" class="btn btn-default" />
            </p>
        </div>
    </form>
}
@if (ViewData["RowsAffected"] != null)
{
    <p>
        Number of rows updated: @ViewData["RowsAffected"]
    </p>
}
<div>
    @Html.ActionLink("Back to List", "Index")
</div>

通过选择 Courses 选项卡运行UpdateCourseCredits方法,然后在浏览器地址栏中 URL 的末尾添加"/ UpdateCourseCredits"到 (例如: http://localhost:5813/Courses/UpdateCourseCredits)。Run the UpdateCourseCredits method by selecting the Courses tab, then adding "/UpdateCourseCredits" to the end of the URL in the browser's address bar (for example: http://localhost:5813/Courses/UpdateCourseCredits). 在文本框中输入数字:Enter a number in the text box:

“更新课程学分”页面

单击 UpdateClick Update. 你会看到受影响的行数:You see the number of rows affected:

“更新课程学分”页面中受影响的行

单击“返回列表”可以查看课程列表,其中学分已替换为修改后的数字。Click Back to List to see the list of courses with the revised number of credits.

请注意,生产代码将确保更新最终生成有效的数据。Note that production code would ensure that updates always result in valid data. 此处所示的简化代码可将学分的数字进行相乘,以便足以生成大于 5 的数字。The simplified code shown here could multiply the number of credits enough to result in numbers greater than 5. (Credits 属性具有 [Range(0, 5)] 特性。)更新查询会起作用,但无效数据可能在系统的其他部分中产生意外结果,这些部分假定学分为 5 或更少。(The Credits property has a [Range(0, 5)] attribute.) The update query would work but the invalid data could cause unexpected results in other parts of the system that assume the number of credits is 5 or less.

有关原生 SQL 查询的详细信息,请参阅原生 SQL 查询For more information about raw SQL queries, see Raw SQL Queries.

检查 SQL 查询Examine SQL queries

有时能够以查看发送到数据库的实际 SQL 查询对于开发者来说是很有用的。Sometimes it's helpful to be able to see the actual SQL queries that are sent to the database. EF Core 自动使用 ASP.NET Core 的内置日志记录功能来编写包含 SQL 查询和更新的日志。Built-in logging functionality for ASP.NET Core is automatically used by EF Core to write logs that contain the SQL for queries and updates. 在本部分中,你将看到记录 SQL 日志的一些示例。In this section you'll see some examples of SQL logging.

打开 StudentsController.cs 并在Details方法的if (student == null)语句上设置断点。Open StudentsController.cs and in the Details method set a breakpoint on the if (student == null) statement.

在调试模式下运行应用,并转到某位学生的“详细信息”页面。Run the app in debug mode, and go to the Details page for a student.

转到 输出 窗口显示调试输出,就可以看到查询语句:Go to the Output window showing debug output, and you see the query:

Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (56ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT TOP(2) [s].[ID], [s].[Discriminator], [s].[FirstName], [s].[LastName], [s].[EnrollmentDate]
FROM [Person] AS [s]
WHERE ([s].[Discriminator] = N'Student') AND ([s].[ID] = @__id_0)
ORDER BY [s].[ID]
Microsoft.EntityFrameworkCore.Database.Command:Information: Executed DbCommand (122ms) [Parameters=[@__id_0='?'], CommandType='Text', CommandTimeout='30']
SELECT [s.Enrollments].[EnrollmentID], [s.Enrollments].[CourseID], [s.Enrollments].[Grade], [s.Enrollments].[StudentID], [e.Course].[CourseID], [e.Course].[Credits], [e.Course].[DepartmentID], [e.Course].[Title]
FROM [Enrollment] AS [s.Enrollments]
INNER JOIN [Course] AS [e.Course] ON [s.Enrollments].[CourseID] = [e.Course].[CourseID]
INNER JOIN (
    SELECT TOP(1) [s0].[ID]
    FROM [Person] AS [s0]
    WHERE ([s0].[Discriminator] = N'Student') AND ([s0].[ID] = @__id_0)
    ORDER BY [s0].[ID]
) AS [t] ON [s.Enrollments].[StudentID] = [t].[ID]
ORDER BY [t].[ID]

你会注意到一些可能会让你觉得惊讶的操作: SQL 从 Person 表最多选择 2 行 (TOP(2)) 。You'll notice something here that might surprise you: the SQL selects up to 2 rows (TOP(2)) from the Person table. SingleOrDefaultAsync方法服务器上不会解析为 1 行。The SingleOrDefaultAsync method doesn't resolve to 1 row on the server. 原因是:Here's why:

  • 如果查询返回多个行,该方法会返回 null。If the query would return multiple rows, the method returns null.
  • 如果想知道查询是否返回多个行,EF 必须检查是否至少返回 2。To determine whether the query would return multiple rows, EF has to check if it returns at least 2.

请注意,你不必使用调试模式,并在断点处停止,然后在 输出 窗口获取日志记录。Note that you don't have to use debug mode and stop at a breakpoint to get logging output in the Output window. 这种方法非常便捷,只需在想查看输出时停止日志记录即可。It's just a convenient way to stop the logging at the point you want to look at the output. 如果不进行此操作,程序将继续进行日志记录,要查看感兴趣的部分则必须向后滚动。If you don't do that, logging continues and you have to scroll back to find the parts you're interested in.

创建抽象层Create an abstraction layer

许多开发人员编写代码实现存储库和工作模式单元以作为使用 Entity Framework 代码的包装器。Many developers write code to implement the repository and unit of work patterns as a wrapper around code that works with the Entity Framework. 这些模式用于在应用程序的数据访问层和业务逻辑层之间创建抽象层。These patterns are intended to create an abstraction layer between the data access layer and the business logic layer of an application. 实现这些模式可让你的应用程序对数据存储介质的更改不敏感,而且很容易进行自动化单元测试和进行测试驱动开发 (TDD)。Implementing these patterns can help insulate your application from changes in the data store and can facilitate automated unit testing or test-driven development (TDD). 但是,编写附加代码以实现这些模式对于使用 EF 的应用程序并不总是最好的选择,原因有以下几个:However, writing additional code to implement these patterns isn't always the best choice for applications that use EF, for several reasons:

  • EF 上下文类可以为使用 EF 的数据库更新充当工作单位类。The EF context class itself insulates your code from data-store-specific code.

  • 对于使用 EF 进行的数据库更新,EF 上下文类可充当工作单元类。The EF context class can act as a unit-of-work class for database updates that you do using EF.

  • EF 包括用于无需编写存储库代码就实现 TDD 的功能。EF includes features for implementing TDD without writing repository code.

有关如何实现存储库和工作单元模式的详细信息,请参阅本系列教程的 Entity Framework 5 版本For information about how to implement the repository and unit of work patterns, see the Entity Framework 5 version of this tutorial series.

Entity Framework Core 的实现可用于测试的内存数据库驱动程序。Entity Framework Core implements an in-memory database provider that can be used for testing. 有关详细信息,请参阅测试以及 InMemoryFor more information, see Test with InMemory.

自动脏值检测Automatic change detection

Entity Framework 通过比较的实体的当前值与原始值来判断更改实体的方式 (因此需要发送更新到数据库)。The Entity Framework determines how an entity has changed (and therefore which updates need to be sent to the database) by comparing the current values of an entity with the original values. 查询或附加该实体时会存储的原始值。The original values are stored when the entity is queried or attached. 如下方法会导致自动脏值:Some of the methods that cause automatic change detection are the following:

  • DbContext.SaveChangesDbContext.SaveChanges

  • DbContext.EntryDbContext.Entry

  • ChangeTracker.EntriesChangeTracker.Entries

如果正在跟踪大量实体,并且这些方法之一在循环中多次调用,通过使用ChangeTracker.AutoDetectChangesEnabled属性暂时关闭自动脏值检测,能够显著改进性能。If you're tracking a large number of entities and you call one of these methods many times in a loop, you might get significant performance improvements by temporarily turning off automatic change detection using the ChangeTracker.AutoDetectChangesEnabled property. 例如:For example:

_context.ChangeTracker.AutoDetectChangesEnabled = false;

EF Core 源代码与开发计划EF Core source code and development plans

Entity Framework Core 源位于 https://github.com/dotnet/efcoreThe Entity Framework Core source is at https://github.com/dotnet/efcore. 仓库中除了有源代码,还可包括每夜生成、 问题跟踪、 功能规范、 设计会议备忘录和将来的开发路线图The EF Core repository contains nightly builds, issue tracking, feature specs, design meeting notes, and the roadmap for future development. 你可以归档或查找 bug 并进行更改。You can file or find bugs, and contribute.

尽管源代码处于开源状态, Entity Framework Core 是由 Microsoft 完全支持的产品。Although the source code is open, Entity Framework Core is fully supported as a Microsoft product. Microsoft Entity Framework 团队将控制接受哪些贡献和测试所有的代码更改,以确保每个版本的质量。The Microsoft Entity Framework team keeps control over which contributions are accepted and tests all code changes to ensure the quality of each release.

现有数据库逆向工程Reverse engineer from existing database

若想要通过对现有数据库中的实体类反向工程得出数据模型,可以使用scaffold-dbcontextTo reverse engineer a data model including entity classes from an existing database, use the scaffold-dbcontext command. 可以参阅入门教程See the getting-started tutorial.

使用动态 LINQ 简化代码Use dynamic LINQ to simplify code

本系列的第三个教程演示如何通过在switch语句中对列名称进行硬编码来编写 LINQ 。The third tutorial in this series shows how to write LINQ code by hard-coding column names in a switch statement. 如果只有两列可供选择,这种方法可行,但是如果拥有许多列,代码可能会变得冗长。With two columns to choose from, this works fine, but if you have many columns the code could get verbose. 要解决该问题,可使用 EF.Property 方法将属性名称指定为一个字符串。To solve that problem, you can use the EF.Property method to specify the name of the property as a string. 要尝试此方法,请将 StudentsController 中的 Index 方法替换为以下代码。To try out this approach, replace the Index method in the StudentsController with the following code.

 public async Task<IActionResult> Index(
     string sortOrder,
     string currentFilter,
     string searchString,
     int? pageNumber)
 {
     ViewData["CurrentSort"] = sortOrder;
     ViewData["NameSortParm"] = 
         String.IsNullOrEmpty(sortOrder) ? "LastName_desc" : "";
     ViewData["DateSortParm"] = 
         sortOrder == "EnrollmentDate" ? "EnrollmentDate_desc" : "EnrollmentDate";

     if (searchString != null)
     {
         pageNumber = 1;
     }
     else
     {
         searchString = currentFilter;
     }

     ViewData["CurrentFilter"] = searchString;

     var students = from s in _context.Students
                    select s;
     
     if (!String.IsNullOrEmpty(searchString))
     {
         students = students.Where(s => s.LastName.Contains(searchString)
                                || s.FirstMidName.Contains(searchString));
     }

     if (string.IsNullOrEmpty(sortOrder))
     {
         sortOrder = "LastName";
     }

     bool descending = false;
     if (sortOrder.EndsWith("_desc"))
     {
         sortOrder = sortOrder.Substring(0, sortOrder.Length - 5);
         descending = true;
     }

     if (descending)
     {
         students = students.OrderByDescending(e => EF.Property<object>(e, sortOrder));
     }
     else
     {
         students = students.OrderBy(e => EF.Property<object>(e, sortOrder));
     }

     int pageSize = 3;
     return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), 
         pageNumber ?? 1, pageSize));
 }

鸣谢Acknowledgments

Tom Dykstra 和 Rick Anderson (twitter @RickAndMSFT) 共同编写了本教程。Tom Dykstra and Rick Anderson (twitter @RickAndMSFT) wrote this tutorial. Rowan Miller、 Diego Vega 和 Entity Framework 团队的其他成员协助代码评审和帮助解决编写教程代码时出现的调试问题。Rowan Miller, Diego Vega, and other members of the Entity Framework team assisted with code reviews and helped debug issues that arose while we were writing code for the tutorials. John Parente 和 Paul Goldman 致力于更新 ASP.NET Core 2.2 的教程。John Parente and Paul Goldman worked on updating the tutorial for ASP.NET Core 2.2.

常见错误疑难解答Troubleshoot common errors

ContosoUniversity.dll 被另一个进程使用ContosoUniversity.dll used by another process

错误消息:Error message:

无法打开“...bin\Debug\netcoreapp1.0\ContosoUniversity.dll' for writing -- ”进程无法访问文件“...\bin\Debug\netcoreapp1.0\ContosoUniversity.dll”,因为它正在由其他进程使用。Cannot open '...bin\Debug\netcoreapp1.0\ContosoUniversity.dll' for writing -- 'The process cannot access the file '...\bin\Debug\netcoreapp1.0\ContosoUniversity.dll' because it is being used by another process.

解决方案:Solution:

停止 IIS Express 中的站点。Stop the site in IIS Express. 请转到 Windows 系统任务栏中,找到 IIS Express 并右键单击其图标、 选择 Contoso 大学站点,然后单击 停止站点Go to the Windows System Tray, find IIS Express and right-click its icon, select the Contoso University site, and then click Stop Site.

迁移基架的 Up 和 Down 方法中没有代码Migration scaffolded with no code in Up and Down methods

可能的原因:Possible cause:

EF CLI 命令不会自动关闭并保存代码文件。The EF CLI commands don't automatically close and save code files. 如果在运行migrations add命令时,你未保存更改,EF 将找不到所做的更改。If you have unsaved changes when you run the migrations add command, EF won't find your changes.

解决方案:Solution:

运行migrations remove命令,保存你更改的代码并重新运行migrations add命令。Run the migrations remove command, save your code changes and rerun the migrations add command.

运行数据库更新时出错Errors while running database update

在有数据的数据库中进行架构更改时,很有可能发生其他错误。It's possible to get other errors when making schema changes in a database that has existing data. 如果遇到无法解决的迁移错误,你可以更改连接字符串中的数据库名称,或删除数据库。If you get migration errors you can't resolve, you can either change the database name in the connection string or delete the database. 若要迁移,创建新的数据库,在数据库尚没有数据时使用更新数据库命令更有望完成且不发生错误。With a new database, there's no data to migrate, and the update-database command is much more likely to complete without errors.

最简单方法是在 appsettings.json 中重命名数据库。The simplest approach is to rename the database in appsettings.json. 下次运行database update时,会创建一个新数据库。The next time you run database update, a new database will be created.

若要在 SSOX 中删除数据库,右键单击数据库,单击 删除 ,然后在 删除数据库 对话框框中,选择 关闭现有连接 ,单击 确定To delete a database in SSOX, right-click the database, click Delete , and then in the Delete Database dialog box select Close existing connections and click OK.

若要使用 CLI 删除数据库,可以运行database dropCLI 命令:To delete a database by using the CLI, run the database drop CLI command:

dotnet ef database drop

定位 SQL Server 实例出错Error locating SQL Server instance

错误消息:Error Message:

建立到 SQL Server 的连接时出现与网络相关或特定于实例的错误。A network-related or instance-specific error occurred while establishing a connection to SQL Server. 找不到或无法访问服务器。The server was not found or was not accessible. 请验证实例名称是否正确,SQL Server 是否已配置为允许远程连接。Verify that the instance name is correct and that SQL Server is configured to allow remote connections. (提供程序:SQL 网络接口,错误:26 - 定位指定服务器/实例出错)(provider: SQL Network Interfaces, error: 26 - Error Locating Server/Instance Specified)

解决方案:Solution:

请检查连接字符串。Check the connection string. 如果你已手动删除数据库文件,更改数据库的构造字符串中数据库的名称,然后从头开始使用新的数据库。If you have manually deleted the database file, change the name of the database in the construction string to start over with a new database.

获取代码Get the code

下载或查看已完成的应用程序。Download or view the completed application.

其他资源Additional resources

有关 EF Core 的详细信息,请参阅 Entity Framework 的Core文档For more information about EF Core, see the Entity Framework Core documentation. 还提供:Entity Framework Core 实战一书。A book is also available: Entity Framework Core in Action.

有关如何部署 Web 应用的信息,请参阅 托管和部署 ASP.NET CoreFor information on how to deploy a web app, see 托管和部署 ASP.NET Core.

有关与 ASP.NET Core MVC 相关的其他主题(如身份验证与授权)的信息,请参阅 ASP.NET Core 简介For information about other topics related to ASP.NET Core MVC, such as authentication and authorization, see ASP.NET Core 简介.

后续步骤Next steps

在本教程中,你将了解:In this tutorial, you:

  • 已执行原始 SQL 查询Performed raw SQL queries
  • 已调用查询以返回实体Called a query to return entities
  • 已调用查询以返回其他类型Called a query to return other types
  • 已调用更新查询Called an update query
  • 已检查 SQL 查询Examined SQL queries
  • 已创建抽象层Created an abstraction layer
  • 已了解自动更改检测Learned about Automatic change detection
  • 已了解 EF Core 源代码与开发计划Learned about EF Core source code and development plans
  • 已了解如何使用动态 LINQ 简化代码Learned how to use dynamic LINQ to simplify code

这将完成在 ASP.NET Core MVC 应用程序中使用 Entity Framework Core 这一系列教程。This completes this series of tutorials on using the Entity Framework Core in an ASP.NET Core MVC application. 本系列使用的是新建数据库;另一种方式是从现有数据库进行模型的反向工程。This series worked with a new database; an alternative is to reverse engineer a model from an existing database.