{"id":595,"date":"2025-04-05T11:38:13","date_gmt":"2025-04-05T03:38:13","guid":{"rendered":"https:\/\/www.hyy.net\/?p=595"},"modified":"2025-04-05T11:38:13","modified_gmt":"2025-04-05T03:38:13","slug":"asp-net-core-in-action-12-saving-data-with-entity-framework-core","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=595","title":{"rendered":"ASP.NET Core in Action 12 Saving data with Entity Framework Core"},"content":{"rendered":"<p>12 Saving data with Entity Framework Core<br \/>\n12 \u4f7f\u7528 Entity Framework Core \u4fdd\u5b58\u6570\u636e<\/p>\n<h2>This chapter covers<\/h2>\n<h2>\u672c\u7ae0\u6db5\u76d6<\/h2>\n<ul>\n<li>\n<p>Understanding what Entity Framework Core is and why you should use it<br \/>\n\u4e86\u89e3\u4ec0\u4e48\u662f Entity Framework Core \u4ee5\u53ca\u4e3a\u4ec0\u4e48\u5e94\u8be5\u4f7f\u7528\u5b83<\/p>\n<\/li>\n<li>\n<p>Adding Entity Framework Core to an ASP.NET Core application<br \/>\n\u5c06 Entity Framework Core \u6dfb\u52a0\u5230 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f<\/p>\n<\/li>\n<li>\n<p>Building a data model and using it to create a database<br \/>\n\u6784\u5efa\u6570\u636e\u6a21\u578b\u5e76\u4f7f\u7528\u5b83\u6765\u521b\u5efa\u6570\u636e\u5e93<\/p>\n<\/li>\n<li>\n<p>Querying, creating, and updating data with Entity Framework Core<br \/>\n\u4f7f\u7528 Entity Framework Core \u67e5\u8be2\u3001\u521b\u5efa\u548c\u66f4\u65b0\u6570\u636e<\/p>\n<\/li>\n<\/ul>\n<p>Most applications that you\u2019ll build with ASP.NET Core require storing and loading some kind of data. Even the examples so far in this book have assumed that you have some sort of data store\u2014storing exchange rates, user shopping carts, or the locations of physical stores. I\u2019ve glossed over this topic for the most part, but typically you\u2019ll store this data in a database.<\/p>\n<p>\u60a8\u5c06\u4f7f\u7528 ASP.NET Core \u6784\u5efa\u7684\u5927\u591a\u6570\u5e94\u7528\u7a0b\u5e8f\u90fd\u9700\u8981\u5b58\u50a8\u548c\u52a0\u8f7d\u67d0\u79cd\u7c7b\u578b\u7684\u6570\u636e\u3002\u5373\u4f7f\u662f\u672c\u4e66\u4e2d\u5230\u76ee\u524d\u4e3a\u6b62\u7684\u793a\u4f8b\u4e5f\u5047\u5b9a\u60a8\u6709\u67d0\u79cd\u6570\u636e\u5b58\u50a8 \u2014 \u5b58\u50a8\u6c47\u7387\u3001\u7528\u6237\u8d2d\u7269\u8f66\u6216\u5b9e\u4f53\u5546\u5e97\u7684\u4f4d\u7f6e\u3002\u6211\u5927\u90e8\u5206\u65f6\u95f4\u90fd\u5ffd\u7565\u4e86\u8fd9\u4e2a\u4e3b\u9898\uff0c\u4f46\u901a\u5e38\u60a8\u4f1a\u5c06\u6b64\u6570\u636e\u5b58\u50a8\u5728\u6570\u636e\u5e93\u4e2d\u3002<\/p>\n<p>Working with databases can be a rather cumbersome process. You have to manage connections to the database, translate data from your application to a format the database can understand, and handle a plethora of other subtle problems. You can manage this complexity in a variety of ways, but I\u2019m going to focus on using a library built for modern .NET: Entity Framework Core (EF Core). EF Core is a library that lets you quickly and easily build database access code for your ASP.NET Core applications. It\u2019s modeled on the popular Entity Framework 6.x library, but it has significant changes that make it stand alone in its own right as more than an upgrade.<\/p>\n<p>\u4f7f\u7528\u6570\u636e\u5e93\u53ef\u80fd\u662f\u4e00\u4e2a\u76f8\u5f53\u7e41\u7410\u7684\u8fc7\u7a0b\u3002\u60a8\u5fc5\u987b\u7ba1\u7406\u4e0e\u6570\u636e\u5e93\u7684\u8fde\u63a5\uff0c\u5c06\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6570\u636e\u8f6c\u6362\u4e3a\u6570\u636e\u5e93\u53ef\u4ee5\u7406\u89e3\u7684\u683c\u5f0f\uff0c\u5e76\u5904\u7406\u5927\u91cf\u5176\u4ed6\u7ec6\u5fae\u7684\u95ee\u9898\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u591a\u79cd\u65b9\u5f0f\u7ba1\u7406\u8fd9\u79cd\u590d\u6742\u6027\uff0c\u4f46\u6211\u5c06\u91cd\u70b9\u4ecb\u7ecd\u5982\u4f55\u4f7f\u7528\u4e3a\u73b0\u4ee3 .NET \u6784\u5efa\u7684\u5e93\uff1aEntity Framework Core \uff08EF Core\uff09\u3002EF Core \u662f\u4e00\u4e2a\u5e93\uff0c\u53ef\u8ba9\u60a8\u5feb\u901f\u8f7b\u677e\u5730\u4e3a ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u6784\u5efa\u6570\u636e\u5e93\u8bbf\u95ee\u4ee3\u7801\u3002\u5b83\u4ee5\u6d41\u884c\u7684 Entity Framework 6.x \u5e93\u4e3a\u6a21\u578b\uff0c\u4f46\u5b83\u5177\u6709\u91cd\u5927\u53d8\u5316\uff0c\u4f7f\u5176\u672c\u8eab\u4e0d\u4ec5\u4ec5\u662f\u5347\u7ea7\u3002<\/p>\n<p>The aim of this chapter is to provide a quick overview of EF Core and show how you can use it in your applications to query and save to a database quickly. You\u2019ll learn enough to connect your app to a database and manage schema changes to the database, but I won\u2019t be going into great depth on any topics.<\/p>\n<p>\u672c\u7ae0\u65e8\u5728\u63d0\u4f9b EF Core \u7684\u5feb\u901f\u6982\u8ff0\uff0c\u5e76\u6f14\u793a\u5982\u4f55\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u5b83\u6765\u5feb\u901f\u67e5\u8be2\u548c\u4fdd\u5b58\u5230\u6570\u636e\u5e93\u3002\u60a8\u5c06\u5b66\u5230\u8db3\u591f\u7684\u77e5\u8bc6\u6765\u5c06\u5e94\u7528\u7a0b\u5e8f\u8fde\u63a5\u5230\u6570\u636e\u5e93\u5e76\u7ba1\u7406\u6570\u636e\u5e93\u7684\u67b6\u6784\u66f4\u6539\uff0c\u4f46\u6211\u4e0d\u4f1a\u6df1\u5165\u8ba8\u8bba\u4efb\u4f55\u4e3b\u9898\u3002<\/p>\n<p><strong>Note<\/strong> For an in-depth look at EF Core, I recommend Entity Framework Core in Action, 2nd ed., by Jon P. Smith (Manning, 2021). Alternatively, you can read about EF Core on the Microsoft documentation website at <a href=\"https:\/\/docs.microsoft.com\/ef\/core\">https:\/\/docs.microsoft.com\/ef\/core<\/a>.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u8981\u6df1\u5165\u4e86\u89e3 EF Core\uff0c\u6211\u63a8\u8350 Jon P. Smith \u7684 Entity Framework Core in Action\uff0c\u7b2c 2 \u7248\uff08Manning\uff0c2021 \u5e74\uff09\u3002\u6216\u8005\uff0c\u60a8\u53ef\u4ee5\u5728 <a href=\"https:\/\/docs.microsoft.com\/ef\/core\">https:\/\/docs.microsoft.com\/ef\/core<\/a> Microsoft \u6587\u6863\u7f51\u7ad9\u4e0a\u9605\u8bfb\u6709\u5173 EF Core \u7684\u4fe1\u606f\u3002<\/p>\n<p>Section 12.1 introduces EF Core and explains why you may want to use it in your applications. You\u2019ll learn how the design of EF Core helps you iterate quickly on your database structure and reduce the friction of interacting with a database.<\/p>\n<p>\u7b2c 12.1 \u8282\u4ecb\u7ecd\u4e86 EF Core\uff0c\u5e76\u89e3\u91ca\u4e86\u4e3a\u4ec0\u4e48\u4f60\u53ef\u80fd\u60f3\u8981\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u5b83\u3002\u4f60\u5c06\u4e86\u89e3 EF Core \u7684\u8bbe\u8ba1\u5982\u4f55\u5e2e\u52a9\u4f60\u5feb\u901f\u8fed\u4ee3\u6570\u636e\u5e93\u7ed3\u6784\u5e76\u51cf\u5c11\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u7684\u6469\u64e6\u3002<\/p>\n<p>In section 12.2 you\u2019ll learn how to add EF Core to an ASP.NET Core app and configure it by using the ASP.NET Core configuration system. You\u2019ll see how to build a model for your app that represents the data you\u2019ll store in the database and how to hook it into the ASP.NET Core DI container.<\/p>\n<p>\u5728\u7b2c 12.2 \u8282\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u5c06 EF Core \u6dfb\u52a0\u5230 ASP.NET Core \u5e94\u7528\uff0c\u5e76\u4f7f\u7528 ASP.NET Core \u914d\u7f6e\u7cfb\u7edf\u5bf9\u5176\u8fdb\u884c\u914d\u7f6e\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u4e3a\u5e94\u7528\u7a0b\u5e8f\u6784\u5efa\u4e00\u4e2a\u6a21\u578b\uff0c\u8be5\u6a21\u578b\u8868\u793a\u60a8\u5c06\u5b58\u50a8\u5728\u6570\u636e\u5e93\u4e2d\u7684\u6570\u636e\uff0c\u4ee5\u53ca\u5982\u4f55\u5c06\u5176\u6302\u63a5\u5230 ASP.NET Core DI \u5bb9\u5668\u4e2d\u3002<\/p>\n<p><strong>Note<\/strong> For this chapter I use SQLite, a small, fast, cross-platform database engine, but none of the code shown in this chapter is specific to SQLite. The code sample for the book also includes a version using SQL Server Express\u2019s LocalDB feature. This version is installed as part of Visual Studio 2022 (when you choose the ASP.NET and Web Development workload), and it provides a lightweight SQL Server engine. You can read more about LocalDB at <a href=\"http:\/\/mng.bz\/5jEa\">http:\/\/mng.bz\/5jEa<\/a>.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4f7f\u7528\u4e86 SQLite\uff0c\u8fd9\u662f\u4e00\u4e2a\u5c0f\u578b\u3001\u5feb\u901f\u3001\u8de8\u5e73\u53f0\u7684\u6570\u636e\u5e93\u5f15\u64ce\uff0c\u4f46\u672c\u7ae0\u4e2d\u5c55\u793a\u7684\u4ee3\u7801\u90fd\u4e0d\u662f\u7279\u5b9a\u4e8e SQLite \u7684\u3002\u8be5\u4e66\u7684\u4ee3\u7801\u793a\u4f8b\u8fd8\u5305\u62ec\u4f7f\u7528 SQL Server Express \u7684 LocalDB \u529f\u80fd\u7684\u7248\u672c\u3002\u6b64\u7248\u672c\u4f5c\u4e3a Visual Studio 2022 \u7684\u4e00\u90e8\u5206\u5b89\u88c5\uff08\u5f53\u60a8\u9009\u62e9 ASP.NET \u548c Web \u5f00\u53d1\u5de5\u4f5c\u8d1f\u8f7d\u65f6\uff09\uff0c\u5b83\u63d0\u4f9b\u8f7b\u91cf\u7ea7 SQL Server \u5f15\u64ce\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/5jEa\">http:\/\/mng.bz\/5jEa<\/a> \u4e0a\u9605\u8bfb\u6709\u5173 LocalDB \u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<p>No matter how carefully you design your original data model, the time will come when you need to change it. In section 12.3 I show how you can easily update your model and apply these changes to the database itself, using EF Core for all the heavy lifting.<\/p>\n<p>\u65e0\u8bba\u60a8\u591a\u4e48\u4ed4\u7ec6\u5730\u8bbe\u8ba1\u539f\u59cb\u6570\u636e\u6a21\u578b\uff0c\u90fd\u9700\u8981\u66f4\u6539\u5b83\u3002\u5728\u90e8\u520612.3 \u6211\u5c06\u5c55\u793a\u5982\u4f55\u8f7b\u677e\u66f4\u65b0\u6a21\u578b\u5e76\u5c06\u8fd9\u4e9b\u66f4\u6539\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u672c\u8eab\uff0c\u4ece\u800c\u4f7f\u7528 EF Core \u5b8c\u6210\u6240\u6709\u7e41\u91cd\u7684\u5de5\u4f5c\u3002<\/p>\n<p>When you have EF Core configured and a database created, section 12.4 shows how to use it in your application code. You\u2019ll see how to create, read, update, and delete (CRUD) records, and you\u2019ll learn about some of the patterns to use when designing your data access.<\/p>\n<p>\u914d\u7f6e EF Core \u5e76\u521b\u5efa\u6570\u636e\u5e93\u540e\uff0c\u7b2c 12.4 \u8282\u4ecb\u7ecd\u4e86\u5982\u4f55\u5728\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u4e2d\u4f7f\u7528\u5b83\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u521b\u5efa\u3001\u8bfb\u53d6\u3001\u66f4\u65b0\u548c\u5220\u9664 \uff08CRUD\uff09 \u8bb0\u5f55\uff0c\u5e76\u4e86\u89e3\u5728\u8bbe\u8ba1\u6570\u636e\u8bbf\u95ee\u65f6\u8981\u4f7f\u7528\u7684\u4e00\u4e9b\u6a21\u5f0f\u3002<\/p>\n<p>In section 12.5 I highlight a few of the problems you\u2019ll want to take into consideration when using EF Core in a production app. A single chapter on EF Core can offer only a brief introduction to all the related concepts, so if you choose to use EF Core in your own applications\u2014especially if you\u2019re using such a data access library for the first time\u2014I strongly recommend reading more after you have the basics from this chapter.<\/p>\n<p>\u5728\u7b2c 12.5 \u8282\u4e2d\uff0c\u6211\u91cd\u70b9\u4ecb\u7ecd\u4e86\u5728\u751f\u4ea7\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 EF Core \u65f6\u9700\u8981\u8003\u8651\u7684\u4e00\u4e9b\u95ee\u9898\u3002EF Core \u7684\u4e00\u7ae0\u53ea\u80fd\u7b80\u8981\u4ecb\u7ecd\u6240\u6709\u76f8\u5173\u6982\u5ff5\uff0c\u56e0\u6b64\uff0c\u5982\u679c\u60a8\u9009\u62e9\u5728\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 EF Core\uff08\u5c24\u5176\u662f\u60a8\u662f\u7b2c\u4e00\u6b21\u4f7f\u7528\u6b64\u7c7b\u6570\u636e\u8bbf\u95ee\u5e93\uff09\uff0c\u6211\u5f3a\u70c8\u5efa\u8bae\u60a8\u5728\u4e86\u89e3\u672c\u7ae0\u7684\u57fa\u7840\u77e5\u8bc6\u540e\u518d\u9605\u8bfb\u66f4\u591a\u5185\u5bb9\u3002<\/p>\n<p>Before we get into any code, let\u2019s look at what EF Core is, what problems it solves, and when you may want to use it.<\/p>\n<p>\u5728\u5f00\u59cb\u4efb\u4f55\u4ee3\u7801\u4e4b\u524d\uff0c\u8ba9\u6211\u4eec\u770b\u770b EF Core \u662f\u4ec0\u4e48\uff0c\u5b83\u89e3\u51b3\u4e86\u4ec0\u4e48\u95ee\u9898\uff0c\u4ee5\u53ca\u4f55\u65f6\u53ef\u80fd\u9700\u8981\u4f7f\u7528\u5b83\u3002<\/p>\n<h2>12.1 Introducing Entity Framework Core<\/h2>\n<h2>12.1 Entity Framework Core \u7b80\u4ecb<\/h2>\n<p>Database access code is ubiquitous across web applications. Whether you\u2019re building an e-commerce app, a blog, or the Next Big Thing\u2122, chances are that you\u2019ll need to interact with a database.<\/p>\n<p>\u6570\u636e\u5e93\u8bbf\u95ee\u4ee3\u7801\u5728 Web \u5e94\u7528\u7a0b\u5e8f\u4e2d\u65e0\u5904\u4e0d\u5728\u3002\u65e0\u8bba\u60a8\u662f\u6784\u5efa\u7535\u5b50\u5546\u52a1\u5e94\u7528\u7a0b\u5e8f\u3001\u535a\u5ba2\u8fd8\u662f Next Big Thing\u2122\uff0c\u60a8\u90fd\u6709\u53ef\u80fd\u9700\u8981\u4e0e\u6570\u636e\u5e93\u8fdb\u884c\u4ea4\u4e92\u3002<\/p>\n<p>Unfortunately, interacting with databases from app code is often a messy affair, and you can take many approaches. A task as simple as reading data from a database, for example, requires handling network connections, writing SQL statements, and handling variable result data. The .NET ecosystem has a whole array of libraries you can use for this task, ranging from the low-level ADO.NET libraries to higher-level abstractions such as EF Core.<\/p>\n<p>\u9057\u61be\u7684\u662f\uff0c\u4ece\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u901a\u5e38\u662f\u4e00\u4ef6\u9ebb\u70e6\u7684\u4e8b\u60c5\uff0c\u60a8\u53ef\u4ee5\u91c7\u7528\u591a\u79cd\u65b9\u6cd5\u3002\u4f8b\u5982\uff0c\u50cf\u4ece\u6570\u636e\u5e93\u8bfb\u53d6\u6570\u636e\u8fd9\u6837\u7b80\u5355\u7684\u4efb\u52a1\u9700\u8981\u5904\u7406\u7f51\u7edc\u8fde\u63a5\u3001\u7f16\u5199 SQL \u8bed\u53e5\u548c\u5904\u7406\u53ef\u53d8\u7ed3\u679c\u6570\u636e\u3002.NET \u751f\u6001\u7cfb\u7edf\u5177\u6709\u53ef\u7528\u4e8e\u6b64\u4efb\u52a1\u7684\u4e00\u6574\u5957\u5e93\uff0c\u4ece\u4f4e\u7ea7 ADO.NET \u5e93\u5230\u9ad8\u7ea7\u62bd\u8c61\uff08\u5982 EF Core\uff09\u3002<\/p>\n<p>In this section, I describe what EF Core is and the problem it\u2019s designed to solve. I cover the motivation for using an abstraction such as EF Core and how it helps bridge the gap between your app code and your database. As part of that discussion, I present some of the tradeoffs you\u2019ll make by using EF Core in your apps, which should help you decide whether it\u2019s right for your purposes. Finally, we\u2019ll take a look at an example EF Core mapping, from app code to database, to get a feel for EF Core\u2019s main concepts.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd\u4ec0\u4e48\u662f EF Core \u4ee5\u53ca\u5b83\u65e8\u5728\u89e3\u51b3\u7684\u95ee\u9898\u3002\u6211\u5c06\u4ecb\u7ecd\u4f7f\u7528 EF Core \u7b49\u62bd\u8c61\u7684\u52a8\u673a\uff0c\u4ee5\u53ca\u5b83\u5982\u4f55\u5e2e\u52a9\u5f25\u5408\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u548c\u6570\u636e\u5e93\u4e4b\u95f4\u7684\u5dee\u8ddd\u3002\u4f5c\u4e3a\u8be5\u8ba8\u8bba\u7684\u4e00\u90e8\u5206\uff0c\u6211\u5c06\u4ecb\u7ecd\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 EF Core \u5c06\u505a\u51fa\u7684\u4e00\u4e9b\u6743\u8861\uff0c\u8fd9\u5e94\u8be5\u6709\u52a9\u4e8e\u60a8\u786e\u5b9a\u5b83\u662f\u5426\u9002\u5408\u60a8\u7684\u76ee\u7684\u3002\u6700\u540e\uff0c\u6211\u4eec\u5c06\u67e5\u770b\u4ece\u5e94\u7528\u4ee3\u7801\u5230\u6570\u636e\u5e93\u7684 EF Core \u6620\u5c04\u793a\u4f8b\uff0c\u4ee5\u4e86\u89e3 EF Core \u7684\u4e3b\u8981\u6982\u5ff5\u3002<\/p>\n<h3>12.1.1 What is EF Core?<\/h3>\n<h3>12.1.1 \u4ec0\u4e48\u662f EF Core\uff1f<\/h3>\n<p>EF Core is a library that provides an object-oriented way to access databases. It acts as an object-relational mapper (ORM), communicating with the database for you and mapping database responses to .NET classes and objects, as shown in figure 12.1.<\/p>\n<p>EF Core \u662f\u4e00\u4e2a\u5e93\uff0c\u5b83\u63d0\u4f9b\u4e86\u4e00\u79cd\u9762\u5411\u5bf9\u8c61\u7684\u65b9\u5f0f\u6765\u8bbf\u95ee\u6570\u636e\u5e93\u3002\u5b83\u5145\u5f53\u5bf9\u8c61\u5173\u7cfb\u6620\u5c04\u5668 \uff08ORM\uff09\uff0c\u4e3a\u60a8\u4e0e\u6570\u636e\u5e93\u901a\u4fe1\uff0c\u5e76\u5c06\u6570\u636e\u5e93\u54cd\u5e94\u6620\u5c04\u5230 .NET \u7c7b\u548c\u5bf9\u8c61\uff0c\u5982\u56fe 12.1 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1201.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.1 EF Core maps .NET classes and objects to database concepts such as tables and rows.<br \/>\n\u56fe 12.1 EF Core \u5c06 .NET \u7c7b\u548c\u5bf9\u8c61\u6620\u5c04\u5230\u6570\u636e\u5e93\u6982\u5ff5\uff0c\u4f8b\u5982\u8868\u548c\u884c\u3002<\/p>\n<p><strong>Definition<\/strong> With an object-relational mapper (ORM), you can manipulate a database with object-oriented concepts such as classes and objects by mapping them to database concepts such as tables and columns.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u4f7f\u7528\u5bf9\u8c61\u5173\u7cfb\u6620\u5c04\u5668 \uff08ORM\uff09\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u9762\u5411\u5bf9\u8c61\u7684\u6982\u5ff5\u6765\u4f5c\u6570\u636e\u5e93,\u4f8b\u5982\u7c7b\u548c\u5bf9\u8c61\uff0c\u65b9\u6cd5\u662f\u5c06\u5b83\u4eec\u6620\u5c04\u5230\u8868\u548c\u5217\u7b49\u6570\u636e\u5e93\u6982\u5ff5\u3002<\/p>\n<p>EF Core is based on but distinct from the existing Entity Framework libraries (currently up to version 6.x). It was built as part of the .NET Core push to work cross-platform, but with additional goals in mind. In particular, the EF Core team wanted to make a highly performant library that could be used with a wide range of databases.<\/p>\n<p>EF Core \u57fa\u4e8e\u73b0\u6709\u7684\u5b9e\u4f53\u6846\u67b6\u5e93\uff08\u5f53\u524d\u6700\u9ad8\u7248\u672c 6.x\uff09\uff0c\u4f46\u4e0d\u540c\u4e8e\u73b0\u6709\u5b9e\u4f53\u6846\u67b6\u5e93\u3002\u5b83\u662f\u4f5c\u4e3a .NET Core \u8de8\u5e73\u53f0\u63a8\u52a8\u5de5\u4f5c\u7684\u4e00\u90e8\u5206\u6784\u5efa\u7684\uff0c\u4f46\u8003\u8651\u4e86\u5176\u4ed6\u76ee\u6807\u3002\u5177\u4f53\u800c\u8a00\uff0cEF Core \u56e2\u961f\u5e0c\u671b\u5236\u4f5c\u4e00\u4e2a\u53ef\u4e0e\u5404\u79cd\u6570\u636e\u5e93\u4e00\u8d77\u4f7f\u7528\u7684\u9ad8\u6027\u80fd\u5e93\u3002<\/p>\n<p>There are many types of databases, but probably the most commonly used family is relational databases, accessed via Structured Query Language (SQL). This is the bread and butter of EF Core; it can map Microsoft SQL Server, SQLite, MySQL, Postgres, and many other relational databases. It even has a cool in-memory feature you can use when testing to create a temporary database. EF Core uses a provider model, so support for other relational databases can be plugged in later as they become available.<\/p>\n<p>\u6570\u636e\u5e93\u6709\u5f88\u591a\u7c7b\u578b\uff0c\u4f46\u6700\u5e38\u7528\u7684\u7cfb\u5217\u53ef\u80fd\u662f\u5173\u7cfb\u6570\u636e\u5e93\uff0c\u901a\u8fc7\u7ed3\u6784\u5316\u67e5\u8be2\u8bed\u8a00 \uff08SQL\uff09 \u8bbf\u95ee\u3002\u8fd9\u662f EF Core \u7684\u751f\u8ba1;\u5b83\u53ef\u4ee5\u6620\u5c04 Microsoft SQL Server\u3001SQLite\u3001MySQL\u3001Postgres \u548c\u8bb8\u591a\u5176\u4ed6\u5173\u7cfb\u6570\u636e\u5e93\u3002\u5b83\u751a\u81f3\u8fd8\u6709\u4e00\u4e2a\u5f88\u9177\u7684\u5185\u5b58\u529f\u80fd\uff0c\u60a8\u53ef\u4ee5\u5728\u6d4b\u8bd5\u521b\u5efa\u4e34\u65f6\u6570\u636e\u5e93\u65f6\u4f7f\u7528\u3002EF Core \u4f7f\u7528\u63d0\u4f9b\u7a0b\u5e8f\u6a21\u578b\uff0c\u56e0\u6b64\u53ef\u4ee5\u5728\u5176\u4ed6\u5173\u7cfb\u6570\u636e\u5e93\u53ef\u7528\u65f6\u63d2\u5165\u5bf9\u5b83\u4eec\u7684\u652f\u6301\u3002<\/p>\n<p><strong>Note<\/strong> As of .NET Core 3.0, EF Core also works with nonrelational, NoSQL, or document databases like Cosmos DB too. I\u2019m going to consider mapping only to relational databases in this book, however, as that\u2019s the most common requirement in my experience. Historically, most data access, especially in the .NET ecosystem, has used relational databases, so it generally remains the most popular approach.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u4ece .NET Core 3.0 \u5f00\u59cb\uff0cEF Core \u4e5f\u9002\u7528\u4e8e\u975e\u5173\u7cfb\u6570\u636e\u5e93\u3001NoSQL \u6570\u636e\u5e93\u6216 Cosmos DB \u7b49\u6587\u6863\u6570\u636e\u5e93\u3002\u4f46\u662f\uff0c\u5728\u672c\u4e66\u4e2d\uff0c\u6211\u5c06\u53ea\u8003\u8651\u6620\u5c04\u5230\u5173\u7cfb\u6570\u636e\u5e93\uff0c\u56e0\u4e3a\u8fd9\u662f\u6211\u7ecf\u9a8c\u4e2d\u6700\u5e38\u89c1\u7684\u8981\u6c42\u3002\u4ece\u5386\u53f2\u4e0a\u770b\uff0c\u5927\u591a\u6570\u6570\u636e\u8bbf\u95ee\uff08\u5c24\u5176\u662f\u5728 .NET \u751f\u6001\u7cfb\u7edf\u4e2d\uff09\u90fd\u4f7f\u7528\u5173\u7cfb\u6570\u636e\u5e93\uff0c\u56e0\u6b64\u5b83\u901a\u5e38\u4ecd\u7136\u662f\u6700\u6d41\u884c\u7684\u65b9\u6cd5\u3002<\/p>\n<p>That discussion covers what EF Core is but doesn\u2019t dig into why you\u2019d want to use it. Why not access the database directly by using the traditional ADO.NET libraries? Most of the arguments for using EF Core can be applied to ORMs in general, so what are the advantages of an ORM?<\/p>\n<p>\u8be5\u8ba8\u8bba\u6db5\u76d6\u4e86 EF Core \u662f\u4ec0\u4e48\uff0c\u4f46\u5e76\u672a\u6df1\u5165\u63a2\u8ba8\u4f60\u4e3a\u4f55\u8981\u4f7f\u7528\u5b83\u3002\u4e3a\u4ec0\u4e48\u4e0d\u4f7f\u7528\u4f20\u7edf\u7684 ADO.NET \u5e93\u76f4\u63a5\u8bbf\u95ee\u6570\u636e\u5e93\u5462\uff1f\u5927\u90e8\u5206\u4f7f\u7528 EF Core \u7684\u8bba\u70b9\u901a\u5e38\u53ef\u4ee5\u5e94\u7528\u4e8e ORM\uff0c\u90a3\u4e48 ORM \u6709\u54ea\u4e9b\u4f18\u52bf\u5462\uff1f<\/p>\n<h3>12.1.2 Why use an object-relational mapper?<\/h3>\n<h3>12.1.2 \u4e3a\u4ec0\u4e48\u4f7f\u7528\u5bf9\u8c61\u5173\u7cfb\u6620\u5c04\u5668\uff1f<\/h3>\n<p>One of the biggest advantages of an ORM is the speed with which it allows you to develop an application. You can stay in the familiar territory of object-oriented .NET, often without needing to manipulate a database directly or write custom SQL.<\/p>\n<p>ORM \u7684\u6700\u5927\u4f18\u52bf\u4e4b\u4e00\u662f\u5b83\u5141\u8bb8\u60a8\u5f00\u53d1\u5e94\u7528\u7a0b\u5e8f\u7684\u901f\u5ea6\u3002\u60a8\u53ef\u4ee5\u505c\u7559\u5728\u719f\u6089\u7684\u9762\u5411\u5bf9\u8c61\u7684 .NET \u9886\u57df\uff0c\u901a\u5e38\u4e0d\u9700\u8981\u76f4\u63a5\u4f5c\u6570\u636e\u5e93\u6216\u7f16\u5199\u81ea\u5b9a\u4e49 SQL\u3002<\/p>\n<p>Suppose that you have an e-commerce site, and you want to load the details of a product from the database. Using low-level database access code, you\u2019d have to open a connection to the database; write the necessary SQL with the correct table and column names; read the data over the connection; create a plain old CLR object (POCO) to hold the data; and set the properties on the object, converting the data to the correct format manually as you go. Sounds painful, right?<\/p>\n<p>\u5047\u8bbe\u60a8\u6709\u4e00\u4e2a\u7535\u5b50\u5546\u52a1\u7f51\u7ad9\uff0c\u5e76\u4e14\u60a8\u5e0c\u671b\u4ece\u6570\u636e\u5e93\u4e2d\u52a0\u8f7d\u4ea7\u54c1\u7684\u8be6\u7ec6\u4fe1\u606f\u3002\u4f7f\u7528\u4f4e\u7ea7\u6570\u636e\u5e93\u8bbf\u95ee\u4ee3\u7801\uff0c\u60a8\u5fc5\u987b\u6253\u5f00\u4e0e\u6570\u636e\u5e93\u7684\u8fde\u63a5;\u4f7f\u7528\u6b63\u786e\u7684\u8868\u548c\u5217\u540d\u79f0\u7f16\u5199\u5fc5\u8981\u7684 SQL;\u901a\u8fc7\u8fde\u63a5\u8bfb\u53d6\u6570\u636e;\u521b\u5efa\u4e00\u4e2a\u666e\u901a\u7684\u65e7 CLR \u5bf9\u8c61 \uff08POCO\uff09 \u6765\u4fdd\u5b58\u6570\u636e;\u5e76\u8bbe\u7f6e\u5bf9\u8c61\u7684\u5c5e\u6027\uff0c\u5e76\u968f\u65f6\u624b\u52a8\u5c06\u6570\u636e\u8f6c\u6362\u4e3a\u6b63\u786e\u7684\u683c\u5f0f\u3002\u542c\u8d77\u6765\u5f88\u75db\u82e6\uff0c\u5bf9\u5427\uff1f<\/p>\n<p>An ORM such as EF Core takes care of most of this work for you. It handles the connection to the database, generates the SQL, and maps data back to your POCO objects. All you need to provide is a LINQ query describing the data you want to retrieve.<\/p>\n<p>EF Core \u7b49 ORM \u4f1a\u4e3a\u4f60\u5904\u7406\u5927\u90e8\u5206\u5de5\u4f5c\u3002\u5b83\u5904\u7406\u4e0e\u6570\u636e\u5e93\u7684\u8fde\u63a5\uff0c\u751f\u6210 SQL\uff0c\u5e76\u5c06\u6570\u636e\u6620\u5c04\u56de POCO \u5bf9\u8c61\u3002\u60a8\u53ea\u9700\u63d0\u4f9b\u4e00\u4e2a LINQ \u67e5\u8be2\uff0c\u63cf\u8ff0\u60a8\u8981\u68c0\u7d22\u7684\u6570\u636e\u3002<\/p>\n<p>ORMs serve as high-level abstractions over databases, so they can significantly reduce the amount of plumbing code you need to write to interact with a database. At the most basic level, they take care of mapping SQL statements to objects, and vice versa, but most ORMs take this process a step further and provide additional features.<\/p>\n<p>ORM \u5145\u5f53\u6570\u636e\u5e93\u7684\u9ad8\u7ea7\u62bd\u8c61\uff0c\u56e0\u6b64\u5b83\u4eec\u53ef\u4ee5\u663e\u8457\u51cf\u5c11\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u6240\u9700\u7f16\u5199\u7684\u7ba1\u9053\u4ee3\u7801\u91cf\u3002\u5728\u6700\u57fa\u672c\u7684\u5c42\u9762\u4e0a\uff0c\u5b83\u4eec\u8d1f\u8d23\u5c06 SQL \u8bed\u53e5\u6620\u5c04\u5230\u5bf9\u8c61\uff0c\u53cd\u4e4b\u4ea6\u7136\uff0c\u4f46\u5927\u591a\u6570 ORM \u5c06\u6b64\u8fc7\u7a0b\u66f4\u8fdb\u4e00\u6b65\u5e76\u63d0\u4f9b\u4e86\u989d\u5916\u7684\u529f\u80fd\u3002<\/p>\n<p>ORMs like EF Core keep track of which properties have changed on any objects they retrieve from the database, which lets you load an object from the database by mapping it from a database table, modify it in .NET code, and then ask the ORM to update the associated record in the database. The ORM works out which properties have changed and issues update statements for the appropriate columns, saving you a bunch of work.<\/p>\n<p>EF Core \u7b49 ORM \u4f1a\u8ddf\u8e2a\u5b83\u4eec\u4ece\u6570\u636e\u5e93\u4e2d\u68c0\u7d22\u7684\u4efb\u4f55\u5bf9\u8c61\u4e0a\u7684\u54ea\u4e9b\u5c5e\u6027\u5df2\u66f4\u6539\uff0c\u8fd9\u5141\u8bb8\u60a8\u901a\u8fc7\u4ece\u6570\u636e\u5e93\u8868\u6620\u5c04\u5bf9\u8c61\u3001\u5728 .NET \u4ee3\u7801\u4e2d\u4fee\u6539\u5bf9\u8c61\uff0c\u7136\u540e\u8981\u6c42 ORM \u66f4\u65b0\u6570\u636e\u5e93\u4e2d\u7684\u5173\u8054\u8bb0\u5f55\u6765\u52a0\u8f7d\u6570\u636e\u5e93\u5bf9\u8c61\u3002ORM \u4f1a\u627e\u51fa\u54ea\u4e9b\u5c5e\u6027\u5df2\u66f4\u6539\uff0c\u5e76\u4e3a\u76f8\u5e94\u7684\u5217\u53d1\u51fa update \u8bed\u53e5\uff0c\u4ece\u800c\u4e3a\u60a8\u8282\u7701\u5927\u91cf\u5de5\u4f5c\u3002<\/p>\n<p>As is so often the case in software development, using an ORM has its drawbacks. One of the biggest advantages of ORMs is also their Achilles\u2019 heel: they hide the database from you. Sometimes this high level of abstraction can lead to problematic database query patterns in your apps. A classic example is the N+1 problem, in which what should be a single database request turns into separate requests for every single row in a database table.<\/p>\n<p>\u4e0e\u8f6f\u4ef6\u5f00\u53d1\u4e2d\u7ecf\u5e38\u51fa\u73b0\u7684\u60c5\u51b5\u4e00\u6837\uff0c\u4f7f\u7528 ORM \u4e5f\u6709\u5176\u7f3a\u70b9\u3002ORM \u7684\u6700\u5927\u4f18\u52bf\u4e4b\u4e00\u4e5f\u662f\u5b83\u4eec\u7684\u81f4\u547d\u5f31\u70b9\uff1a\u5b83\u4eec\u5bf9\u60a8\u9690\u85cf\u4e86\u6570\u636e\u5e93\u3002\u6709\u65f6\uff0c\u8fd9\u79cd\u9ad8\u7ea7\u522b\u7684\u62bd\u8c61\u53ef\u80fd\u4f1a\u5bfc\u81f4\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u51fa\u73b0\u6709\u95ee\u9898\u7684\u6570\u636e\u5e93\u67e5\u8be2\u6a21\u5f0f\u3002\u4e00\u4e2a\u5178\u578b\u7684\u4f8b\u5b50\u662f N+1 \u95ee\u9898\uff0c\u5728\u8fd9\u4e2a\u95ee\u9898\u4e2d\uff0c\u672c\u5e94\u662f\u5355\u4e2a\u6570\u636e\u5e93\u8bf7\u6c42\u7684\u5185\u5bb9\u53d8\u6210\u4e86\u5bf9\u6570\u636e\u5e93\u8868\u4e2d\u6bcf\u4e00\u884c\u7684\u5355\u72ec\u8bf7\u6c42\u3002<\/p>\n<p>Another commonly cited drawback is performance. ORMs are abstractions over several concepts, so they inherently do more work than if you were to handcraft every piece of data access in your app. Most ORMs, EF Core included, trade some degree of performance for ease of development.<\/p>\n<p>\u53e6\u4e00\u4e2a\u7ecf\u5e38\u88ab\u63d0\u53ca\u7684\u7f3a\u70b9\u662f\u6027\u80fd\u3002ORM \u662f\u591a\u4e2a\u6982\u5ff5\u7684\u62bd\u8c61\uff0c\u56e0\u6b64\u5b83\u4eec\u672c\u8d28\u4e0a\u6bd4\u60a8\u624b\u52a8\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6bcf\u4e00\u6761\u6570\u636e\u8bbf\u95ee\u8981\u591a\u3002\u5927\u591a\u6570 ORM\uff08\u5305\u62ec EF Core\uff09\u90fd\u4ee5\u67d0\u79cd\u7a0b\u5ea6\u7684\u6027\u80fd\u4e3a\u4ee3\u4ef7\u6765\u7b80\u5316\u5f00\u53d1\u3002<\/p>\n<p>That said, if you\u2019re aware of the pitfalls of ORMs, you can often drastically simplify the code required to interact with a database. As with anything, if the abstraction works for you, use it; otherwise, don\u2019t. If you have only minimal database access requirements or need the best performance you can get, an ORM such as EF Core may not be the right fit.<\/p>\n<p>\u4e5f\u5c31\u662f\u8bf4\uff0c\u5982\u679c\u60a8\u610f\u8bc6\u5230 ORM \u7684\u9677\u9631\uff0c\u60a8\u901a\u5e38\u53ef\u4ee5\u5927\u5927\u7b80\u5316\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u6240\u9700\u7684\u4ee3\u7801\u3002\u4e0e\u4efb\u4f55\u4e8b\u60c5\u4e00\u6837\uff0c\u5982\u679c\u62bd\u8c61\u5bf9\u60a8\u6709\u7528\uff0c\u8bf7\u4f7f\u7528\u5b83;\u5426\u5219\uff0c\u4e0d\u8981\u3002\u5982\u679c\u60a8\u53ea\u6709\u6700\u5c0f\u7684\u6570\u636e\u5e93\u8bbf\u95ee\u8981\u6c42\u6216\u9700\u8981\u60a8\u53ef\u4ee5\u83b7\u5f97\u7684\u6700\u4f73\u6027\u80fd\uff0c\u5219 EF Core \u7b49 ORM \u53ef\u80fd\u4e0d\u9002\u5408\u3002<\/p>\n<p>An alternative is to get the best of both worlds: use an ORM for the quick development of the bulk of your application, and fall back to lower-level APIs such as ADO.NET for those few areas that prove to be bottlenecks. That way, you can get good-enough performance with EF Core, trading performance for development time, and optimize only those areas that need it.<\/p>\n<p>\u53e6\u4e00\u79cd\u9009\u62e9\u662f\u4e24\u5168\u5176\u7f8e\uff1a\u4f7f\u7528 ORM \u5feb\u901f\u5f00\u53d1\u5927\u90e8\u5206\u5e94\u7528\u7a0b\u5e8f\uff0c\u5e76\u56de\u9000\u5230\u8f83\u4f4e\u7ea7\u522b\u7684 API\uff0c\u4f8b\u5982 ADO.NET \u7528\u4e8e\u90a3\u4e9b\u88ab\u8bc1\u660e\u662f\u74f6\u9888\u7684\u5c11\u6570\u9886\u57df\u3002\u8fd9\u6837\uff0c\u60a8\u5c31\u53ef\u4ee5\u4f7f\u7528 EF Core \u83b7\u5f97\u8db3\u591f\u597d\u7684\u6027\u80fd\uff0c\u4ee5\u727a\u7272\u5f00\u53d1\u65f6\u95f4\u6765\u6362\u53d6\u6027\u80fd\uff0c\u5e76\u4ec5\u4f18\u5316\u9700\u8981\u5b83\u7684\u9886\u57df\u3002<\/p>\n<p><strong>Note<\/strong> These days, the performance aspect is one of the weaker arguments against ORMs. EF Core uses many database tricks and crafts clean SQL queries, so unless you\u2019re a database expert, you may find that it outperforms even your handcrafted ADO.NET queries!<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5982\u4eca\uff0c\u6027\u80fd\u65b9\u9762\u662f\u53cd\u5bf9 ORM \u7684\u8f83\u5f31\u7684\u8bba\u70b9\u4e4b\u4e00\u3002EF Core \u4f7f\u7528\u8bb8\u591a\u6570\u636e\u5e93\u6280\u5de7\u5e76\u5236\u4f5c\u5e72\u51c0\u7684 SQL \u67e5\u8be2\uff0c\u56e0\u6b64\uff0c\u9664\u975e\u4f60\u662f\u6570\u636e\u5e93\u4e13\u5bb6\uff0c\u5426\u5219\u4f60\u53ef\u80fd\u4f1a\u53d1\u73b0\u5b83\u751a\u81f3\u4f18\u4e8e\u4f60\u624b\u5de5\u5236\u4f5c\u7684 ADO.NET \u67e5\u8be2\uff01<\/p>\n<p>Even if you decide to use an ORM in your app, many ORMs are available for .NET, of which EF Core is one. Whether EF Core is right for you depends on the features you need and the tradeoffs you\u2019re willing to make to get them. Section 12.1.3 compares EF Core with Microsoft\u2019s other offering, Entity Framework, but you could consider many other alternatives, such as Dapper and NHibernate, each of which has its own set of tradeoffs.<\/p>\n<p>\u5373\u4f7f\u60a8\u51b3\u5b9a\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 ORM\uff0c\u4e5f\u6709\u8bb8\u591a ORM \u53ef\u7528\u4e8e .NET\uff0cEF Core \u5c31\u662f\u5176\u4e2d\u4e4b\u4e00\u3002EF Core \u662f\u5426\u9002\u5408\u4f60\u53d6\u51b3\u4e8e\u4f60\u9700\u8981\u7684\u529f\u80fd\u4ee5\u53ca\u4f60\u613f\u610f\u4e3a\u83b7\u5f97\u8fd9\u4e9b\u529f\u80fd\u800c\u505a\u51fa\u7684\u6743\u8861\u300212.1.3\u90e8\u5206\u5c06 EF Core \u4e0e Microsoft \u7684\u5176\u4ed6\u4ea7\u54c1 Entity Framework \u8fdb\u884c\u6bd4\u8f83\uff0c\u4f46\u60a8\u53ef\u4ee5\u8003\u8651\u8bb8\u591a\u5176\u4ed6\u66ff\u4ee3\u65b9\u6848\uff0c\u4f8b\u5982 Dapper \u548c NHibernate\uff0c\u6bcf\u4e2a\u66ff\u4ee3\u65b9\u6848\u90fd\u6709\u81ea\u5df1\u7684\u4e00\u7ec4\u6743\u8861\u3002<\/p>\n<h3>12.1.3 When should you choose EF Core?<\/h3>\n<h3>12.1.3 \u4f55\u65f6\u5e94\u9009\u62e9 EF Core\uff1f<\/h3>\n<p>Microsoft designed EF Core as a reimagining of the mature Entity Framework 6.x (EF 6.x) ORM, which it released in 2008. With many years of development behind it, EF 6.x was a stable and feature-rich ORM, but it\u2019s no longer under active development.<\/p>\n<p>Microsoft \u5c06 EF Core \u8bbe\u8ba1\u4e3a\u5bf9\u6210\u719f\u7684 Entity Framework 6.x \uff08EF 6.x\uff09 ORM \u7684\u91cd\u65b0\u6784\u60f3\uff0c\u8be5 ORM \u57282008. \u7ecf\u8fc7\u591a\u5e74\u7684\u5f00\u53d1\uff0cEF 6.x \u662f\u4e00\u4e2a\u7a33\u5b9a\u4e14\u529f\u80fd\u4e30\u5bcc\u7684 ORM\uff0c\u4f46\u5b83\u4e0d\u518d\u5904\u4e8e\u79ef\u6781\u5f00\u53d1\u9636\u6bb5\u3002<\/p>\n<p>EF Core, released in 2016, is a comparatively new project. The APIs of EF Core are designed to be close to those of EF 6.x\u2014though they aren\u2019t identical\u2014but the core components have been completely rewritten. You should consider EF Core to be distinct from EF 6.x; upgrading directly from EF 6.x to EF Core is nontrivial.<\/p>\n<p>EF Core \u4e8e 2016 \u5e74\u53d1\u5e03\uff0c\u662f\u4e00\u4e2a\u76f8\u5bf9\u8f83\u65b0\u7684\u9879\u76ee\u3002EF Core \u7684 API \u8bbe\u8ba1\u4e3a\u63a5\u8fd1 EF 6.x \u7684 API\uff08\u5c3d\u7ba1\u5b83\u4eec\u5e76\u4e0d\u76f8\u540c\uff09\uff0c\u4f46\u6838\u5fc3\u7ec4\u4ef6\u5df2\u5b8c\u5168\u91cd\u5199\u3002\u60a8\u5e94\u8be5\u5c06 EF Core \u4e0e EF 6.x \u533a\u5206\u5f00\u6765;\u76f4\u63a5\u4ece EF 6.x \u5347\u7ea7\u5230 EF Core \u5e76\u975e\u6613\u4e8b\u3002<\/p>\n<p>Although Microsoft supports both EF Core and EF 6.x, EF 6.x isn\u2019t recommended for new .NET applications. There\u2019s little reason to start a new application with EF 6.x these days, but the exact tradeoffs will depend largely on your specific app. If you decide to choose EF 6.x instead of EF Core, make sure that you understand what you\u2019re sacrificing. Also make sure that you keep an eye on the guidance and feature comparison from the EF team at <a href=\"http:\/\/mng.bz\/GxgA\">http:\/\/mng.bz\/GxgA<\/a>.<\/p>\n<p>\u5c3d\u7ba1 Microsoft \u540c\u65f6\u652f\u6301 EF Core \u548c EF 6.x\uff0c\u4f46\u4e0d\u5efa\u8bae\u5c06 EF 6.x \u7528\u4e8e\u65b0\u7684 .NET \u5e94\u7528\u7a0b\u5e8f\u3002\u5982\u4eca\uff0c\u51e0\u4e4e\u6ca1\u6709\u7406\u7531\u4f7f\u7528 EF 6.x \u542f\u52a8\u65b0\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f46\u786e\u5207\u7684\u6743\u8861\u5728\u5f88\u5927\u7a0b\u5ea6\u4e0a\u53d6\u51b3\u4e8e\u60a8\u7684\u7279\u5b9a\u5e94\u7528\u7a0b\u5e8f\u3002\u5982\u679c\u60a8\u51b3\u5b9a\u9009\u62e9 EF 6.x \u800c\u4e0d\u662f EF Core\uff0c\u8bf7\u786e\u4fdd\u60a8\u4e86\u89e3\u60a8\u6b63\u5728\u727a\u7272\u4ec0\u4e48\u3002\u6b64\u5916\uff0c\u8bf7\u52a1\u5fc5\u5bc6\u5207\u5173\u6ce8 <a href=\"http:\/\/mng.bz\/GxgA\">http:\/\/mng.bz\/GxgA<\/a> \u7684 EF \u56e2\u961f\u63d0\u4f9b\u7684\u6307\u5357\u548c\u529f\u80fd\u6bd4\u8f83\u3002<\/p>\n<p>If you decide to use an ORM for your app, EF Core is a great contender. It\u2019s also supported out of the box by various other subsystems of ASP.NET Core. In chapter 23 you\u2019ll see how to use EF Core with the ASP.NET Core Identity authentication system for managing users in your apps.<\/p>\n<p>\u5982\u679c\u4f60\u51b3\u5b9a\u4e3a\u5e94\u7528\u4f7f\u7528 ORM\uff0cEF Core \u662f\u4e00\u4e2a\u5f88\u597d\u7684\u7ade\u4e89\u8005\u3002ASP.NET Core \u7684\u5404\u79cd\u5176\u4ed6\u5b50\u7cfb\u7edf\u4e5f\u652f\u6301\u5f00\u7bb1\u5373\u7528\u3002\u5728\u7b2c 23 \u7ae0\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u5c06 EF Core \u4e0e ASP.NET Core Identity \u8eab\u4efd\u9a8c\u8bc1\u7cfb\u7edf\u914d\u5408\u4f7f\u7528\uff0c\u4ee5\u7ba1\u7406\u5e94\u7528\u4e2d\u7684\u7528\u6237\u3002<\/p>\n<p>Before I get into the nitty-gritty of using EF Core in your app, I\u2019ll describe the application we\u2019re going to be using as the case study for this chapter. I\u2019ll go over the application and database details and discuss how to use EF Core to communicate between the two.<\/p>\n<p>\u5728\u6df1\u5165\u63a2\u8ba8\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 EF Core \u7684\u7ec6\u8282\u4e4b\u524d\uff0c\u6211\u5c06\u4ecb\u7ecd\u6211\u4eec\u5c06\u7528\u4f5c\u672c\u7ae0\u6848\u4f8b\u7814\u7a76\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u6211\u5c06\u4ecb\u7ecd\u5e94\u7528\u7a0b\u5e8f\u548c\u6570\u636e\u5e93\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u5e76\u8ba8\u8bba\u5982\u4f55\u4f7f\u7528 EF Core \u5728\u4e24\u8005\u4e4b\u95f4\u8fdb\u884c\u901a\u4fe1\u3002<\/p>\n<h3>12.1.4 Mapping a database to your application code<\/h3>\n<h3>12.1.4 \u5c06\u6570\u636e\u5e93\u6620\u5c04\u5230\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801<\/h3>\n<p>EF Core focuses on the communication between an application and a database, so to show it off, you need an application. This chapter uses the example of a simple cooking app API that lists recipes and lets you retrieve a recipe\u2019s ingredients, as shown in figure 12.2. Users can list all recipes, add new ones, edit recipes, and delete old ones.<\/p>\n<p>EF Core \u4fa7\u91cd\u4e8e\u5e94\u7528\u7a0b\u5e8f\u548c\u6570\u636e\u5e93\u4e4b\u95f4\u7684\u901a\u4fe1\uff0c\u56e0\u6b64\u8981\u5c55\u793a\u5b83\uff0c\u60a8\u9700\u8981\u4e00\u4e2a\u5e94\u7528\u7a0b\u5e8f\u3002\u672c\u7ae0\u4f7f\u7528\u4e00\u4e2a\u7b80\u5355\u7684\u70f9\u996a\u5e94\u7528\u7a0b\u5e8f API \u793a\u4f8b\uff0c\u8be5 API \u5217\u51fa\u4e86\u98df\u8c31\u5e76\u5141\u8bb8\u60a8\u68c0\u7d22\u98df\u8c31\u7684\u6210\u5206\uff0c\u5982\u56fe 12.2 \u6240\u793a\u3002\u7528\u6237\u53ef\u4ee5\u5217\u51fa\u6240\u6709\u914d\u65b9\u3001\u6dfb\u52a0\u65b0\u914d\u65b9\u3001\u7f16\u8f91\u914d\u65b9\u548c\u5220\u9664\u65e7\u914d\u65b9\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1202.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.2 The recipe app provides an API for managing recipes. You can view, update, and delete recipes, as well as create new ones.<br \/>\n\u56fe 12.2 \u914d\u65b9\u5e94\u7528\u7a0b\u5e8f\u63d0\u4f9b\u7528\u4e8e\u7ba1\u7406\u914d\u65b9\u7684 API\u3002\u60a8\u53ef\u4ee5\u67e5\u770b\u3001\u66f4\u65b0\u548c\u5220\u9664\u98df\u8c31\uff0c\u4ee5\u53ca\u521b\u5efa\u65b0\u7684\u98df\u8c31\u3002<\/p>\n<p>This API is obviously a simple one, but it contains all the database interactions you need with its two entities: Recipe and Ingredient.<\/p>\n<p>\u8fd9\u4e2a API \u663e\u7136\u5f88\u7b80\u5355\uff0c\u4f46\u5b83\u5305\u542b\u4e86\u60a8\u9700\u8981\u7684\u6240\u6709\u6570\u636e\u5e93\u4ea4\u4e92\uff0c\u5b83\u4e0e\u4e24\u4e2a\u5b9e\u4f53\uff1aRecipe \u548c Ingredient\u3002<\/p>\n<p><strong>Definition<\/strong> An entity is a .NET class that\u2019s mapped by EF Core to the database. These are classes you define, typically as POCO classes, that can be saved and loaded by mapping to database tables using EF Core.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u5b9e\u4f53\u662f\u7531 EF Core \u6620\u5c04\u5230\u6570\u636e\u5e93\u7684 .NET \u7c7b\u3002\u8fd9\u4e9b\u662f\u4f60\u5b9a\u4e49\u7684\u7c7b\uff0c\u901a\u5e38\u4e3a POCO \u7c7b\uff0c\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528 EF Core \u6620\u5c04\u5230\u6570\u636e\u5e93\u8868\u6765\u4fdd\u5b58\u548c\u52a0\u8f7d\u8fd9\u4e9b\u7c7b\u3002<\/p>\n<p>When you interact with EF Core, you\u2019ll be using primarily POCO entities and a database context that inherits from the DbContext EF Core class. The entity classes are the object-oriented representations of the tables in your database; they represent the data you want to store in the database. You use the DbContext in your application both to configure EF Core and access the database at runtime.<\/p>\n<p>\u4e0e EF Core \u4ea4\u4e92\u65f6\uff0c\u5c06\u4e3b\u8981\u4f7f\u7528 POCO \u5b9e\u4f53\u548c\u4ece DbContext EF Core \u7c7b\u7ee7\u627f\u7684\u6570\u636e\u5e93\u4e0a\u4e0b\u6587\u3002\u5b9e\u4f53\u7c7b\u662f\u6570\u636e\u5e93\u4e2d\u8868\u7684\u9762\u5411\u5bf9\u8c61\u7684\u8868\u793a\u5f62\u5f0f;\u5b83\u4eec\u8868\u793a\u8981\u5b58\u50a8\u5728\u6570\u636e\u5e93\u4e2d\u7684\u6570\u636e\u3002\u60a8\u53ef\u4ee5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 DbContext \u6765\u914d\u7f6e EF Core \u5e76\u5728\u8fd0\u884c\u65f6\u8bbf\u95ee\u6570\u636e\u5e93\u3002<\/p>\n<p><strong>Note<\/strong> You can potentially have multiple DbContexts in your application and even configure them to integrate with different databases.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u53ef\u80fd\u6709\u591a\u4e2a DbContext\uff0c\u751a\u81f3\u53ef\u4ee5\u5c06\u5b83\u4eec\u914d\u7f6e\u4e3a\u4e0e\u4e0d\u540c\u7684\u6570\u636e\u5e93\u96c6\u6210\u3002<\/p>\n<p>When your application first uses EF Core, EF Core creates an internal representation of the database based on the DbSet<T> properties on your application\u2019s DbContext and the entity classes themselves, as shown in figure 12.3.<\/p>\n<p>\u5f53\u5e94\u7528\u7a0b\u5e8f\u9996\u6b21\u4f7f\u7528 EF Core \u65f6\uff0cEF Core \u4f1a\u6839\u636e\u5e94\u7528\u7a0b\u5e8f\u7684 DbContext \u548c\u5b9e\u4f53\u7c7b\u672c\u8eab\u7684 DbSet<T> \u5c5e\u6027\u521b\u5efa\u6570\u636e\u5e93\u7684\u5185\u90e8\u8868\u793a\u5f62\u5f0f\uff0c\u5982\u56fe 12.3 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1203.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.3 EF Core creates an internal model of your application\u2019s data model by exploring the types in your code. It adds all the types referenced in the DbSet&lt;&gt; properties on your app\u2019s DbContext and any linked types.<br \/>\n\u56fe 12.3 EF Core \u901a\u8fc7\u6d4f\u89c8\u4ee3\u7801\u4e2d\u7684\u7c7b\u578b\u6765\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u6570\u636e\u6a21\u578b\u7684\u5185\u90e8\u6a21\u578b\u3002\u5b83\u4f1a\u6dfb\u52a0DbSet&lt;&gt; \u5e94\u7528\u7684 DbContext \u5c5e\u6027\u548c\u4efb\u4f55\u94fe\u63a5\u7c7b\u578b\u3002<\/p>\n<p>For the recipe app, EF Core builds a model of the Recipe class because it\u2019s exposed on the AppDbContext as a DbSet<Recipe>. Furthermore, EF Core loops through all the properties of Recipe, looking for types it doesn\u2019t know about, and adds them to its internal model. In the app, the Ingredients collection on Recipe exposes the Ingredient entity as an <code>ICollection&lt;Ingredient&gt;<\/code>, so EF Core models the entity appropriately.<\/p>\n<p>\u5bf9\u4e8e\u914d\u65b9\u5e94\u7528\uff0cEF Core \u4f1a\u751f\u6210 Recipe \u7c7b\u7684\u6a21\u578b\uff0c\u56e0\u4e3a\u5b83\u5728 AppDbContext \u4e0a\u4f5c\u4e3a DbSet<Recipe> \u516c\u5f00\u3002\u6b64\u5916\uff0cEF Core \u4f1a\u5faa\u73af\u8bbf\u95ee Recipe \u7684\u6240\u6709\u5c5e\u6027\uff0c\u67e5\u627e\u5b83\u4e0d\u77e5\u9053\u7684\u7c7b\u578b\uff0c\u5e76\u5c06\u5b83\u4eec\u6dfb\u52a0\u5230\u5176\u5185\u90e8\u6a21\u578b\u4e2d\u3002\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0cRecipe \u4e0a\u7684 Ingredients \u96c6\u5408\u5c06 Ingredient \u5b9e\u4f53\u516c\u5f00\u4e3a <code>ICollection&lt;Ingredient&gt;<\/code>\uff0c\u56e0\u6b64 EF Core \u4f1a\u9002\u5f53\u5730\u5bf9\u5b9e\u4f53\u8fdb\u884c\u5efa\u6a21\u3002<\/p>\n<p>EF Core maps each entity to a table in the database, but it also maps the relationships between the entities. Each recipe can have many ingredients, but each ingredient (which has a name, quantity, and unit) belongs to one recipe, so this is a many-to-one relationship. EF Core uses that knowledge to correctly model the equivalent many-to-one database structure.<\/p>\n<p>EF Core \u5c06\u6bcf\u4e2a\u5b9e\u4f53\u6620\u5c04\u5230\u6570\u636e\u5e93\u4e2d\u7684\u8868\uff0c\u4f46\u5b83\u4e5f\u4f1a\u6620\u5c04\u5b9e\u4f53\u4e4b\u95f4\u7684\u5173\u7cfb\u3002\u6bcf\u4e2a\u914d\u65b9\u53ef\u4ee5\u5305\u542b\u8bb8\u591a\u6210\u5206\uff0c\u4f46\u6bcf\u4e2a\u6210\u5206\uff08\u5177\u6709\u540d\u79f0\u3001\u6570\u91cf\u548c\u5355\u4f4d\uff09\u90fd\u5c5e\u4e8e\u4e00\u4e2a\u914d\u65b9\uff0c\u56e0\u6b64\u8fd9\u662f\u4e00\u79cd\u591a\u5bf9\u4e00\u5173\u7cfb\u3002EF Core \u4f7f\u7528\u8be5\u77e5\u8bc6\u5bf9\u7b49\u6548\u7684\u591a\u5bf9\u4e00\u6570\u636e\u5e93\u7ed3\u6784\u8fdb\u884c\u6b63\u786e\u5efa\u6a21\u3002<\/p>\n<p><strong>Note<\/strong>  Two different recipes, such as fish pie and lemon chicken, may use an ingredient that has both the same name and quantity, such as the juice of one lemon, but they\u2019re fundamentally two different instances. If you update the lemon chicken recipe to use two lemons, you wouldn\u2019t want this change to automatically update the fish pie to use two lemons too!<br \/>\n<strong>\u6ce8\u610f<\/strong> \u4e24\u79cd\u4e0d\u540c\u7684\u98df\u8c31\uff0c\u4f8b\u5982\u9c7c\u9985\u997c\u548c\u67e0\u6aac\u9e21\uff0c\u53ef\u80fd\u4f7f\u7528\u540d\u79f0\u548c\u6570\u91cf\u76f8\u540c\u7684\u6210\u5206\uff0c\u4f8b\u5982\u4e00\u4e2a\u67e0\u6aac\u7684\u6c41\u6db2\uff0c\u4f46\u5b83\u4eec\u672c\u8d28\u4e0a\u662f\u4e24\u4e2a\u4e0d\u540c\u7684\u5b9e\u4f8b\u3002\u5982\u679c\u60a8\u5c06\u67e0\u6aac\u9e21\u98df\u8c31\u66f4\u65b0\u4e3a\u4f7f\u7528\u4e24\u4e2a\u67e0\u6aac\uff0c\u60a8\u80af\u5b9a\u4e0d\u5e0c\u671b\u6b64\u66f4\u6539\u81ea\u52a8\u66f4\u65b0\u9c7c\u9985\u997c\u98df\u8c31\u4ee5\u4f7f\u7528\u4e24\u4e2a\u67e0\u6aac\uff01<\/p>\n<p>EF Core uses the internal model it builds when interacting with the database to ensure that it builds the correct SQL to create, read, update, and delete entities.<\/p>\n<p>EF Core \u5728\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u65f6\u4f7f\u7528\u5b83\u751f\u6210\u7684\u5185\u90e8\u6a21\u578b\uff0c\u4ee5\u786e\u4fdd\u5b83\u751f\u6210\u6b63\u786e\u7684 SQL \u6765\u521b\u5efa\u3001\u8bfb\u53d6\u3001\u66f4\u65b0\u548c\u5220\u9664\u5b9e\u4f53\u3002<\/p>\n<p>Right\u2014it\u2019s about time for some code! In section 12.2, you\u2019ll start building the recipe app. You\u2019ll see how to add EF Core to an ASP.NET Core application, configure a database provider, and design your application\u2019s data model.<\/p>\n<p>\u597d\u4e86 \u2014 \u662f\u65f6\u5019\u7f16\u5199\u4e00\u4e9b\u4ee3\u7801\u4e86\uff01\u5728\u7b2c 12.2 \u8282\u4e2d\uff0c\u60a8\u5c06\u5f00\u59cb\u6784\u5efa\u914d\u65b9\u5e94\u7528\u7a0b\u5e8f\u3002\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u5c06 EF Core \u6dfb\u52a0\u5230 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u3001\u914d\u7f6e\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u4ee5\u53ca\u8bbe\u8ba1\u5e94\u7528\u7a0b\u5e8f\u7684\u6570\u636e\u6a21\u578b\u3002<\/p>\n<h2>12.2 Adding EF Core to an application<\/h2>\n<h2>12.2 \u5c06 EF Core \u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f<\/h2>\n<p>In this section we focus on getting EF Core installed and configured in your ASP.NET Core recipe API app. You\u2019ll learn how to install the required NuGet packages and build the data model for your application. As we\u2019re talking about EF Core in this chapter, I\u2019m not going to go into how to create the application in general. I created a simple minimal API app as the basis\u2014nothing fancy.<\/p>\n<p>\u5728\u672c\u90e8\u5206\u4e2d\uff0c\u6211\u4eec\u91cd\u70b9\u4ecb\u7ecd\u5982\u4f55\u5728 ASP.NET Core \u914d\u65b9 API \u5e94\u7528\u7a0b\u5e8f\u4e2d\u5b89\u88c5\u548c\u914d\u7f6e EF Core\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u5b89\u88c5\u6240\u9700\u7684 NuGet \u5305\u5e76\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6784\u5efa\u6570\u636e\u6a21\u578b\u3002\u7531\u4e8e\u6211\u4eec\u5728\u672c\u7ae0\u4e2d\u8ba8\u8bba\u7684\u662f EF Core\uff0c\u56e0\u6b64\u6211\u4e0d\u6253\u7b97\u4e00\u822c\u5730\u4ecb\u7ecd\u5982\u4f55\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u3002\u6211\u521b\u5efa\u4e86\u4e00\u4e2a\u7b80\u5355\u3001\u6700\u5c0f\u7684 API \u5e94\u7528\u7a0b\u5e8f\u4f5c\u4e3a\u57fa\u7840 \u2014 \u6ca1\u4ec0\u4e48\u82b1\u54e8\u7684\u3002<\/p>\n<p><strong>Tip<\/strong> The sample code for this chapter shows the state of the application at three points in this chapter: at the end of section 12.2, at the end of section 12.3, and at the end of the chapter. It also includes examples using both LocalDB and SQLite providers.<br \/>\n<strong>\u63d0\u793a<\/strong> \u672c\u7ae0\u7684\u793a\u4f8b\u4ee3\u7801\u663e\u793a\u4e86\u672c\u7ae0\u4e2d\u4e09\u4e2a\u70b9\u7684\u5e94\u7528\u7a0b\u5e8f\u72b6\u6001\uff1a\u7b2c 12.2 \u8282\u7684\u672b\u5c3e\u3001\u7b2c 12.3 \u8282\u7684\u672b\u5c3e\u548c\u672c\u7ae0\u7684\u672b\u5c3e\u3002\u5b83\u8fd8\u5305\u62ec\u4f7f\u7528 LocalDB \u548c SQLite \u63d0\u4f9b\u7a0b\u5e8f\u7684\u793a\u4f8b\u3002<\/p>\n<p>Interaction with EF Core in the example app occurs in a service layer that encapsulates all the data access outside your minimal API endpoint handlers, as shown in figure 12.4. This design keeps your concerns separated and makes your services testable.<\/p>\n<p>\u5728\u793a\u4f8b\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4e0e EF Core \u7684\u4ea4\u4e92\u53d1\u751f\u5728\u670d\u52a1\u5c42\u4e2d\uff0c\u8be5\u670d\u52a1\u5c42\u5c01\u88c5\u4e86\u6700\u5c0f API \u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e4b\u5916\u7684\u6240\u6709\u6570\u636e\u8bbf\u95ee\uff0c\u5982\u56fe 12.4 \u6240\u793a\u3002\u8fd9\u79cd\u8bbe\u8ba1\u5c06\u60a8\u7684\u5173\u6ce8\u70b9\u5206\u5f00\uff0c\u5e76\u4f7f\u60a8\u7684\u670d\u52a1\u53ef\u6d4b\u8bd5\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1204.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.4 Handling a request by loading data from a database using EF Core. Interaction with EF Core is restricted to RecipeService; the endpoint doesn\u2019t access EF Core directly.<\/p>\n<p>\u56fe 12.4 \u901a\u8fc7\u4f7f\u7528 EF Core \u4ece\u6570\u636e\u5e93\u52a0\u8f7d\u6570\u636e\u6765\u5904\u7406\u8bf7\u6c42\u3002\u4e0e EF Core \u7684\u4ea4\u4e92\u4ec5\u9650\u4e8e RecipeService;\u7ec8\u7aef\u8282\u70b9\u4e0d\u76f4\u63a5\u8bbf\u95ee EF Core\u3002<\/p>\n<p>Adding EF Core to an application is a multistep process:<br \/>\n\u5c06 EF Core \u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f\u662f\u4e00\u4e2a\u591a\u6b65\u9aa4\u8fc7\u7a0b\uff1a<\/p>\n<ol>\n<li>\n<p>Choose a database provider, such as Postgres, SQLite, or MS SQL Server.<br \/>\n\u9009\u62e9\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\uff0c\u4f8b\u5982 Postgres\u3001SQLite \u6216 MS SQL Server\u3002<\/p>\n<\/li>\n<li>\n<p>Install the EF Core NuGet packages.<br \/>\n\u5b89\u88c5 EF Core NuGet \u5305\u3002<\/p>\n<\/li>\n<li>\n<p>Design your app\u2019s DbContext and entities that make up your data model.<br \/>\n\u8bbe\u8ba1\u5e94\u7528\u7a0b\u5e8f\u7684 DbContext \u548c\u6784\u6210\u6570\u636e\u6a21\u578b\u7684\u5b9e\u4f53\u3002<\/p>\n<\/li>\n<li>\n<p>Register your app\u2019s DbContext with the ASP.NET Core DI container.<br \/>\n\u5c06\u5e94\u7528\u7684 DbContext \u6ce8\u518c\u5230 ASP.NET Core DI \u5bb9\u5668\u3002<\/p>\n<\/li>\n<li>\n<p>Use EF Core to generate a migration describing your data model.<br \/>\n\u4f7f\u7528 EF Core \u751f\u6210\u63cf\u8ff0\u6570\u636e\u6a21\u578b\u7684\u8fc1\u79fb\u3002<\/p>\n<\/li>\n<li>\n<p>Apply the migration to the database to update the database\u2019s schema.<br \/>\n\u5c06\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u4ee5\u66f4\u65b0\u6570\u636e\u5e93\u7684\u67b6\u6784\u3002<\/p>\n<\/li>\n<\/ol>\n<p>This process may seem a little daunting already, but I\u2019ll walk through steps 1\u20134 in sections 12.2.1\u201312.2.3 and steps 5\u20136 in section 12.3, so it won\u2019t take long. Given the space constraints of this chapter, I stick to the default conventions of EF Core in the code I show you. EF Core is far more customizable than it may initially appear to be, but I encourage you to stick to the defaults wherever possible, which will make your life easier in the long run.<\/p>\n<p>\u8fd9\u4e2a\u8fc7\u7a0b\u53ef\u80fd\u770b\u8d77\u6765\u6709\u70b9\u4ee4\u4eba\u751f\u754f\uff0c\u4f46\u6211\u5c06\u4ecb\u7ecd\u7b2c 12.2.1-12.2.3 \u8282\u4e2d\u7684\u7b2c 1-4 \u6b65\u548c\u7b2c 12.3 \u8282\u4e2d\u7684\u7b2c 5-6 \u6b65\uff0c\u56e0\u6b64\u4e0d\u4f1a\u82b1\u8d39\u5f88\u957f\u65f6\u95f4\u3002\u9274\u4e8e\u672c\u7ae0\u7684\u7bc7\u5e45\u9650\u5236\uff0c\u6211\u5728\u5411\u60a8\u5c55\u793a\u7684\u4ee3\u7801\u4e2d\u575a\u6301 EF Core \u7684\u9ed8\u8ba4\u7ea6\u5b9a\u3002EF Core \u7684\u53ef\u5b9a\u5236\u6027\u6bd4\u6700\u521d\u770b\u8d77\u6765\u8981\u9ad8\u5f97\u591a\uff0c\u4f46\u6211\u9f13\u52b1\u60a8\u5c3d\u53ef\u80fd\u575a\u6301\u4f7f\u7528\u9ed8\u8ba4\u503c\uff0c\u4ece\u957f\u8fdc\u6765\u770b\uff0c\u8fd9\u5c06\u4f7f\u60a8\u7684\u751f\u6d3b\u66f4\u8f7b\u677e\u3002<\/p>\n<p>The first step in setting up EF Core is deciding which database you\u2019d like to interact with. It\u2019s likely that a client or your company\u2019s policy will dictate this decision, but giving some thought to it is still worthwhile.<\/p>\n<p>\u8bbe\u7f6e EF Core \u7684\u7b2c\u4e00\u6b65\u662f\u786e\u5b9a\u8981\u4e0e\u4e4b\u4ea4\u4e92\u7684\u6570\u636e\u5e93\u3002\u5ba2\u6237\u6216\u60a8\u516c\u53f8\u7684\u653f\u7b56\u53ef\u80fd\u4f1a\u51b3\u5b9a\u8fd9\u4e2a\u51b3\u5b9a\uff0c\u4f46\u8003\u8651\u4e00\u4e0b\u4ecd\u7136\u662f\u503c\u5f97\u7684\u3002<\/p>\n<h3>12.2.1 Choosing a database provider and installing EF Core<\/h3>\n<h3>12.2.1 \u9009\u62e9\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u5e76\u5b89\u88c5 EF Core<\/h3>\n<p>EF Core supports a range of databases by using a provider model. The modular nature of EF Core means that you can use the same high-level API to program against different underlying databases; EF Core knows how to generate the necessary implementation-specific code and SQL statements.<\/p>\n<p>EF Core \u4f7f\u7528\u63d0\u4f9b\u7a0b\u5e8f\u6a21\u578b\u652f\u6301\u4e00\u7cfb\u5217\u6570\u636e\u5e93\u3002EF Core \u7684\u6a21\u5757\u5316\u7279\u6027\u610f\u5473\u7740\u60a8\u53ef\u4ee5\u4f7f\u7528\u76f8\u540c\u7684\u9ad8\u7ea7 API \u5bf9\u4e0d\u540c\u7684\u5e95\u5c42\u6570\u636e\u5e93\u8fdb\u884c\u7f16\u7a0b;EF Core \u77e5\u9053\u5982\u4f55\u751f\u6210\u5fc5\u8981\u7684\u7279\u5b9a\u4e8e\u5b9e\u73b0\u7684\u4ee3\u7801\u548c SQL \u8bed\u53e5\u3002<\/p>\n<p>You\u2019ll probably have a database in mind when you start your application, and you\u2019ll be pleased to know that EF Core has most of the popular ones covered. Adding support for a given database involves adding the correct NuGet package to your .csproj file, such as the following:<br \/>\n\u5728\u542f\u52a8\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u60a8\u53ef\u80fd\u4f1a\u60f3\u5230\u4e00\u4e2a\u6570\u636e\u5e93\uff0c\u5e76\u4e14\u60a8\u4f1a\u5f88\u9ad8\u5174\u5730\u77e5\u9053 EF Core \u6db5\u76d6\u4e86\u5927\u591a\u6570\u5e38\u7528\u6570\u636e\u5e93\u3002\u6dfb\u52a0\u5bf9\u7ed9\u5b9a\u6570\u636e\u5e93\u7684\u652f\u6301\u6d89\u53ca\u5c06\u6b63\u786e\u7684 NuGet \u5305\u6dfb\u52a0\u5230.csproj \u6587\u4ef6\uff0c\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<ul>\n<li>\n<p>PostgreSQL\u2014Npgsql.EntityFrameworkCore.PostgreSQL<\/p>\n<\/li>\n<li>\n<p>Microsoft SQL Server\u2014Microsoft.EntityFrameworkCore.SqlServer<\/p>\n<\/li>\n<li>\n<p>MySQL\u2014MySql.Data.EntityFrameworkCore<\/p>\n<\/li>\n<li>\n<p>SQLite\u2014Microsoft.EntityFrameworkCore.SQLite<\/p>\n<\/li>\n<\/ul>\n<p>Some of the database provider packages are maintained by Microsoft, some are maintained by the open-source community, and some (such as the Oracle provider) require a paid license, so be sure to check your requirements. You can find a list of providers at <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/providers\">https:\/\/docs.microsoft.com\/ef\/core\/providers<\/a>.<\/p>\n<p>\u4e00\u4e9b\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u5305\u7531 Microsoft \u7ef4\u62a4\uff0c\u4e00\u4e9b\u7531\u5f00\u6e90\u793e\u533a\u7ef4\u62a4\uff0c\u8fd8\u6709\u4e00\u4e9b\uff08\u5982 Oracle \u63d0\u4f9b\u7a0b\u5e8f\uff09\u9700\u8981\u4ed8\u8d39\u8bb8\u53ef\u8bc1\uff0c\u56e0\u6b64\u8bf7\u52a1\u5fc5\u68c0\u67e5\u60a8\u7684\u8981\u6c42\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/providers\">https:\/\/docs.microsoft.com\/ef\/core\/providers<\/a> \u4e0a\u627e\u5230\u63d0\u4f9b\u5546\u5217\u8868\u3002<\/p>\n<p>You install a database provider in your application in the same way as any other library: by adding a NuGet package to your project\u2019s .csproj file and running dotnet restore from the command line (or letting Visual Studio automatically restore for you).<\/p>\n<p>\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5b89\u88c5\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u7684\u65b9\u5f0f\u4e0e\u4efb\u4f55\u5176\u4ed6\u5e93\u76f8\u540c\uff1a\u5c06 NuGet \u5305\u6dfb\u52a0\u5230\u9879\u76ee\u7684 .csproj \u6587\u4ef6\u5e76\u8fd0\u884c dotnet restore\u4ece\u547d\u4ee4\u884c\uff08\u6216\u8ba9 Visual Studio \u81ea\u52a8\u4e3a\u60a8\u8fd8\u539f\uff09\u3002<\/p>\n<p>EF Core is inherently modular, so you\u2019ll need to install multiple packages. I\u2019m using the SQLite database provider, so I\u2019ll be using the SQLite packages:<\/p>\n<p>EF Core \u672c\u8d28\u4e0a\u662f\u6a21\u5757\u5316\u7684\uff0c\u56e0\u6b64\u9700\u8981\u5b89\u88c5\u591a\u4e2a\u5305\u3002\u6211\u6b63\u5728\u4f7f\u7528 SQLite \u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\uff0c\u56e0\u6b64\u6211\u5c06\u4f7f\u7528 SQLite \u5305\uff1a<\/p>\n<ul>\n<li>\n<p>Microsoft.EntityFrameworkCore.SQLite\u2014This package is the main database provider package for using EF Core at runtime. It also contains a reference to the main EF Core NuGet package.<br \/>\nMicrosoft.EntityFrameworkCore.SQLite \u2013 \u6b64\u5305\u662f\u5728\u8fd0\u884c\u65f6\u4f7f\u7528 EF Core \u7684\u4e3b\u8981\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u5305\u3002\u5b83\u8fd8\u5305\u542b\u5bf9\u4e3b EF Core NuGet \u5305\u7684\u5f15\u7528\u3002<\/p>\n<\/li>\n<li>\n<p>Microsoft.EntityFrameworkCore.Design\u2014This package contains shared build-time components for EF Core, required for building the EF Core data model for your app.<br \/>\nMicrosoft.EntityFrameworkCore.Design \u2013 \u6b64\u5305\u5305\u542b EF Core \u7684\u5171\u4eab\u6784\u5efa\u65f6\u7ec4\u4ef6\uff0c\u8fd9\u4e9b\u7ec4\u4ef6\u662f\u4e3a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6784\u5efa EF Core \u6570\u636e\u6a21\u578b\u6240\u5fc5\u9700\u7684\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>Tip<\/strong> You\u2019ll also want to install tooling to help create and update your database. I show how to install these tools in section 12.3.1.<br \/>\n<strong>\u63d0\u793a<\/strong> \u60a8\u8fd8\u9700\u8981\u5b89\u88c5\u5de5\u5177\u6765\u5e2e\u52a9\u521b\u5efa\u548c\u66f4\u65b0\u6570\u636e\u5e93\u3002\u6211\u5728 Section 12.3.1 \u4e2d\u6f14\u793a\u4e86\u5982\u4f55\u5b89\u88c5\u8fd9\u4e9b\u5de5\u5177\u3002<\/p>\n<p>Listing 12.1 shows the recipe app\u2019s .csproj file after adding the EF Core packages. Remember, you add NuGet packages as PackageReference elements.<\/p>\n<p>\u5217\u8868 12.1 \u663e\u793a\u4e86\u6dfb\u52a0 EF Core \u5305\u540e\u914d\u65b9\u5e94\u7528\u7a0b\u5e8f\u7684 .csproj \u6587\u4ef6\u3002\u8bf7\u8bb0\u4f4f\uff0c\u5c06 NuGet \u5305\u6dfb\u52a0\u4e3a PackageReference \u5143\u7d20\u3002<\/p>\n<p>Listing 12.1 Installing EF Core in an ASP.NET Core application<br \/>\n\u5217\u8868 12.1 \u5728 ASP.NET Core \u4e2d\u5b89\u88c5 EF Core\u5e94\u7528<\/p>\n<pre><code>&lt;Project Sdk=&quot;Microsoft.NET.Sdk.Web&quot;&gt;\n\n  &lt;PropertyGroup&gt;\n    &lt;TargetFramework&gt;net7.0&lt;\/TargetFramework&gt;   \u2776\n    &lt;Nullable&gt;enable&lt;\/Nullable&gt;\n    &lt;ImplicitUsings&gt;enable&lt;\/ImplicitUsings&gt;\n  &lt;\/PropertyGroup&gt;\n\n  &lt;ItemGroup&gt;\n    &lt;PackageReference     \u2777\n      Include=&quot;Microsoft.EntityFrameworkCore.SQLite&quot;    \u2777\n      Version=&quot;7.0.0&quot; \/&gt;    \u2777\n    &lt;PackageReference     \u2778\n      Include=&quot;Microsoft.EntityFrameworkCore.Design&quot;    \u2778\n      Version=&quot;7.0.0&quot; &gt;    \u2778\n        &lt;IncludeAssets&gt;runtime; build; native; contentfiles;  \u2779\n          Analyzers; buildtransitive&lt;\/IncludeAssets&gt;    \u2779\n        &lt;PrivateAssets&gt;all&lt;\/PrivateAssets&gt;    \u2779\n    &lt;\/PackageReference&gt;\n  &lt;\/ItemGroup&gt;<\/code><\/pre>\n<p>\u2776 The app targets .NET 7.0.<br \/>\n\u8be5\u5e94\u7528\u7a0b\u5e8f\u9762\u5411 .NET 7.0\u3002<br \/>\n\u2777 Installs the appropriate NuGet package for your selected DB<br \/>\n\u4e3a\u6240\u9009\u6570\u636e\u5e93\u5b89\u88c5\u9002\u5f53\u7684 NuGet \u5305<br \/>\n\u2778 Contains shared design-time components for EF Core<br \/>\n\u5305\u542b EF Core \u7684\u5171\u4eab\u8bbe\u8ba1\u65f6\u7ec4\u4ef6<br \/>\n\u2779 Added automatically by NuGet<br \/>\n\u7531 NuGet \u81ea\u52a8\u6dfb\u52a0<\/p>\n<p>With these packages installed and restored, you have everything you need to start building the data model for your application. In section 12.2.2 we\u2019ll create the entity classes and the DbContext for your recipe app.<\/p>\n<p>\u5b89\u88c5\u5e76\u8fd8\u539f\u8fd9\u4e9b\u5305\u540e\uff0c\u60a8\u5c31\u62e5\u6709\u4e86\u5f00\u59cb\u4e3a\u5e94\u7528\u7a0b\u5e8f\u6784\u5efa\u6570\u636e\u6a21\u578b\u6240\u9700\u7684\u4e00\u5207\u3002\u5728\u7b2c 12.2.2 \u8282\u4e2d\uff0c\u6211\u4eec\u5c06\u4e3a\u60a8\u7684\u914d\u65b9\u5e94\u7528\u7a0b\u5e8f\u521b\u5efa\u5b9e\u4f53\u7c7b\u548c DbContext\u3002<\/p>\n<h3>12.2.2 Building a data model<\/h3>\n<h3>12.2.2 \u6784\u5efa\u6570\u636e\u6a21\u578b<\/h3>\n<p>In section 12.1.4 I showed an overview of how EF Core builds up its internal model of your database from the DbContext and entity models. Apart from this discovery mechanism, EF Core is flexible in letting you define your entities the way you want to, as POCO classes.<\/p>\n<p>\u5728\u7b2c 12.1.4 \u8282\u4e2d\uff0c\u6211\u6982\u8ff0\u4e86 EF Core \u5982\u4f55\u4ece DbContext \u548c\u5b9e\u4f53\u6a21\u578b\u6784\u5efa\u5176\u6570\u636e\u5e93\u7684\u5185\u90e8\u6a21\u578b\u3002\u9664\u4e86\u8fd9\u79cd\u53d1\u73b0\u673a\u5236\u4e4b\u5916\uff0cEF Core \u8fd8\u53ef\u4ee5\u7075\u6d3b\u5730\u8ba9\u4f60\u4ee5\u6240\u9700\u7684\u65b9\u5f0f\u5c06\u5b9e\u4f53\u5b9a\u4e49\u4e3a POCO \u7c7b\u3002<\/p>\n<p>Some ORMs require your entities to inherit from a specific base class or require you to decorate your models with attributes that describe how to map them. EF Core heavily favors a convention over configuration approach, as you can see in listing 12.2, which shows the Recipe and Ingredient entity classes for your app.<\/p>\n<p>\u67d0\u4e9b ORM \u8981\u6c42\u5b9e\u4f53\u4ece\u7279\u5b9a\u57fa\u7c7b\u7ee7\u627f\uff0c\u6216\u8005\u8981\u6c42\u60a8\u4f7f\u7528\u63cf\u8ff0\u5982\u4f55\u6620\u5c04\u6a21\u578b\u7684\u5c5e\u6027\u6765\u88c5\u9970\u6a21\u578b\u3002EF Core \u975e\u5e38\u503e\u5411\u4e8e\u4f7f\u7528\u7ea6\u5b9a\u800c\u4e0d\u662f\u914d\u7f6e\u65b9\u6cd5\uff0c\u56e0\u4e3a\u4f60\u53ef\u4ee5\u8fd9\u6837\u505a\u53c2\u89c1 \u6e05\u5355 12.2 \u4e2d\uff0c\u5b83\u663e\u793a\u4e86 Recipe \u548cngredient \u5b9e\u4f53\u7c7b\u3002<\/p>\n<p><strong>Tip<\/strong> The required keyword, used on several properties in listing 12.2, was introduced in C# 11. It\u2019s used here to prevent warnings about uninitialized non-nullable values. You can read more about how EF Core interacts with non-nullable types in the documentation at <a href=\"http:\/\/mng.bz\/Keoj\">http:\/\/mng.bz\/Keoj<\/a>.<\/p>\n<p>\u63d0\u793a \u5728\u6e05\u5355 12.2 \u4e2d\u7684\u51e0\u4e2a\u5c5e\u6027\u4e0a\u4f7f\u7528\u7684 required \u5173\u952e\u5b57\u662f\u5728 C# 11 \u4e2d\u5f15\u5165\u7684\u3002\u5b83\u5728\u6b64\u5904\u7528\u4e8e\u9632\u6b62\u6709\u5173\u672a\u521d\u59cb\u5316\u7684\u4e0d\u53ef\u4e3a null \u503c\u7684\u8b66\u544a\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"http:\/\/mng.bz\/Keoj\">http:\/\/mng.bz\/Keoj<\/a> \u6587\u6863\u4e2d\u9605\u8bfb\u6709\u5173 EF Core \u5982\u4f55\u4e0e\u4e0d\u53ef\u4e3a null \u7684\u7c7b\u578b\u4ea4\u4e92\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<p>Listing 12.2 Defining the EF Core entity classes<br \/>\n\u5217\u8868 12.2 \u5b9a\u4e49 EF Core \u5b9e\u4f53\u7c7b<\/p>\n<pre><code>public class Recipe\n{\n    public int RecipeId { get; set; }\n    public required string Name { get; set; }\n    public TimeSpan TimeToCook { get; set; }\n    public bool IsDeleted { get; set; }\n    public required string Method { get; set; }\n    public required ICollection&lt;Ingredient&gt; Ingredients { get; set; }  \u2776\n}\npublic class Ingredient\n{\n    public int IngredientId { get; set; }\n    public int RecipeId { get; set; }\n    public required string Name { get; set; }\n    public decimal Quantity { get; set; }\n    public required string Unit { get; set; }\n}<\/code><\/pre>\n<p>\u2776 A Recipe can have many Ingredients, represented by ICollection.<br \/>\n\u4e00\u4e2a\u914d\u65b9\u53ef\u4ee5\u6709\u5f88\u591a\u6210\u5206\uff0c\u7528 ICollection \u8868\u793a\u3002<\/p>\n<p>These classes conform to certain default conventions that EF Core uses to build up a picture of the database it\u2019s mapping. The Recipe class, for example, has a RecipeId property, and the Ingredient class has an IngredientId property. EF Core identifies this pattern of an Id suffix as indicating the primary key of the table.<\/p>\n<p>\u8fd9\u4e9b\u7c7b\u7b26\u5408 EF Core \u7528\u4e8e\u6784\u5efa\u5176\u6620\u5c04\u7684\u6570\u636e\u5e93\u56fe\u7247\u7684\u67d0\u4e9b\u9ed8\u8ba4\u7ea6\u5b9a\u3002\u4f8b\u5982\uff0cRecipe \u7c7b\u5177\u6709 RecipeId \u5c5e\u6027\uff0c\u800c Ingredient \u7c7b\u5177\u6709 IngredientId \u5c5e\u6027\u3002EF Core \u5c06 Id \u540e\u7f00\u7684\u8fd9\u79cd\u6a21\u5f0f\u6807\u8bc6\u4e3a\u6307\u793a\u8868\u7684\u4e3b\u952e\u3002<\/p>\n<p><strong>Definition<\/strong> The primary key of a table is a value that uniquely identifies the row among all the others in the table. It\u2019s often an int or a Guid.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u8868\u7684\u4e3b\u952e\u662f\u4e00\u4e2a\u503c\uff0c\u7528\u4e8e\u5728\u8868\u4e2d\u7684\u6240\u6709\u5176\u4ed6\u884c\u4e2d\u552f\u4e00\u6807\u8bc6\u8be5\u884c\u3002\u5b83\u901a\u5e38\u662f int \u6216 Guid\u3002<\/p>\n<p>Another convention visible here is the RecipeId property on the Ingredient class. EF Core interprets this property to be a foreign key pointing to the Recipe class. When considered with ICollection<Ingredient> on the Recipe class, this property represents a many-to-one relationship, in which each recipe has many ingredients but each ingredient belongs to a single recipe (figure 12.5).<\/p>\n<p>\u6b64\u5904\u663e\u793a\u7684\u53e6\u4e00\u4e2a\u7ea6\u5b9a\u662f Ingredient \u7c7b\u7684 RecipeId \u5c5e\u6027\u3002EF Core \u5c06\u6b64\u5c5e\u6027\u89e3\u91ca\u4e3a\u6307\u5411 Recipe \u7c7b\u7684\u5916\u952e\u3002\u5f53\u5728 Recipe \u7c7b\u4e2d\u4f7f\u7528 ICollection<Ingredient> \u65f6\uff0c\u6b64\u5c5e\u6027\u8868\u793a\u591a\u5bf9\u4e00\u5173\u7cfb\uff0c\u5176\u4e2d\u6bcf\u4e2a\u914d\u65b9\u90fd\u6709\u8bb8\u591a\u6210\u5206\uff0c\u4f46\u6bcf\u4e2a\u6210\u5206\u90fd\u5c5e\u4e8e\u4e00\u4e2a\u914d\u65b9\uff08\u56fe 12.5\uff09\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1205.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.5 Many-to-one relationships in code are translated to foreign key relationships between tables.<br \/>\n\u56fe 12.5 \u4ee3\u7801\u4e2d\u7684\u591a\u5bf9\u4e00\u5173\u7cfb\u8f6c\u6362\u4e3a\u8868\u4e4b\u95f4\u7684\u5916\u952e\u5173\u7cfb\u3002<\/p>\n<p><strong>Definition<\/strong> A foreign key on a table points to the primary key of a different table, forming a link between the two rows.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u8868\u4e0a\u7684\u5916\u952e\u6307\u5411\u4e0d\u540c\u8868\u7684\u4e3b\u952e\uff0c\u4ece\u800c\u5728\u4e24\u884c\u4e4b\u95f4\u5f62\u6210\u94fe\u63a5\u3002<\/p>\n<p>Many other conventions are at play here, such as the names EF Core will assume for the database tables and columns or the database column types it will use for each property, but I\u2019m not going to discuss them here. The EF Core documentation contains details about all these conventions, as well as how to customize them for your application; see <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/modeling\">https:\/\/docs.microsoft.com\/ef\/core\/modeling<\/a>.<\/p>\n<p>\u8fd9\u91cc\u8fd8\u6709\u8bb8\u591a\u5176\u4ed6\u7ea6\u5b9a\uff0c\u4f8b\u5982 EF Core \u5c06\u4e3a\u6570\u636e\u5e93\u8868\u548c\u5217\u91c7\u7528\u7684\u540d\u79f0\uff0c\u6216\u8005\u5b83\u5c06\u7528\u4e8e\u6bcf\u4e2a\u5c5e\u6027\u7684\u6570\u636e\u5e93\u5217\u7c7b\u578b\uff0c\u4f46\u6211\u4e0d\u6253\u7b97\u5728\u8fd9\u91cc\u8ba8\u8bba\u5b83\u4eec\u3002EF Core \u6587\u6863\u5305\u542b\u6709\u5173\u6240\u6709\u8fd9\u4e9b\u7ea6\u5b9a\u4ee5\u53ca\u5982\u4f55\u4e3a\u5e94\u7528\u7a0b\u5e8f\u81ea\u5b9a\u4e49\u5b83\u4eec\u7684\u8be6\u7ec6\u4fe1\u606f;\u8bf7\u53c2\u9605 <a href=\"https:\/\/docs.microsoft.com\/ef\/core\/modeling\">https:\/\/docs.microsoft.com\/ef\/core\/modeling<\/a>\u3002<\/p>\n<p><strong>Tip<\/strong> You can also use DataAnnotations attributes to decorate your entity classes, controlling things like column naming and string length. EF Core will use these attributes to override the default conventions.<br \/>\n<strong>\u63d0\u793a<\/strong> \u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 DataAnnotations \u5c5e\u6027\u6765\u88c5\u9970\u5b9e\u4f53\u7c7b\uff0c\u4ece\u800c\u63a7\u5236\u5217\u547d\u540d\u548c\u5b57\u7b26\u4e32\u957f\u5ea6\u7b49\u5185\u5bb9\u3002EF Core \u5c06\u4f7f\u7528\u8fd9\u4e9b\u5c5e\u6027\u6765\u66ff\u4ee3\u9ed8\u8ba4\u7ea6\u5b9a\u3002<\/p>\n<p>As well as defining the entities, you define the DbContext for your application. The DbContext is the heart of EF Core in your application, used for all your database calls. Create a custom DbContext, in this case called AppDbContext, and derive from the DbContext base class, as shown in listing 12.3. This class exposes the DbSet<Recipe> so that EF Core can discover and map the Recipe entity. You can expose multiple instances of DbSet&lt;&gt; in this way for each of the top-level entities in your application.<\/p>\n<p>\u9664\u4e86\u5b9a\u4e49\u5b9e\u4f53\u4e4b\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u4e3a\u5e94\u7528\u7a0b\u5e8f\u5b9a\u4e49 DbContext\u3002DbContext \u662f\u5e94\u7528\u7a0b\u5e8f\u4e2d EF Core \u7684\u6838\u5fc3\uff0c\u7528\u4e8e\u6240\u6709\u6570\u636e\u5e93\u8c03\u7528\u3002\u521b\u5efa\u81ea\u5b9a\u4e49 DbContext\uff08\u5728\u672c\u4f8b\u4e2d\u79f0\u4e3a AppDbContext\uff09\uff0c\u5e76\u4ece DbContext \u57fa\u7c7b\u6d3e\u751f\uff0c\u5982\u6e05\u5355\u6240\u793a12.3. \u6b64\u7c7b\u516c\u5f00 DbSet<Recipe>\u4ee5\u4fbf EF Core \u53ef\u4ee5\u53d1\u73b0\u548c\u6620\u5c04 Recipe \u5b9e\u4f53\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8fd9\u79cd\u65b9\u5f0f\u4e3a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6bcf\u4e2a\u9876\u7ea7\u5b9e\u4f53\u516c\u5f00 DbSet&lt;&gt; \u7684\u591a\u4e2a\u5b9e\u4f8b<\/p>\n<p>Listing 12.3 Defining the application DbContext<br \/>\n\u6e05\u5355 12.3 \u5b9a\u4e49\u5e94\u7528\u7a0b\u5e8f DbContext<\/p>\n<pre><code>public class AppDbContext : DbContext\n{\n    public AppDbContext(DbContextOptions&lt;AppDbContext&gt; options)      \u2776\n        : base(options) { }                                          \u2776\n    public DbSet&lt;Recipe&gt; Recipes { get; set; }    \u2777\n}<\/code><\/pre>\n<p>\u2776 The constructor options object, containing details such as the connection string<br \/>\n\u6784\u9020\u51fd\u6570\u9009\u9879\u5bf9\u8c61\uff0c\u5305\u542b\u8fde\u63a5\u5b57\u7b26\u4e32\u7b49\u8be6\u7ec6\u4fe1\u606f<\/p>\n<p>\u2777 You\u2019ll use the Recipes property to query the database.<br \/>\n\u60a8\u5c06\u4f7f\u7528 Recipes \u5c5e\u6027\u6765\u67e5\u8be2\u6570\u636e\u5e93\u3002<\/p>\n<p>The AppDbContext for your app is simple, containing a list of your root entities, but you can do a lot more with it in a more complex application. If you wanted to, you could customize how EF Core maps entities to the database, but for this app you\u2019re going to use the defaults.<\/p>\n<p>\u5e94\u7528\u7a0b\u5e8f\u7684 AppDbContext \u5f88\u7b80\u5355\uff0c\u5305\u542b\u6839\u5b9e\u4f53\u7684\u5217\u8868\uff0c\u4f46\u60a8\u53ef\u4ee5\u5728\u66f4\u590d\u6742\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u5b83\u6267\u884c\u66f4\u591a\u4f5c\u3002\u5982\u679c\u9700\u8981\uff0c\u53ef\u4ee5\u81ea\u5b9a\u4e49 EF Core \u5c06\u5b9e\u4f53\u6620\u5c04\u5230\u6570\u636e\u5e93\u7684\u65b9\u5f0f\uff0c\u4f46\u5bf9\u4e8e\u6b64\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f60\u5c06\u4f7f\u7528\u9ed8\u8ba4\u503c\u3002<\/p>\n<p><strong>Note<\/strong> You didn\u2019t list Ingredient on AppDbContext, but EF Core models it correctly as it\u2019s exposed on the Recipe. You can still access the Ingredient objects in the database, but you must navigate via the Recipe entity\u2019s Ingredients property to do so, as you\u2019ll see in section 12.4.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u60a8\u6ca1\u6709\u5728 AppDbContext \u4e0a\u5217\u51fa Ingredient\uff0c\u4f46 EF Core \u4f1a\u6b63\u786e\u5efa\u6a21\uff0c\u56e0\u4e3a\u5b83\u5728 Recipe \u4e0a\u516c\u5f00\u3002\u60a8\u4ecd\u7136\u53ef\u4ee5\u8bbf\u95ee\u6570\u636e\u5e93\u4e2d\u7684 Ingredient \u5bf9\u8c61\uff0c\u4f46\u5fc5\u987b\u901a\u8fc7 Recipe \u5b9e\u4f53\u7684 Ingredients \u5c5e\u6027\u8fdb\u884c\u5bfc\u822a\uff0c\u5982\u7b2c 12.4 \u8282\u6240\u793a\u3002<\/p>\n<p>For this simple example, your data model consists of these three classes: AppDbContext, Recipe, and Ingredient. The two entities are mapped to tables and their columns to properties, and you use the AppDbContext to access them.<\/p>\n<p>\u5bf9\u4e8e\u8fd9\u4e2a\u7b80\u5355\u7684\u793a\u4f8b\uff0c\u60a8\u7684\u6570\u636e\u6a21\u578b\u7531\u4ee5\u4e0b\u4e09\u4e2a\u7c7b\u7ec4\u6210\uff1aAppDbContext\u3001Recipe \u548c Ingredient\u3002\u8fd9\u4e24\u4e2a\u5b9e\u4f53\u6620\u5c04\u5230\u8868\uff0c\u5b83\u4eec\u7684\u5217\u6620\u5c04\u5230\u5c5e\u6027\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 AppDbContext \u8bbf\u95ee\u5b83\u4eec\u3002<\/p>\n<p><strong>Note<\/strong> This code-first approach is typical, but if you have an existing database, you can automatically generate the EF entities and DbContext instead. (You can find more information in Microsoft\u2019s \u201creverse engineering\u201d article at <a href=\"http:\/\/mng.bz\/mgd4\">http:\/\/mng.bz\/mgd4<\/a>.)<br \/>\n<strong>\u6ce8\u610f<\/strong> \u8fd9\u79cd\u4ee3\u7801\u4f18\u5148\u65b9\u6cd5\u662f\u5178\u578b\u7684\uff0c\u4f46\u5982\u679c\u4f60\u6709\u73b0\u6709\u6570\u636e\u5e93\uff0c\u5219\u53ef\u4ee5\u81ea\u52a8\u751f\u6210 EF \u5b9e\u4f53\u548c DbContext\u3002\uff08\u60a8\u53ef\u4ee5\u5728 Microsoft \u7684\u201c\u9006\u5411\u5de5\u7a0b\u201d\u6587\u7ae0\u4e2d\u627e\u5230\u66f4\u591a\u4fe1\u606f\uff0c\u7f51\u5740\u4e3a <a href=\"http:\/\/mng.bz\/mgd4\">http:\/\/mng.bz\/mgd4<\/a>\u3002<\/p>\n<p>The data model is complete, but you\u2019re not quite ready to use it: your ASP.NET Core app doesn\u2019t know how to create your AppDbContext, and your AppDbContext needs a connection string so that it can talk to the database. In section 12.2.3 we tackle both of these problems, and we finish setting up EF Core in your ASP.NET Core app.<\/p>\n<p>\u6570\u636e\u6a21\u578b\u5df2\u5b8c\u6210\uff0c\u4f46\u60a8\u8fd8\u6ca1\u6709\u5b8c\u5168\u51c6\u5907\u597d\u4f7f\u7528\u5b83\uff1a\u60a8\u7684 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e0d\u77e5\u9053\u5982\u4f55\u521b\u5efa AppDbContext\uff0c\u5e76\u4e14\u60a8\u7684 AppDbContext \u9700\u8981\u4e00\u4e2a\u8fde\u63a5\u5b57\u7b26\u4e32\uff0c\u4ee5\u4fbf\u5b83\u53ef\u4ee5\u4e0e\u6570\u636e\u5e93\u901a\u4fe1\u3002\u5728\u7b2c 12.2.3 \u8282\u4e2d\uff0c\u6211\u4eec\u89e3\u51b3\u4e86\u8fd9\u4e24\u4e2a\u95ee\u9898\uff0c\u5e76\u5b8c\u6210\u4e86 ASP.NET Core \u5e94\u7528\u4e2d\u7684 EF Core \u8bbe\u7f6e\u3002<\/p>\n<h3>12.2.3 Registering a data context<\/h3>\n<h3>12.2.3 \u6ce8\u518c\u6570\u636e\u4e0a\u4e0b\u6587<\/h3>\n<p>As with any other service in ASP.Net Core, you should register your AppDbContext with the dependency injection (DI) container. When registering your context, you also configure the database provider and set the connection string so that EF Core knows how to talk with the database.<\/p>\n<p>\u4e0e ASP.Net Core \u4e2d\u7684\u4efb\u4f55\u5176\u4ed6\u670d\u52a1\u4e00\u6837\uff0c\u60a8\u5e94\u8be5\u5411\u4f9d\u8d56\u9879\u6ce8\u5165 \uff08DI\uff09 \u5bb9\u5668\u6ce8\u518c AppDbContext\u3002\u6ce8\u518c\u4e0a\u4e0b\u6587\u65f6\uff0c\u8fd8\u8981\u914d\u7f6e\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u5e76\u8bbe\u7f6e\u8fde\u63a5\u5b57\u7b26\u4e32\uff0c\u4ee5\u4fbf EF Core \u77e5\u9053\u5982\u4f55\u4e0e\u6570\u636e\u5e93\u901a\u4fe1\u3002<\/p>\n<p>You register the AppDbContext with the WebApplicationBuilder in Program.cs. EF Core provides a generic AddDbContext<T> extension method for this purpose; the method takes a configuration function for a DbContextOptionsBuilder instance. This builder can set a host of internal properties of EF Core and lets you replace all the internal services of EF Core if you want.<\/p>\n<p>\u60a8\u53ef\u4ee5\u5728 Program.cs \u4e2d\u5411 WebApplicationBuilder \u6ce8\u518c AppDbContext\u3002EF Core \u4e3a\u6b64\u63d0\u4f9b\u4e86\u901a\u7528\u7684 AddDbContext<T> \u6269\u5c55\u65b9\u6cd5;\u8be5\u65b9\u6cd5\u91c7\u7528 DbContextOptionsBuilder \u5b9e\u4f8b\u7684\u914d\u7f6e\u51fd\u6570\u3002\u6b64\u751f\u6210\u5668\u53ef\u4ee5\u8bbe\u7f6e EF Core \u7684\u5927\u91cf\u5185\u90e8\u5c5e\u6027\uff0c\u5e76\u5141\u8bb8\u4f60\u6839\u636e\u9700\u8981\u66ff\u6362 EF Core \u7684\u6240\u6709\u5185\u90e8\u670d\u52a1\u3002<\/p>\n<p>The configuration for your app is, again, nice and simple, as you can see in the following listing. You set the database provider with the UseSqlite extension method, made available by the Microsoft.EntityFrameworkCore.SQLite package, and pass it a connection string.<\/p>\n<p>\u540c\u6837\uff0c\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u7684\u914d\u7f6e\u65e2\u6f02\u4eae\u53c8\u7b80\u5355\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 UseSqlite \u6269\u5c55\u65b9\u6cd5\uff08\u7531 Microsoft.EntityFrameworkCore.SQLite \u5305\u63d0\u4f9b\uff09\u8bbe\u7f6e\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\uff0c\u5e76\u5411\u5176\u4f20\u9012\u8fde\u63a5\u5b57\u7b26\u4e32\u3002<\/p>\n<p>Listing 12.4 Registering a DbContext with the DI container<br \/>\n\u6e05\u5355 12.4 \u5411 DI \u6ce8\u518c DbContext\u5bb9\u5668<\/p>\n<pre><code>using Microsoft.EntityFrameworkCore;\nWebApplicationBuillder builder = WebApplication.CreateBuilder(args);\nvar connString = builder.Configuration                  \u2776\n        .GetConnectionString(&quot;DefaultConnection&quot;);      \u2776\n\nBuilder.Services.AddDbContext&lt;AppDbContext&gt;(            \u2777\n        options =&gt; options.UseSqlite(connString));   \u2778\n\nWebApplication app = builder.Build();\napp.Run();<\/code><\/pre>\n<p>\u2776 The connection string is taken from configuration, from the ConnectionStrings<br \/>\nsection.<br \/>\n\u8fde\u63a5\u5b57\u7b26\u4e32\u53d6\u81ea\u914d\u7f6e\u7684 ConnectionStrings \u90e8\u5206\u3002<br \/>\n\u2777 Registers your app\u2019s DbContext by using it as the generic parameter<br \/>\n\u901a\u8fc7\u5c06\u5e94\u7528\u7684 DbContext \u7528\u4f5c\u6cdb\u578b\u53c2\u6570\u6765\u6ce8\u518c\u8be5\u5e94\u7528\u7684 DbContext<br \/>\n\u2778 Specifies the database provider in the customization options for the<br \/>\nDbContext.<br \/>\n\u5728 DbContext \u7684\u81ea\u5b9a\u4e49\u9009\u9879\u4e2d\u6307\u5b9a\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u3002<\/p>\n<p><strong>Note<\/strong> If you\u2019re using a different database provider, such as a provider for SQL Server, you need to call the appropriate Use<em> method on the options object when registering your AppDbContext.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5982\u679c\u60a8\u4f7f\u7528\u7684\u662f\u5176\u4ed6\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\uff08\u4f8b\u5982 SQL Server \u7684\u63d0\u4f9b\u7a0b\u5e8f\uff09\uff0c\u5219\u9700\u8981\u5728\u6ce8\u518c AppDbContext \u65f6\u5bf9 options \u5bf9\u8c61\u8c03\u7528\u76f8\u5e94\u7684 Use<\/em> \u65b9\u6cd5\u3002<\/p>\n<p>The connection string is a typical secret, as I discussed in chapter 10, so loading it from configuration makes sense. At runtime the correct configuration string for your current environment is used, so you can use different databases when developing locally and in production.<\/p>\n<p>\u6b63\u5982\u6211\u5728\u7b2c 10 \u7ae0\u4e2d\u8ba8\u8bba\u7684\u90a3\u6837\uff0c\u8fde\u63a5\u5b57\u7b26\u4e32\u662f\u4e00\u4e2a\u5178\u578b\u7684\u5bc6\u94a5\uff0c\u56e0\u6b64\u4ece\u914d\u7f6e\u4e2d\u52a0\u8f7d\u5b83\u662f\u6709\u610f\u4e49\u7684\u3002\u5728\u8fd0\u884c\u65f6\uff0c\u5c06\u4f7f\u7528\u5f53\u524d\u73af\u5883\u7684\u6b63\u786e\u914d\u7f6e\u5b57\u7b26\u4e32\uff0c\u56e0\u6b64\u5728\u672c\u5730\u5f00\u53d1\u548c\u751f\u4ea7\u65f6\u53ef\u4ee5\u4f7f\u7528\u4e0d\u540c\u7684\u6570\u636e\u5e93\u3002<\/p>\n<p><strong>Tip<\/strong> You can configure your AppDbContext\u2019s connection string in other ways, such as with the OnConfiguring method, but I recommend the method shown here for ASP.NET Core websites.<br \/>\n<strong>\u63d0\u793a<\/strong> \u60a8\u53ef\u4ee5\u901a\u8fc7\u5176\u4ed6\u65b9\u5f0f\u914d\u7f6e AppDbContext \u7684\u8fde\u63a5\u5b57\u7b26\u4e32\uff0c\u4f8b\u5982\u4f7f\u7528 OnConfiguring \u65b9\u6cd5\uff0c\u4f46\u6211\u5efa\u8bae\u5bf9 ASP.NET Core \u7f51\u7ad9\u4f7f\u7528\u6b64\u5904\u663e\u793a\u7684\u65b9\u6cd5\u3002<\/p>\n<p>Now you have a DbContext, named AppDbContext, registered as a scoped service with the DI container (typical for database-related services), and a data model corresponding to your database. Codewise, you\u2019re ready to start using EF Core, but the one thing you don\u2019t have is a database! In section 12.3 you\u2019ll see how you can easily use the .NET CLI to ensure that your database stays up to date with your EF Core data model.<\/p>\n<p>\u73b0\u5728\uff0c\u60a8\u6709\u4e00\u4e2a\u540d\u4e3a AppDbContext \u7684 DbContext\uff0c\u5b83\u6ce8\u518c\u4e3a DI \u5bb9\u5668\u7684\u4f5c\u7528\u57df\u670d\u52a1\uff08\u901a\u5e38\u7528\u4e8e\u6570\u636e\u5e93\u76f8\u5173\u670d\u52a1\uff09\uff0c\u4ee5\u53ca\u4e0e\u60a8\u7684\u6570\u636e\u5e93\u5bf9\u5e94\u7684\u6570\u636e\u6a21\u578b\u3002\u5728\u4ee3\u7801\u65b9\u9762\uff0c\u4f60\u5df2\u51c6\u5907\u597d\u5f00\u59cb\u4f7f\u7528 EF Core\uff0c\u4f46\u4f60\u6ca1\u6709\u7684\u4e00\u4ef6\u4e8b\u662f\u6570\u636e\u5e93\uff01\u5728\u7b2c 12.3 \u8282\u4e2d\uff0c\u4f60\u5c06\u4e86\u89e3\u5982\u4f55\u8f7b\u677e\u4f7f\u7528 .NET CLI \u6765\u786e\u4fdd\u6570\u636e\u5e93\u4e0e EF Core \u6570\u636e\u6a21\u578b\u4fdd\u6301\u540c\u6b65\u3002<\/p>\n<h2>12.3 Managing changes with migrations<\/h2>\n<h2>12.3 \u901a\u8fc7\u8fc1\u79fb\u7ba1\u7406\u66f4\u6539<\/h2>\n<p>In this section you\u2019ll learn how to generate SQL statements to keep your database\u2019s schema in sync with your application\u2019s data model, using migrations. You\u2019ll learn how to create an initial migration and use it to create the database. Then you\u2019ll update your data model, create a second migration, and use it to update the database schema.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528\u8fc1\u79fb\u751f\u6210 SQL \u8bed\u53e5\uff0c\u4ee5\u4f7f\u6570\u636e\u5e93\u7684\u67b6\u6784\u4e0e\u5e94\u7528\u7a0b\u5e8f\u7684\u6570\u636e\u6a21\u578b\u4fdd\u6301\u540c\u6b65\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u521b\u5efa\u521d\u59cb\u8fc1\u79fb\u5e76\u4f7f\u7528\u5b83\u6765\u521b\u5efa\u6570\u636e\u5e93\u3002\u7136\u540e\uff0c\u60a8\u5c06\u66f4\u65b0\u6570\u636e\u6a21\u578b\uff0c\u521b\u5efa\u7b2c\u4e8c\u4e2a\u8fc1\u79fb\uff0c\u5e76\u4f7f\u7528\u5b83\u6765\u66f4\u65b0\u6570\u636e\u5e93\u67b6\u6784\u3002<\/p>\n<p>Managing schema changes for databases, such as when you need to add a new table or a new column, is notoriously difficult. Your application code is explicitly tied to a particular version of a database, and you need to make sure that the two are always in sync.<\/p>\n<p>\u4f17\u6240\u5468\u77e5\uff0c\u7ba1\u7406\u6570\u636e\u5e93\u7684\u67b6\u6784\u66f4\u6539\uff08\u4f8b\u5982\uff0c\u5f53\u60a8\u9700\u8981\u6dfb\u52a0\u65b0\u8868\u6216\u65b0\u5217\u65f6\uff09\u975e\u5e38\u56f0\u96be\u3002\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u660e\u786e\u7ed1\u5b9a\u5230\u6570\u636e\u5e93\u7684\u7279\u5b9a\u7248\u672c\uff0c\u60a8\u9700\u8981\u786e\u4fdd\u4e24\u8005\u59cb\u7ec8\u540c\u6b65\u3002<\/p>\n<p><strong>Definition<\/strong> Schema refers to how the data is organized in a database, including the tables, columns, and relationships among them.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u67b6\u6784\u662f\u6307\u6570\u636e\u5728\u6570\u636e\u5e93\u4e2d\u7684\u7ec4\u7ec7\u65b9\u5f0f\uff0c\u5305\u62ec\u8868\u3001\u5217\u4ee5\u53ca\u5b83\u4eec\u4e4b\u95f4\u7684\u5173\u7cfb\u3002<\/p>\n<p>When you deploy an app, normally you can delete the old code\/executable and replace it with the new code. Job done. If you need to roll back a change, delete that new code, and deploy an old version of the app.<\/p>\n<p>\u90e8\u7f72\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u901a\u5e38\u53ef\u4ee5\u5220\u9664\u65e7\u4ee3\u7801\/\u53ef\u6267\u884c\u6587\u4ef6\u5e76\u5c06\u5176\u66ff\u6362\u4e3a\u65b0\u4ee3\u7801\u3002\u5de5\u4f5c\u5b8c\u6210\u3002\u5982\u679c\u60a8\u9700\u8981\u56de\u6eda\u66f4\u6539\uff0c\u8bf7\u5220\u9664\u8be5\u65b0\u4ee3\u7801\uff0c\u7136\u540e\u90e8\u7f72\u65e7\u7248\u672c\u7684\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>The difficulty with databases is that they contain data, so blowing it away and creating a new database with every deployment isn\u2019t possible. A common best practice is to version a database\u2019s schema explicitly along with your application\u2019s code. You can do this in many ways, but typically you need to store the SQL script that takes the database from the previous schema to the new schema. Then you can use a library such as DbUp (<a href=\"https:\/\/github.com\/DbUp\/DbUp\">https:\/\/github.com\/DbUp\/DbUp<\/a>) or FluentMigrator (<a href=\"https:\/\/github.com\/fluentmigrator\/fluentmigrator\">https:\/\/github.com\/fluentmigrator\/fluentmigrator<\/a>) to keep track of which scripts have been applied and ensure that your database schema is up to date. Alternatively, you can use external tools to manage this task.<\/p>\n<p>\u6570\u636e\u5e93\u7684\u96be\u70b9\u5728\u4e8e\u5b83\u4eec\u5305\u542b\u6570\u636e\uff0c\u56e0\u6b64\u4e0d\u53ef\u80fd\u5728\u6bcf\u6b21\u90e8\u7f72\u65f6\u90fd\u5c06\u5176\u5439\u8d70\u5e76\u521b\u5efa\u4e00\u4e2a\u65b0\u6570\u636e\u5e93\u3002\u4e00\u79cd\u5e38\u89c1\u7684\u6700\u4f73\u5b9e\u8df5\u662f\u5c06\u6570\u636e\u5e93\u7684\u67b6\u6784\u4e0e\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u4e00\u8d77\u663e\u5f0f\u8fdb\u884c\u7248\u672c\u63a7\u5236\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u591a\u79cd\u65b9\u5f0f\u6267\u884c\u6b64\u4f5c\uff0c\u4f46\u901a\u5e38\u9700\u8981\u5b58\u50a8\u5c06\u6570\u636e\u5e93\u4ece\u4ee5\u524d\u7684\u67b6\u6784\u8f6c\u79fb\u5230\u65b0\u67b6\u6784\u7684 SQL \u811a\u672c\u3002\u7136\u540e\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 DbUp \uff08<a href=\"https:\/\/github.com\/DbUp\/DbUp\">https:\/\/github.com\/DbUp\/DbUp<\/a>\uff09 \u6216 FluentMigrator \uff08<a href=\"https:\/\/github.com\/fluentmigrator\/fluentmigrator\">https:\/\/github.com\/fluentmigrator\/fluentmigrator<\/a>\uff09 \u7b49\u5e93\u6765\u8ddf\u8e2a\u5df2\u5e94\u7528\u7684\u811a\u672c\uff0c\u5e76\u786e\u4fdd\u60a8\u7684\u6570\u636e\u5e93\u67b6\u6784\u662f\u6700\u65b0\u7684\u3002\u6216\u8005\uff0c\u60a8\u4e5f\u53ef\u4ee5\u4f7f\u7528\u5916\u90e8\u5de5\u5177\u6765\u7ba1\u7406\u6b64\u4efb\u52a1\u3002<\/p>\n<p>EF Core provides its own version of schema management called migrations. Migrations provide a way to manage changes to a database schema when your EF Core data model changes.<\/p>\n<p>EF Core \u63d0\u4f9b\u81ea\u5df1\u7684\u67b6\u6784\u7ba1\u7406\u7248\u672c\uff0c\u79f0\u4e3a\u8fc1\u79fb\u3002 \u8fc1\u79fb\u63d0\u4f9b\u4e86\u4e00\u79cd\u5728 EF Core \u6570\u636e\u6a21\u578b\u66f4\u6539\u65f6\u7ba1\u7406\u6570\u636e\u5e93\u67b6\u6784\u66f4\u6539\u7684\u65b9\u6cd5\u3002<\/p>\n<p><strong>Definition<\/strong> A migration is a C# code file in your application that defines how the data model changed\u2014which columns were added, new entities, and so on. Migrations provide a record over time of how your database schema evolved as part of your application, so the schema is always in sync with your app\u2019s data model.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u8fc1\u79fb\u662f\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 C# \u4ee3\u7801\u6587\u4ef6\uff0c\u7528\u4e8e\u5b9a\u4e49\u6570\u636e\u6a21\u578b\u7684\u66f4\u6539\u65b9\u5f0f - \u6dfb\u52a0\u4e86\u54ea\u4e9b\u5217\u3001\u65b0\u5b9e\u4f53\u7b49\u3002\u8fc1\u79fb\u4f1a\u8bb0\u5f55\u6570\u636e\u5e93 Schema \u4f5c\u4e3a\u5e94\u7528\u7a0b\u5e8f\u7684\u4e00\u90e8\u5206\u5982\u4f55\u968f\u65f6\u95f4\u6f14\u53d8\uff0c\u56e0\u6b64 Schema \u59cb\u7ec8\u4e0e\u5e94\u7528\u7684\u6570\u636e\u6a21\u578b\u4fdd\u6301\u540c\u6b65\u3002<\/p>\n<p>You can use command-line tools to create a new database from the migrations or to update an existing database by applying new migrations to it. You can even roll back a migration, which updates a database to a previous schema.<\/p>\n<p>\u60a8\u53ef\u4ee5\u4f7f\u7528\u547d\u4ee4\u884c\u5de5\u5177\u4ece\u8fc1\u79fb\u4e2d\u521b\u5efa\u65b0\u6570\u636e\u5e93\uff0c\u6216\u8005\u901a\u8fc7\u5411\u73b0\u6709\u6570\u636e\u5e93\u5e94\u7528\u65b0\u7684\u8fc1\u79fb\u6765\u66f4\u65b0\u73b0\u6709\u6570\u636e\u5e93\u3002\u60a8\u751a\u81f3\u53ef\u4ee5\u56de\u6eda\u8fc1\u79fb\uff0c\u8fd9\u4f1a\u5c06\u6570\u636e\u5e93\u66f4\u65b0\u5230\u4ee5\u524d\u7684\u67b6\u6784\u3002<\/p>\n<p><strong>Warning<\/strong> Applying migrations modifies the database, so you must always be aware of data loss. If you remove a table from the database using a migration and then roll back the migration, the table will be re-created, but the data it previously contained will be gone forever!<br \/>\n<strong>\u8b66\u544a<\/strong> \u5e94\u7528\u8fc1\u79fb\u4f1a\u4fee\u6539\u6570\u636e\u5e93\uff0c\u56e0\u6b64\u60a8\u5fc5\u987b\u59cb\u7ec8\u6ce8\u610f\u6570\u636e\u4e22\u5931\u3002\u5982\u679c\u60a8\u4f7f\u7528\u8fc1\u79fb\u4ece\u6570\u636e\u5e93\u4e2d\u5220\u9664\u8868\uff0c\u7136\u540e\u56de\u6eda\u8fc1\u79fb\uff0c\u5219\u4f1a\u91cd\u65b0\u521b\u5efa\u8be5\u8868\uff0c\u4f46\u4e4b\u524d\u5305\u542b\u7684\u6570\u636e\u5c06\u6c38\u8fdc\u6d88\u5931\uff01<\/p>\n<p>In this section, you\u2019ll see how to create your first migration and use it to create a database. Then you\u2019ll update your data model, create a second migration, and use it to update the database schema.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u521b\u5efa\u60a8\u7684\u7b2c\u4e00\u4e2a\u8fc1\u79fb\u5e76\u4f7f\u7528\u5b83\u6765\u521b\u5efa\u6570\u636e\u5e93\u3002\u7136\u540e\uff0c\u60a8\u5c06\u66f4\u65b0\u6570\u636e\u6a21\u578b\uff0c\u521b\u5efa\u7b2c\u4e8c\u4e2a\u8fc1\u79fb\uff0c\u5e76\u4f7f\u7528\u5b83\u6765\u66f4\u65b0\u6570\u636e\u5e93\u67b6\u6784\u3002<\/p>\n<h3>12.3.1 Creating your first migration<\/h3>\n<h3>12.3.1 \u521b\u5efa\u60a8\u7684\u7b2c\u4e00\u4e2a\u8fc1\u79fb<\/h3>\n<p>Before you can create migrations, you need to install the necessary tooling. You have two primary ways to do this:<br \/>\n\u5728\u521b\u5efa\u8fc1\u79fb\u4e4b\u524d\uff0c\u60a8\u9700\u8981\u5b89\u88c5\u5fc5\u8981\u7684\u5de5\u5177\u3002\u6709\u4e24\u79cd\u4e3b\u8981\u65b9\u6cd5\u53ef\u4ee5\u6267\u884c\u6b64\u4f5c\uff1a<\/p>\n<ul>\n<li>\n<p>Package manager console\u2014You can use PowerShell cmdlets inside Visual Studio\u2019s Package Manager Console (PMC). You can install them directly from the PMC or by adding the Microsoft.EntityFrameworkCore.Tools package to your project.<br \/>\n\u5305\u7ba1\u7406\u5668\u63a7\u5236\u53f0 \u2014 \u60a8\u53ef\u4ee5\u5728 Visual Studio \u7684\u5305\u7ba1\u7406\u5668\u63a7\u5236\u53f0 \uff08PMC\uff09 \u4e2d\u4f7f\u7528 PowerShell cmdlet\u3002\u60a8\u53ef\u4ee5\u76f4\u63a5\u4ece PMC \u5b89\u88c5\u5b83\u4eec\uff0c\u4e5f\u53ef\u4ee5\u901a\u8fc7\u5c06 Microsoft.EntityFrameworkCore.Tools \u5305\u6dfb\u52a0\u5230\u60a8\u7684\u9879\u76ee\u6765\u5b89\u88c5\u5b83\u4eec\u3002<\/p>\n<\/li>\n<li>\n<p>.NET tool\u2014You can use cross-platform, command-line tooling that extends the .NET software development kit (SDK). You can install the EF Core .NET tool globally for your machine by running dotnet tool install --global dotnet-ef.<br \/>\n.NET \u5de5\u5177 \u2014 \u60a8\u53ef\u4ee5\u4f7f\u7528\u6269\u5c55 .NET SDK \u7684\u8de8\u5e73\u53f0\u547d\u4ee4\u884c\u5de5\u5177\u3002\u53ef\u4ee5\u901a\u8fc7\u8fd0\u884c dotnet tool install -- global dotnet-ef \u4e3a\u8ba1\u7b97\u673a\u5168\u5c40\u5b89\u88c5 EF Core .NET \u5de5\u5177\u3002<\/p>\n<\/li>\n<\/ul>\n<p>In this book I use the cross-platform .NET tools, but if you\u2019re familiar with EF 6.x or prefer to use the Visual Studio PMC, there are equivalent commands for the steps you\u2019re going to take (<a href=\"http:\/\/mng.bz\/9DK7\">http:\/\/mng.bz\/9DK7<\/a>). You can check that the .NET tool installed correctly by running dotnet ef, which should produce a help screen like the one shown in figure 12.6.<\/p>\n<p>\u5728\u672c\u4e66\u4e2d\uff0c\u6211\u4f7f\u7528\u4e86\u8de8\u5e73\u53f0\u7684 .NET \u5de5\u5177\uff0c\u4f46\u5982\u679c\u60a8\u719f\u6089 EF 6.x \u6216\u66f4\u559c\u6b22\u4f7f\u7528 Visual Studio PMC\uff0c\u5219\u5bf9\u4e8e\u60a8\u5c06\u8981\u6267\u884c\u7684\u6b65\u9aa4 \uff08<a href=\"http:\/\/mng.bz\/9DK7\uff09\uff0c\u6709\u7b49\u6548\u7684\u547d\u4ee4\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8fd0\u884c\">http:\/\/mng.bz\/9DK7\uff09\uff0c\u6709\u7b49\u6548\u7684\u547d\u4ee4\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8fd0\u884c<\/a> dotnet ef \u6765\u68c0\u67e5 .NET \u5de5\u5177\u662f\u5426\u6b63\u786e\u5b89\u88c5\uff0c\u8fd9\u5e94\u8be5\u4f1a\u4ea7\u751f\u5982\u56fe 12.6 \u6240\u793a\u7684\u5e2e\u52a9\u5c4f\u5e55\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1206.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.6 Running the dotnet ef command to check that the .NET EF Core tools are installed correctly<br \/>\n\u56fe 12.6 \u8fd0\u884c dotnet ef \u547d\u4ee4\u4ee5\u68c0\u67e5 .NET EF Core \u5de5\u5177\u662f\u5426\u5df2\u6b63\u786e\u5b89\u88c5<\/p>\n<p><strong>Tip<\/strong> If you get the No executable found matching command \u2018dotnet-ef\u2019 message when running the preceding command, make sure that you installed the global tool by using dotnet tool install --global dotnet-ef. In general, you need to run the dotnet ef tools from the project folder in which you registered your AppDbContext\u2014not from the solution-folder level.<br \/>\n\u63d0\u793a \u5982\u679c\u5728\u8fd0\u884c\u4e0a\u8ff0\u547d\u4ee4\u65f6\u6536\u5230 No executable found matching command 'dotnet-ef' \u6d88\u606f\uff0c\u8bf7\u786e\u4fdd\u4f7f\u7528 dotnet tool install --global \u5b89\u88c5\u4e86\u5168\u5c40\u5de5\u5177dotnet-ef \u7684\u901a\u5e38\uff0c\u60a8\u9700\u8981\u4ece\u6ce8\u518c AppDbContext \u7684\u9879\u76ee\u6587\u4ef6\u5939\u8fd0\u884c dotnet ef \u5de5\u5177\uff0c\u800c\u4e0d\u662f\u4ece\u89e3\u51b3\u65b9\u6848\u6587\u4ef6\u5939\u7ea7\u522b\u8fd0\u884c\u3002<\/p>\n<p>With the tools installed and your database context configured, you can create your first migration by running the following command from inside your web project folder and providing a name for the migration (in this case, InitialSchema):<br \/>\n\u5b89\u88c5\u5de5\u5177\u5e76\u914d\u7f6e\u6570\u636e\u5e93\u4e0a\u4e0b\u6587\u540e\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u4ece Web \u9879\u76ee\u6587\u4ef6\u5939\u5185\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\u5e76\u63d0\u4f9b\u8fc1\u79fb\u540d\u79f0\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a InitialSchema\uff09\u6765\u521b\u5efa\u7b2c\u4e00\u4e2a\u8fc1\u79fb\uff1a<\/p>\n<pre><code>dotnet ef migrations add InitialSchema<\/code><\/pre>\n<p>This command creates three files in the Migrations folder in your project:<br \/>\n\u6b64\u547d\u4ee4\u5728\u9879\u76ee\u7684 Migrations \u6587\u4ef6\u5939\u4e2d\u521b\u5efa\u4e09\u4e2a\u6587\u4ef6\uff1a<\/p>\n<ul>\n<li>\n<p>Migration file\u2014This file, with the Timestamp_MigrationName.cs format, describes the actions to take on the database, such as creating a table or adding a column. Note that the commands generated here are database-provider-specific, based on the database provider configured in your project.<br \/>\n\u8fc1\u79fb\u6587\u4ef6 - \u6b64\u6587\u4ef6\u91c7\u7528 Timestamp_MigrationName.cs \u683c\u5f0f\uff0c\u63cf\u8ff0\u8981\u5bf9\u6570\u636e\u5e93\u6267\u884c\u7684\u4f5c\uff0c\u4f8b\u5982\u521b\u5efa\u8868\u6216\u6dfb\u52a0\u5217\u3002\u8bf7\u6ce8\u610f\uff0c\u6b64\u5904\u751f\u6210\u7684\u547d\u4ee4\u662f\u7279\u5b9a\u4e8e\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u7684\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u9879\u76ee\u4e2d\u914d\u7f6e\u7684\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u3002<\/p>\n<\/li>\n<li>\n<p>Migration designer.cs file\u2014This file describes EF Core\u2019s internal model of your data model at the point in time when the migration was generated.<br \/>\n\u8fc1\u79fbdesigner.cs\u6587\u4ef6 \u2013 \u6b64\u6587\u4ef6\u63cf\u8ff0\u5728\u751f\u6210\u8fc1\u79fb\u65f6\u6570\u636e\u6a21\u578b\u7684 EF Core \u5185\u90e8\u6a21\u578b\u3002<\/p>\n<\/li>\n<li>\n<p>AppDbContextModelSnapshot.cs\u2014This file describes EF Core\u2019s current internal model. This file is updated when you add another migration, so it should always be the same as the current (latest) migration. EF Core can use AppDbContextModelSnapshot.cs to determine a database\u2019s previous state when creating a new migration without interacting with the database directly.<br \/>\nAppDbContextModelSnapshot.cs \u2013 \u6b64\u6587\u4ef6\u63cf\u8ff0 EF Core \u7684\u5f53\u524d\u5185\u90e8\u6a21\u578b\u3002\u5f53\u60a8\u6dfb\u52a0\u53e6\u4e00\u4e2a\u8fc1\u79fb\u65f6\uff0c\u6b64\u6587\u4ef6\u4f1a\u66f4\u65b0\uff0c\u56e0\u6b64\u5b83\u5e94\u59cb\u7ec8\u4e0e\u5f53\u524d\uff08\u6700\u65b0\uff09\u8fc1\u79fb\u76f8\u540c\u3002EF Core \u53ef\u4ee5\u5728\u521b\u5efa\u65b0\u8fc1\u79fb\u65f6\u4f7f\u7528 AppDbContextModelSnapshot.cs \u6765\u786e\u5b9a\u6570\u636e\u5e93\u7684\u5148\u524d\u72b6\u6001\uff0c\u800c\u65e0\u9700\u76f4\u63a5\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u3002<\/p>\n<\/li>\n<\/ul>\n<p>These three files encapsulate the migration process, but adding a migration doesn\u2019t update anything in the database itself. For that task, you must run a different command to apply the migration to the database.<\/p>\n<p>\u8fd9\u4e09\u4e2a\u6587\u4ef6\u5c01\u88c5\u4e86\u8fc1\u79fb\u8fc7\u7a0b\uff0c\u4f46\u6dfb\u52a0\u8fc1\u79fb\u4e0d\u4f1a\u66f4\u65b0\u6570\u636e\u5e93\u672c\u8eab\u7684\u4efb\u4f55\u5185\u5bb9\u3002\u5bf9\u4e8e\u8be5\u4efb\u52a1\uff0c\u60a8\u5fc5\u987b\u8fd0\u884c\u5176\u4ed6\u547d\u4ee4\u624d\u80fd\u5c06\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u3002<\/p>\n<p><strong>Tip<\/strong> You can, and should, look inside the migration file EF Core generates to check what it will do to your database before running the following commands. Better safe than sorry!<br \/>\n<strong>\u63d0\u793a<\/strong> \u5728\u8fd0\u884c\u4ee5\u4e0b\u547d\u4ee4\u4e4b\u524d\uff0c\u60a8\u53ef\u4ee5\u800c\u4e14\u5e94\u8be5\u67e5\u770b EF Core \u751f\u6210\u7684\u8fc1\u79fb\u6587\u4ef6\uff0c\u4ee5\u68c0\u67e5\u5b83\u5c06\u5bf9\u6570\u636e\u5e93\u6267\u884c\u4ec0\u4e48\u4f5c\u3002\u5b89\u5168\u603b\u6bd4\u540e\u6094\u597d\uff01<\/p>\n<p>You can apply migrations in any of four ways:<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u4ee5\u4e0b\u56db\u79cd\u65b9\u5f0f\u4e2d\u7684\u4efb\u4f55\u4e00\u79cd\u5e94\u7528\u8fc1\u79fb\uff1a<\/p>\n<ul>\n<li>Using the .NET tool<br \/>\n\u4f7f\u7528 .NET \u5de5\u5177<\/li>\n<li>Using the Visual Studio PowerShell cmdlets<br \/>\n\u4f7f\u7528 Visual Studio PowerShell cmdlets<\/li>\n<li>In code, by obtaining an instance of your AppDbContext from the DI container and calling context.Database.Migrate()<br \/>\n\u5728\u4ee3\u7801\u4e2d\uff0c\u901a\u8fc7\u83b7\u53d6AppDbContext \u5e76\u4ece DI \u5bb9\u5668\u4e2d\u8c03\u7528\u4e0a\u4e0b\u6587\u3002Database.Migrate\uff08\uff09 \u6570\u636e\u5e93<\/li>\n<li>By generating a migration bundle application (see <a href=\"http:\/\/mng.bz\/jPyr\">http:\/\/mng.bz\/jPyr<\/a>)<br \/>\n\u901a\u8fc7\u751f\u6210\u8fc1\u79fb\u6346\u7ed1\u5305\u5e94\u7528\u7a0b\u5e8f\uff08\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/jPyr\">http:\/\/mng.bz\/jPyr<\/a>\uff09<\/li>\n<\/ul>\n<p>Which method is best for you depends on how you designed your application, how you\u2019ll update your production database, and what your personal preference is. I\u2019ll use the .NET tool for now, but I discuss some of these considerations in section 12.5. You can apply migrations to a database by running<br \/>\n\u54ea\u79cd\u65b9\u6cd5\u6700\u9002\u5408\u60a8\u53d6\u51b3\u4e8e\u60a8\u5982\u4f55\u8bbe\u8ba1\u5e94\u7528\u7a0b\u5e8f\u3001\u5982\u4f55\u66f4\u65b0\u751f\u4ea7\u6570\u636e\u5e93\u4ee5\u53ca\u60a8\u7684\u4e2a\u4eba\u504f\u597d\u3002\u6211\u5c06\u4f7f\u7528.NET \u5de5\u5177\uff0c\u4f46\u6211\u4f1a\u5728\u7b2c 12.5 \u8282\u4e2d\u8ba8\u8bba\u5176\u4e2d\u7684\u4e00\u4e9b\u6ce8\u610f\u4e8b\u9879\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8fd0\u884c<\/p>\n<pre><code>dotnet ef database update<\/code><\/pre>\n<p>from the project folder of your application. I won\u2019t go into the details on how this command works, but it performs four steps:<br \/>\n\u4ece\u5e94\u7528\u7a0b\u5e8f\u7684 project \u6587\u4ef6\u5939\u4e2d\u3002\u6211\u4e0d\u4f1a\u8be6\u7ec6\u4ecb\u7ecd\u6b64\u547d\u4ee4\u7684\u5de5\u4f5c\u539f\u7406\uff0c\u4f46\u5b83\u6267\u884c\u56db\u4e2a\u6b65\u9aa4\uff1a<\/p>\n<ol>\n<li>Builds your application<br \/>\n\u6784\u5efa\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f<\/li>\n<li>Loads the services configured in your app\u2019s Program.cs, including AppDbContext<br \/>\n\u52a0\u8f7d\u5728\u5e94\u7528\u7a0b\u5e8f\u7684 Program.cs \u4e2d\u914d\u7f6e\u7684\u670d\u52a1\uff0c\u5305\u62ec AppDbContext<\/li>\n<li>Checks whether the database in the AppDbContext connection string exists and if not, creates it<br \/>\n\u68c0\u67e5 AppDbContext \u8fde\u63a5\u5b57\u7b26\u4e32\u4e2d\u7684\u6570\u636e\u5e93\u662f\u5426\u5b58\u5728\uff0c\u5982\u679c\u4e0d\u5b58\u5728\uff0c\u5219\u521b\u5efa\u8be5\u6570\u636e\u5e93<\/li>\n<li>Updates the database by applying any unapplied migrations<br \/>\n\u901a\u8fc7\u5e94\u7528\u4efb\u4f55\u672a\u5e94\u7528\u7684\u8fc1\u79fb\u6765\u66f4\u65b0\u6570\u636e\u5e93<\/li>\n<\/ol>\n<p>If everything is configured correctly, as in section 12.2, running this command sets you up with a shiny new database like the one shown in figure 12.7.<br \/>\n\u5982\u679c\u4e00\u5207\u90fd\u914d\u7f6e\u6b63\u786e\uff0c\u5982 Section 12.2 \u6240\u793a\uff0c\u8fd0\u884c\u6b64\u547d\u4ee4\u5c06\u4e3a\u60a8\u8bbe\u7f6e\u4e00\u4e2a\u95ea\u4eae\u7684\u65b0\u6570\u636e\u5e93\uff0c\u5982\u56fe 12.7 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1207.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.7 Applying migrations to a database creates the database if it doesn\u2019t exist and updates the database to match EF Core\u2019s internal data model. The list of applied migrations is stored in the <strong>EFMigrationsHistory table.<br \/>\n\u56fe 12.7 \u5c06\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u4f1a\u521b\u5efa\u6570\u636e\u5e93\uff08\u5982\u679c\u6570\u636e\u5e93\u4e0d\u5b58\u5728\uff09\uff0c\u5e76\u66f4\u65b0\u6570\u636e\u5e93\u4ee5\u5339\u914d EF Core \u7684\u5185\u90e8\u6570\u636e\u6a21\u578b\u3002\u5e94\u7528\u7684\u8fc1\u79fb\u5217\u8868\u5b58\u50a8\u5728 <\/strong>EFMigrationsHistory \u8868\u3002<\/p>\n<p><strong>Note<\/strong> If you get an error message saying No project was found when running these commands, check that you\u2019re running them in your application\u2019s project folder, not the top-level solution folder.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5982\u679c\u60a8\u5728\u8fd0\u884c\u8fd9\u4e9b\u547d\u4ee4\u65f6\u6536\u5230\u4e00\u6761\u9519\u8bef\u6d88\u606f\uff0c\u6307\u51fa No project was found\uff0c\u8bf7\u68c0\u67e5\u60a8\u662f\u5426\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u9879\u76ee\u6587\u4ef6\u5939\u4e2d\u8fd0\u884c\u5b83\u4eec\uff0c\u800c\u4e0d\u662f\u5728\u9876\u7ea7\u89e3\u51b3\u65b9\u6848\u6587\u4ef6\u5939\u4e2d\u8fd0\u884c\u5b83\u4eec\u3002<\/p>\n<p>When you apply the migrations to the database, EF Core creates the necessary tables in the database and adds the appropriate columns and keys. You may have also noticed the __EFMigrationsHistory table, which EF Core uses to store the names of migrations that it\u2019s applied to the database. Next time you run dotnet ef database update, EF Core can compare this table with the list of migrations in your app and apply only the new ones to your database.<\/p>\n<p>\u5c06\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u65f6\uff0cEF Core \u4f1a\u5728\u6570\u636e\u5e93\u4e2d\u521b\u5efa\u5fc5\u8981\u7684\u8868\uff0c\u5e76\u6dfb\u52a0\u76f8\u5e94\u7684\u5217\u548c\u952e\u3002\u4f60\u53ef\u80fd\u8fd8\u6ce8\u610f\u5230\u4e86 EFMigrationsHistory \u8868\uff0cEF Core \u4f7f\u7528\u8be5\u8868\u6765\u5b58\u50a8\u5b83\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u7684\u8fc1\u79fb\u7684\u540d\u79f0\u3002\u4e0b\u6b21\u8fd0\u884c dotnet ef database update \u65f6\uff0cEF Core \u53ef\u4ee5\u5c06\u6b64\u8868\u4e0e\u5e94\u7528\u4e2d\u7684\u8fc1\u79fb\u5217\u8868\u8fdb\u884c\u6bd4\u8f83\uff0c\u5e76\u4ec5\u5c06\u65b0\u7684\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u3002<\/p>\n<p>In section 12.3.2 we\u2019ll look at how migrations make it easy to change your data model and update the database schema without having to re-create the database from scratch.<\/p>\n<p>\u5728 Section 12.3.2 \u4e2d\uff0c\u6211\u4eec\u5c06\u4e86\u89e3\u8fc1\u79fb\u5982\u4f55\u4f7f\u66f4\u6539\u6570\u636e\u6a21\u578b\u548c\u66f4\u65b0\u6570\u636e\u5e93\u6a21\u5f0f\u53d8\u5f97\u5bb9\u6613\uff0c\u800c\u65e0\u9700\u4ece\u5934\u5f00\u59cb\u91cd\u65b0\u521b\u5efa\u6570\u636e\u5e93\u3002<\/p>\n<h3>12.3.2 Adding a second migration<\/h3>\n<h3>12.3.2 \u6dfb\u52a0\u7b2c\u4e8c\u4e2a\u8fc1\u79fb<\/h3>\n<p>Most applications inevitably evolve due to increased scope or simple maintenance. Adding properties to your entities, adding new entities , and removing obsolete classes are all likely.<\/p>\n<p>\u7531\u4e8e\u8303\u56f4\u6269\u5927\u6216\u7ef4\u62a4\u7b80\u5355\uff0c\u5927\u591a\u6570\u5e94\u7528\u4e0d\u53ef\u907f\u514d\u5730\u4f1a\u4e0d\u65ad\u53d1\u5c55\u3002\u5411\u5b9e\u4f53\u6dfb\u52a0\u5c5e\u6027\u3001\u6dfb\u52a0\u65b0\u5b9e\u4f53\u548c\u5220\u9664\u8fc7\u65f6\u7684\u7c7b\u90fd\u662f\u53ef\u80fd\u7684\u3002<\/p>\n<p>EF Core migrations make this evolution simple. Suppose that you decide to highlight vegetarian and vegan dishes in your recipe app by exposing IsVegetarian and IsVegan properties on the Recipe entity (listing 12.5). Change your entities to your desired state, generate a migration, and apply it to the database, as shown in figure 12.8.<\/p>\n<p>EF Core \u8fc1\u79fb\u4f7f\u8fd9\u79cd\u6f14\u53d8\u53d8\u5f97\u7b80\u5355\u3002\u5047\u8bbe\u60a8\u51b3\u5b9a\u901a\u8fc7\u5728 Recipe \u5b9e\u4f53\uff08\u6e05\u5355 12.5\uff09\u4e0a\u516c\u5f00 IsVegetarian \u548c IsVegan \u5c5e\u6027\uff0c\u5728\u98df\u8c31\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7a81\u51fa\u663e\u793a\u7d20\u98df\u548c\u7eaf\u7d20\u98df\u83dc\u80b4\u3002\u5c06\u5b9e\u4f53\u66f4\u6539\u4e3a\u6240\u9700\u72b6\u6001\uff0c\u751f\u6210\u8fc1\u79fb\u5e76\u5c06\u5176\u5e94\u7528\u4e8e\u6570\u636e\u5e93\uff0c\u5982\u56fe 12.8 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1208.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.8 Creating a second migration and applying it to the database using the command-line tools.<br \/>\n\u56fe 12.8 \u4f7f\u7528\u547d\u4ee4\u884c\u5de5\u5177\u521b\u5efa\u7b2c\u4e8c\u4e2a\u8fc1\u79fb\u5e76\u5c06\u5176\u5e94\u7528\u4e8e\u6570\u636e\u5e93<\/p>\n<p>Listing 12.5 Adding properties to the Recipe entity<br \/>\n\u6e05\u5355 12.5 \u5411 Recipe \u5b9e\u4f53\u6dfb\u52a0\u5c5e\u6027<\/p>\n<pre><code>public class Recipe\n{\n    public int RecipeId { get; set; }\n    public required string Name { get; set; }\n    public TimeSpan TimeToCook { get; set; }\n    public bool IsDeleted { get; set; }\n    public required string Method { get; set; }\n    public bool IsVegetarian { get; set; }\n    public bool IsVegan { get; set; }\n    public required ICollection&lt;Ingredient&gt; Ingredients { get; set; }\n}<\/code><\/pre>\n<p>As shown in figure 12.8, after changing your entities, you need to update EF Core\u2019s internal representation of your data model. You perform this update exactly the same way that you did for the first migration, by calling dotnet ef migrations add and providing a name for the migration:<br \/>\n\u5982\u56fe 12.8 \u6240\u793a\uff0c\u66f4\u6539\u5b9e\u4f53\u540e\uff0c\u60a8\u9700\u8981\u66f4\u65b0 EF Core \u7684\u6570\u636e\u6a21\u578b\u7684\u5185\u90e8\u8868\u793a\u5f62\u5f0f\u3002\u6267\u884c\u6b64\u66f4\u65b0\u7684\u65b9\u5f0f\u4e0e\u7b2c\u4e00\u6b21\u8fc1\u79fb\u5b8c\u5168\u76f8\u540c\uff0c\u65b9\u6cd5\u662f\u8c03\u7528 dotnet ef migrations add \u5e76\u63d0\u4f9b\u8fc1\u79fb\u7684\u540d\u79f0\uff1a<\/p>\n<pre><code>dotnet ef migrations add ExtraRecipeFields<\/code><\/pre>\n<p>This command creates a second migration in your project by adding the migration file and its .designer.cs snapshot file; it also updates AppDbContextModelSnapshot.cs (figure 12.9).<br \/>\n\u6b64\u547d\u4ee4\u901a\u8fc7\u6dfb\u52a0\u8fc1\u79fb\u6587\u4ef6\u53ca\u5176 .designer.cs \u5feb\u7167\u6587\u4ef6\uff0c\u5728\u9879\u76ee\u4e2d\u521b\u5efa\u7b2c\u4e8c\u4e2a\u8fc1\u79fb;\u5b83\u8fd8\u4f1a\u66f4\u65b0 AppDbContextModelSnapshot.cs \uff08\u56fe 12.9\uff09\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1209.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.9 Adding a second migration adds a new migration file and a migration Designer.cs file. It also updates AppDbContextModelSnapshot to match the new migration\u2019s Designer.cs file.<br \/>\n\u56fe 12.9 \u6dfb\u52a0\u7b2c\u4e8c\u4e2a\u8fc1\u79fb\u4f1a\u6dfb\u52a0\u65b0\u7684\u8fc1\u79fb\u6587\u4ef6\u548c\u8fc1\u79fbDesigner.cs\u6587\u4ef6\u3002\u5b83\u8fd8\u66f4\u65b0 AppDbContextModelSnapshot \u4ee5\u5339\u914d\u65b0\u8fc1\u79fb\u7684 Designer.cs \u6587\u4ef6\u3002<\/p>\n<p>As before, this command creates the migration\u2019s files but doesn\u2019t modify the database. You can apply the migration and update the database by running<\/p>\n<p>\u4e0e\u4ee5\u524d\u4e00\u6837\uff0c\u6b64\u547d\u4ee4\u4f1a\u521b\u5efa\u8fc1\u79fb\u7684\u6587\u4ef6\uff0c\u4f46\u4e0d\u4f1a\u4fee\u6539\u6570\u636e\u5e93\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8fd0\u884c<\/p>\n<pre><code>dotnet ef database update<\/code><\/pre>\n<p>This command compares the migrations in your application with the __EFMigrationsHistory table in your database to see which migrations are outstanding; then it runs them. EF Core runs the 20220825201452_ExtraRecipeFields migration, adding the IsVegetarian and IsVegan fields to the database, as shown in figure 12.10.<\/p>\n<p>\u6b64\u547d\u4ee4\u5c06\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u8fc1\u79fb\u4e0e\u6570\u636e\u5e93\u4e2d\u7684 EFMigrationsHistory \u8868\u8fdb\u884c\u6bd4\u8f83\uff0c\u4ee5\u67e5\u770b\u54ea\u4e9b\u8fc1\u79fb\u672a\u5b8c\u6210;\u7136\u540e\u5b83\u8fd0\u884c\u5b83\u4eec\u3002EF Core \u8fd0\u884c 20220825201452_ExtraRecipeFields \u8fc1\u79fb\uff0c\u5c06 IsVegetarian \u548c IsVegan \u5b57\u6bb5\u6dfb\u52a0\u5230\u6570\u636e\u5e93\u4e2d\uff0c\u5982\u56fe 12.10 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1210.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.10 Applying the ExtraRecipeFields migration to the database adds the IsVegetarian and IsVegan fields to the Recipes table.<br \/>\n\u56fe 12.10 \u5c06 ExtraRecipeFields \u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\uff0c\u5c06 IsVegetarian \u548c IsVegan \u5b57\u6bb5\u6dfb\u52a0\u5230 Recipes \u8868\u4e2d\u3002<\/p>\n<p>Using migrations is a great way to ensure that your database is versioned along with your app code in source control. You can easily check out your app\u2019s source code for a historical point in time and re-create the database schema your application used at that point.<\/p>\n<p>\u4f7f\u7528\u8fc1\u79fb\u662f\u786e\u4fdd\u6570\u636e\u5e93\u4e0e\u6e90\u4ee3\u7801\u7ba1\u7406\u4e2d\u7684\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u4e00\u8d77\u8fdb\u884c\u7248\u672c\u63a7\u5236\u7684\u597d\u65b9\u6cd5\u3002\u60a8\u53ef\u4ee5\u8f7b\u677e\u67e5\u770b\u5e94\u7528\u7a0b\u5e8f\u7684\u5386\u53f2\u65f6\u95f4\u70b9\u6e90\u4ee3\u7801\uff0c\u5e76\u91cd\u65b0\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\u5728\u8be5\u65f6\u95f4\u70b9\u4f7f\u7528\u7684\u6570\u636e\u5e93\u67b6\u6784\u3002<\/p>\n<p>Migrations are easy to use when you\u2019re working alone or deploying to a single web server, but even in these cases, you have important things to consider when deciding how to manage your databases. For apps with multiple web servers using a shared database or for containerized applications, you have even more things to think about.<\/p>\n<p>\u5f53\u60a8\u5355\u72ec\u5de5\u4f5c\u6216\u90e8\u7f72\u5230\u5355\u4e2a Web \u670d\u52a1\u5668\u65f6\uff0c\u8fc1\u79fb\u5f88\u5bb9\u6613\u4f7f\u7528\uff0c\u4f46\u5373\u4f7f\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u5728\u51b3\u5b9a\u5982\u4f55\u7ba1\u7406\u6570\u636e\u5e93\u65f6\uff0c\u60a8\u4e5f\u9700\u8981\u8003\u8651\u91cd\u8981\u4e8b\u9879\u3002\u5bf9\u4e8e\u5177\u6709\u591a\u4e2a Web \u670d\u52a1\u5668\u3001\u4f7f\u7528\u5171\u4eab\u6570\u636e\u5e93\u7684\u5e94\u7528\u7a0b\u5e8f\u6216\u5bb9\u5668\u5316\u5e94\u7528\u7a0b\u5e8f\uff0c\u60a8\u9700\u8981\u8003\u8651\u7684\u4e8b\u9879\u66f4\u591a\u3002<\/p>\n<p>This book is about ASP.NET Core, not EF Core, so I don\u2019t want to dwell on database management much. But section 12.5 points out some of the things you need to bear in mind when using migrations in production.<\/p>\n<p>\u8fd9\u672c\u4e66\u662f\u5173\u4e8e ASP.NET Core \u7684\uff0c\u800c\u4e0d\u662f EF Core\uff0c\u56e0\u6b64\u6211\u4e0d\u60f3\u8fc7\u591a\u5730\u8ba8\u8bba\u6570\u636e\u5e93\u7ba1\u7406\u3002\u4f46\u662f Section 12.5 \u6307\u51fa\u4e86\u5728 \u751f\u4ea7\u73af\u5883 \u4e2d\u4f7f\u7528 migrations \u65f6\u9700\u8981\u8bb0\u4f4f\u7684\u4e00\u4e9b\u4e8b\u9879\u3002<\/p>\n<p>In section 12.4 we\u2019ll get back to the meaty stuff: defining our business logic and performing CRUD operations on the database.<\/p>\n<p>\u5728 Section 12.4 \u4e2d\uff0c\u6211\u4eec\u5c06\u56de\u5230\u5185\u5bb9\u4e30\u5bcc\u7684\u4e1c\u897f\uff1a\u5b9a\u4e49\u6211\u4eec\u7684\u4e1a\u52a1\u903b\u8f91\u5e76\u5728\u6570\u636e\u5e93\u4e0a\u6267\u884c CRUD\u4f5c\u3002<\/p>\n<h2>12.4 Querying data from and saving data to the database<\/h2>\n<h2>12.4 \u4ece\u6570\u636e\u5e93\u67e5\u8be2\u6570\u636e\u5e76\u5c06\u6570\u636e\u4fdd\u5b58\u5230\u6570\u636e\u5e93<\/h2>\n<p>Let\u2019s review where you are in creating the recipe application:<br \/>\n\u8ba9\u6211\u4eec\u56de\u987e\u4e00\u4e0b\u60a8\u521b\u5efa\u914d\u65b9\u5e94\u7528\u7a0b\u5e8f\u7684\u4f4d\u7f6e\uff1a<\/p>\n<ul>\n<li>\n<p>You created a simple data model consisting of recipes and ingredients.<br \/>\n\u60a8\u521b\u5efa\u4e86\u4e00\u4e2a\u7531\u914d\u65b9\u548c\u6210\u5206\u7ec4\u6210\u7684\u7b80\u5355\u6570\u636e\u6a21\u578b\u3002<\/p>\n<\/li>\n<li>\n<p>You generated migrations for the data model to update EF Core\u2019s internal model of your entities.<br \/>\n\u60a8\u4e3a\u6570\u636e\u6a21\u578b\u751f\u6210\u4e86\u8fc1\u79fb\uff0c\u4ee5\u66f4\u65b0 EF Core \u7684\u5b9e\u4f53\u5185\u90e8\u6a21\u578b\u3002<\/p>\n<\/li>\n<li>\n<p>You applied the migrations to the database so that its schema matches EF Core\u2019s model.<br \/>\n\u60a8\u5df2\u5c06\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\uff0c\u4f7f\u5176\u67b6\u6784\u4e0e EF Core \u7684\u6a21\u578b\u5339\u914d\u3002<\/p>\n<\/li>\n<\/ul>\n<p>In this section you\u2019ll build the business logic for your application by creating a RecipeService. This service handles querying the database for recipes, creating new recipes, and modifying existing ones. As this app has a simple domain, I\u2019ll be using RecipeService to handle all the requirements, but in your own apps you may have multiple services that cooperate to provide the business logic.<\/p>\n<p>\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u901a\u8fc7\u521b\u5efa RecipeService \u6765\u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u7684\u4e1a\u52a1\u903b\u8f91\u3002\u6b64\u670d\u52a1\u5904\u7406\u5728\u6570\u636e\u5e93\u4e2d\u67e5\u8be2\u914d\u65b9\u3001\u521b\u5efa\u65b0\u914d\u65b9\u548c\u4fee\u6539\u73b0\u6709\u914d\u65b9\u3002\u7531\u4e8e\u6b64\u5e94\u7528\u7a0b\u5e8f\u5177\u6709\u4e00\u4e2a\u7b80\u5355\u7684\u57df\uff0c\u56e0\u6b64\u6211\u5c06\u4f7f\u7528 RecipeService \u6765\u5904\u7406\u6240\u6709\u9700\u6c42\uff0c\u4f46\u5728\u60a8\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u60a8\u53ef\u80fd\u6709\u591a\u4e2a\u670d\u52a1\u76f8\u4e92\u534f\u4f5c\u4ee5\u63d0\u4f9b\u4e1a\u52a1\u903b\u8f91\u3002<\/p>\n<p><strong>Note<\/strong> For simple apps, you may be tempted to move this logic into your endpoint handlers or Razor Pages. This approach may be fine for tiny apps, but I encourage you to resist the urge generally; extracting your business logic to other services decouples the HTTP-centric nature of your handlers from the underlying business logic, whichoften makes your business logic easier to test and more reusable.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5bf9\u4e8e\u7b80\u5355\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u60a8\u53ef\u80fd\u4f1a\u60f3\u5c06\u6b64\u903b\u8f91\u79fb\u52a8\u5230\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u6216 Razor Pages \u4e2d\u3002\u8fd9\u79cd\u65b9\u6cd5\u53ef\u80fd\u9002\u7528\u4e8e\u5c0f\u578b\u5e94\u7528\u7a0b\u5e8f\uff0c\u4f46\u6211\u9f13\u52b1\u60a8\u901a\u5e38\u62b5\u5236\u8fd9\u79cd\u51b2\u52a8;\u5c06\u4e1a\u52a1\u903b\u8f91\u63d0\u53d6\u5230\u5176\u4ed6\u670d\u52a1\u53ef\u4ee5\u5c06\u5904\u7406\u7a0b\u5e8f\u4ee5 HTTP \u4e3a\u4e2d\u5fc3\u7684\u6027\u8d28\u4e0e\u5e95\u5c42\u4e1a\u52a1\u903b\u8f91\u5206\u79bb\uff0c\u8fd9\u901a\u5e38\u4f7f\u4e1a\u52a1\u903b\u8f91\u66f4\u6613\u4e8e\u6d4b\u8bd5\u548c\u66f4\u53ef\u91cd\u7528\u3002<\/p>\n<p>Our database doesn\u2019t have any data in it yet, so we\u2019d better start by creating a recipe.<\/p>\n<p>\u6211\u4eec\u7684\u6570\u636e\u5e93\u4e2d\u8fd8\u6ca1\u6709\u4efb\u4f55\u6570\u636e\uff0c\u56e0\u6b64\u6211\u4eec\u6700\u597d\u5148\u521b\u5efa\u4e00\u4e2a\u914d\u65b9\u3002<\/p>\n<h3>12.4.1 Creating a record<\/h3>\n<h3>12.4.1 \u521b\u5efa\u8bb0\u5f55<\/h3>\n<p>In this section you\u2019re going to build functionality to let users create a recipe by using the API. Clients send all the details of the recipe in the body of a POST request to an endpoint in your app. The endpoint uses model binding and validation attributes to confirm that the request is valid, as you learned in chapter 7.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u6784\u5efa\u529f\u80fd\uff0c\u8ba9\u7528\u6237\u4f7f\u7528 API \u521b\u5efa\u914d\u65b9\u3002\u5ba2\u6237\u7aef\u5c06 POST \u8bf7\u6c42\u6b63\u6587\u4e2d\u914d\u65b9\u7684\u6240\u6709\u8be6\u7ec6\u4fe1\u606f\u53d1\u9001\u5230 \u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u7ec8\u7aef\u8282\u70b9\u3002\u7ec8\u7aef\u8282\u70b9\u4f7f\u7528\u6a21\u578b\u7ed1\u5b9a\u548c\u9a8c\u8bc1\u5c5e\u6027\u6765\u786e\u8ba4\u8bf7\u6c42\u6709\u6548\uff0c\u5982\u60a8\u5728\u7b2c 7 \u7ae0\u4e2d\u5b66\u5230\u7684\u90a3\u6837\u3002<\/p>\n<p>If the request is valid, the endpoint handler calls RecipeService to create the new Recipe object in the database. As EF Core is the topic of this chapter, I\u2019m going to focus on this service alone, but you can always check out the source code for this book if you want to see how everything fits together in a minimal API application.<\/p>\n<p>\u5982\u679c\u8bf7\u6c42\u6709\u6548\uff0c\u5219\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4f1a\u8c03\u7528RecipeService \u5728\u6570\u636e\u5e93\u3002\u7531\u4e8e EF Core \u662f\u672c\u7ae0\u7684\u4e3b\u9898\uff0c\u56e0\u6b64\u6211\u5c06\u5355\u72ec\u5173\u6ce8\u6b64\u670d\u52a1\uff0c\u4f46\u5982\u679c\u60a8\u60f3\u4e86\u89e3\u6240\u6709\u5185\u5bb9\u5982\u4f55\u7ec4\u5408\u5230\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u60a8\u59cb\u7ec8\u53ef\u4ee5\u67e5\u770b\u672c\u4e66\u7684\u6e90\u4ee3\u7801\u3002<\/p>\n<p>The business logic for creating a recipe in this application is simple: there is no logic! Copy the properties from the command binding model provided in the endpoint handler to a Recipe entity and its Ingredients, add the Recipe object to AppDbContext, and save it in the database, as shown in figure 12.11.<\/p>\n<p>\u5728\u6b64\u5e94\u7528\u7a0b\u5e8f\u4e2d\u521b\u5efa\u914d\u65b9\u7684\u4e1a\u52a1\u903b\u8f91\u5f88\u7b80\u5355\uff1a\u6ca1\u6709\u903b\u8f91\uff01\u5c06\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u63d0\u4f9b\u7684\u547d\u4ee4\u7ed1\u5b9a\u6a21\u578b\u4e2d\u7684\u5c5e\u6027\u590d\u5236\u5230 Recipe \u5b9e\u4f53\u53ca\u5176 Ingredients\uff0c\u5c06 Recipe \u5bf9\u8c61\u6dfb\u52a0\u5230 AppDbContext\uff0c\u5e76\u5c06\u5176\u4fdd\u5b58\u5728\u6570\u636e\u5e93\u4e2d\uff0c\u5982\u56fe 12.11 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1211.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.11 Calling the POST endpoint and creating a new entity. A Recipe is created from the CreateRecipeCommand model and is added to the DbContext. EF Core generates the SQL to add a new row to the Recipes table in the database.<\/p>\n<p>\u56fe 12.11 \u8c03\u7528 POST \u7aef\u70b9\u5e76\u521b\u5efa\u65b0\u5b9e\u4f53\u3002\u914d\u65b9\u662f\u4ece CreateRecipeCommand \u6a21\u578b\u521b\u5efa\u7684\uff0c\u5e76\u6dfb\u52a0\u5230 DbContext \u4e2d\u3002EF Core \u751f\u6210 SQL \u4ee5\u5411\u6570\u636e\u5e93\u7684 Recipes \u8868\u6dfb\u52a0\u65b0\u884c\u3002<\/p>\n<p><strong>Warning<\/strong> Many simple, equivalent sample applications using EF or EF Core allow you to bind directly to the Recipe entity as the model in your endpoint. Unfortunately, this approach exposes a security vulnerability known as overposting, which is bad practice. If you want to avoid the boilerplate mapping code in your applications, consider using a library such as AutoMapper (<a href=\"http:\/\/automapper.org\">http:\/\/automapper.org<\/a>). For more details on overposting, see my blog post on the subject at <a href=\"http:\/\/mng.bz\/d48O\">http:\/\/mng.bz\/d48O<\/a>.<br \/>\n<strong>\u8b66\u544a<\/strong> \u8bb8\u591a\u4f7f\u7528 EF \u6216 EF Core \u7684\u7b80\u5355\u7b49\u6548\u793a\u4f8b\u5e94\u7528\u7a0b\u5e8f\u5141\u8bb8\u60a8\u76f4\u63a5\u7ed1\u5b9a\u5230 Recipe \u5b9e\u4f53\u4f5c\u4e3a\u7ec8\u7aef\u8282\u70b9\u4e2d\u7684\u6a21\u578b\u3002\u4e0d\u5e78\u7684\u662f\uff0c\u8fd9\u4e2a\u65b9\u6cd5\u4f1a\u66b4\u9732\u4e00\u4e2a\u79f0\u4e3a overpost \u7684\u5b89\u5168\u6f0f\u6d1e\uff0c\u8fd9\u662f\u4e00\u79cd\u4e0d\u597d\u7684\u505a\u6cd5\u3002\u5982\u679c\u8981\u907f\u514d\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u6837\u677f\u6620\u5c04\u4ee3\u7801\uff0c\u8bf7\u8003\u8651\u4f7f\u7528 AutoMapper \uff08<a href=\"http:\/\/automapper.org\">http:\/\/automapper.org<\/a>\uff09 \u7b49\u5e93\u3002\u6709\u5173\u8fc7\u5ea6\u53d1\u5e03\u7684\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u6211\u5728 <a href=\"http:\/\/mng.bz\/d48O\">http:\/\/mng.bz\/d48O<\/a> \u4e0a\u5173\u4e8e\u8be5\u4e3b\u9898\u7684\u535a\u5ba2\u6587\u7ae0\u3002<\/p>\n<p>Creating an entity in EF Core involves adding a new row to the mapped table. For your application, whenever you create a new Recipe, you also add the linked Ingredient entities. EF Core takes care of linking all these entities correctly by creating the correct RecipeId for each Ingredient in the database.<\/p>\n<p>\u5728 EF Core \u4e2d\u521b\u5efa\u5b9e\u4f53\u6d89\u53ca\u5411\u6620\u5c04\u8868\u6dfb\u52a0\u65b0\u884c\u3002\u5bf9\u4e8e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u6bcf\u5f53\u60a8\u521b\u5efa\u65b0\u914d\u65b9\u65f6\uff0c\u60a8\u90fd\u4f1a\u6dfb\u52a0\u94fe\u63a5\u7684 Ingredient \u5b9e\u4f53\u3002EF Core \u901a\u8fc7\u4e3a\u6570\u636e\u5e93\u4e2d\u7684\u6bcf\u4e2a\u6210\u5206\u521b\u5efa\u6b63\u786e\u7684 RecipeId \u6765\u6b63\u786e\u94fe\u63a5\u6240\u6709\u8fd9\u4e9b\u5b9e\u4f53\u3002<\/p>\n<p>All interactions with EF Core and the database start with an instance of AppDbContext, which typically is DI-injected via the constructor. Creating a new entity requires three steps:<\/p>\n<p>\u4e0e EF Core \u548c\u6570\u636e\u5e93\u7684\u6240\u6709\u4ea4\u4e92\u90fd\u4ece AppDbContext \u5b9e\u4f8b\u5f00\u59cb\uff0c\u8be5\u5b9e\u4f8b\u901a\u5e38\u901a\u8fc7\u6784\u9020\u51fd\u6570\u8fdb\u884c DI \u6ce8\u5165\u3002\u521b\u5efa\u65b0\u5b9e\u4f53\u9700\u8981\u4e09\u4e2a\u6b65\u9aa4\uff1a<\/p>\n<ol>\n<li>\n<p>Create the and Ingredient entities.<br \/>\n\u521b\u5efa Recipe \u548c Ingredient \u5b9e\u4f53\u3002<\/p>\n<\/li>\n<li>\n<p>Add the entities to EF Core\u2019s list of tracked entities using _context.Add(entity).<br \/>\n\u4f7f\u7528 _context \u5c06\u5b9e\u4f53\u6dfb\u52a0\u5230 EF Core \u7684\u8ddf\u8e2a\u5b9e\u4f53\u5217\u8868\u4e2d\u3002<\/p>\n<\/li>\n<li>\n<p>Execute the SQL INSERT statements against the database, adding the necessary rows to the Recipe and Ingredient tables, by calling _context.SaveChangesAsync().<br \/>\n\u5bf9\u6570\u636e\u5e93\u6267\u884c SQL INSERT \u8bed\u53e5\uff0c\u901a\u8fc7\u8c03\u7528SaveChangesAsync\uff08\uff09 \u4e0a\u4e0b\u6587\u3002<\/p>\n<\/li>\n<\/ol>\n<p><strong>Tip<\/strong> There are sync and async versions of most of the EF Core commands that involve interacting with the database, such as SaveChanges() and SaveChangesAsync(). In general, the async versions will allow your app to handle more concurrent connections, so I tend to favor them whenever I can use them.<br \/>\n<strong>\u63d0\u793a<\/strong> \u5927\u591a\u6570\u6d89\u53ca\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u7684 EF Core \u547d\u4ee4\u90fd\u6709\u540c\u6b65\u548c\u5f02\u6b65\u7248\u672c\uff0c\u4f8b\u5982 SaveChanges\uff08\uff09 \u548c SaveChangesAsync\uff08\uff09\u3002\u901a\u5e38\uff0c\u5f02\u6b65\u7248\u672c\u5c06\u5141\u8bb8\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5904\u7406\u66f4\u591a\u7684\u5e76\u53d1\u8fde\u63a5\uff0c\u56e0\u6b64\u53ea\u8981\u53ef\u4ee5\u4f7f\u7528\u5b83\u4eec\uff0c\u6211\u5c31\u4f1a\u503e\u5411\u4e8e\u4f7f\u7528\u5b83\u4eec\u3002<\/p>\n<p>Listing 12.6 shows these three steps in practice. The bulk of the code in this example involves copying properties from CreateRecipeCommand to the Recipe entity. The interaction with the AppDbContext consists of only two methods: Add() and SaveChangesAsync().<\/p>\n<p>\u6e05\u5355 12.6 \u5c55\u793a\u4e86\u8fd9\u4e09\u4e2a\u6b65\u9aa4\u7684\u5b9e\u9645\u5e94\u7528\u3002\u6b64\u793a\u4f8b\u4e2d\u7684\u5927\u90e8\u5206\u4ee3\u7801\u6d89\u53ca\u5c06\u5c5e\u6027\u4ece CreateRecipeCommand \u590d\u5236\u5230 Recipe \u5b9e\u4f53\u3002\u4e0e AppDbContext \u7684\u4ea4\u4e92\u4ec5\u5305\u542b\u4e24\u4e2a\u65b9\u6cd5\uff1aAdd\uff08\uff09 \u548c SaveChangesAsync\uff08\uff09\u3002<\/p>\n<p>Listing 12.6 Creating a Recipe entity in the database in RecipeService<br \/>\n\u6e05\u5355 12.6 \u5728\u6570\u636e\u5e93\u4e2d\u521b\u5efa\u4e00\u4e2a Recipe \u5b9e\u4f53<\/p>\n<pre><code>readonly AppDbContext _context;    \u2776\npublic async Task&lt;int&gt; CreateRecipe(CreateRecipeCommand cmd)   \u2777\n{\n    var recipe = new Recipe                             \u2778\n    {                                                   \u2778\n        Name = cmd.Name,                                \u2778\n        TimeToCook = new TimeSpan(                      \u2778\n            cmd.TimeToCookHrs, cmd.TimeToCookMins, 0),  \u2778\n        Method = cmd.Method,                            \u2778\n        IsVegetarian = cmd.IsVegetarian,                \u2778\n        IsVegan = cmd.IsVegan,                          \u2778\n        Ingredients = cmd.Ingredients.Select(i =&gt;      \u2778\n        new Ingredient              \u2779\n        {                           \u2779\n            Name = i.Name,          \u2779\n            Quantity = i.Quantity,  \u2779\n            Unit = i.Unit,          \u2779\n        }).ToList()                 \u2779\n    };\n    _context.Add(recipe);       \u277a\n    await _context.SaveChangesAsync();   \u277b\n    return recipe.RecipeId;    \u277c\n}<\/code><\/pre>\n<p>\u2776 An instance of the AppDbContext is injected in the class constructor using DI.<br \/>\n\u4f7f\u7528 DI \u5c06 AppDbContext \u7684\u5b9e\u4f8b\u6ce8\u5165\u7c7b\u6784\u9020\u51fd\u6570\u4e2d\u3002<br \/>\n\u2777 CreateRecipeCommand is passed in from the endpoint handler.<br \/>\nCreateRecipeCommand \u4ece\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4f20\u5165\u3002<br \/>\n\u2778 Creates a Recipe by mapping from the command object to the Recipe entity<br \/>\n\u901a\u8fc7\u4ece\u547d\u4ee4\u5bf9\u8c61\u6620\u5c04\u5230 Recipe \u5b9e\u4f53\u6765\u521b\u5efa Recipe<br \/>\n\u2779 Maps each CreateIngredientCommand onto an Ingredient entity<br \/>\n\u5c06\u6bcf\u4e2a CreateIngredientCommand \u6620\u5c04\u5230\u4e00\u4e2a Ingredient \u5b9e\u4f53<br \/>\n\u277a Tells EF Core to track the new entities<br \/>\n\u544a\u77e5 EF Core \u8ddf\u8e2a\u65b0\u5b9e\u4f53<br \/>\n\u277b Tells EF Core to write the entities to the database; uses the async version of the command<br \/>\n\u544a\u77e5 EF Core \u5c06\u5b9e\u4f53\u5199\u5165\u6570\u636e\u5e93;\u4f7f\u7528\u547d\u4ee4\u7684\u5f02\u6b65\u7248\u672c<br \/>\n\u277c EF Core populates the RecipeId field on your new Recipe when it\u2019s saved.<br \/>\n\u4fdd\u5b58\u65b0\u914d\u65b9\u65f6\uff0cEF Core \u4f1a\u586b\u5145\u65b0\u914d\u65b9\u4e0a\u7684 RecipeId \u5b57\u6bb5\u3002<\/p>\n<p>If a problem occurs when EF Core tries to interact with your database\u2014you haven\u2019t run the migrations to update the database schema, for example\u2014this code throws an exception. I haven\u2019t shown it here, but it\u2019s important to handle these exceptions in your application so you don\u2019t present an ugly error message to user when things go wrong.<\/p>\n<p>\u5982\u679c\u5728 EF Core \u5c1d\u8bd5\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u65f6\u51fa\u73b0\u95ee\u9898\uff08\u4f8b\u5982\uff0c\u60a8\u5c1a\u672a\u8fd0\u884c\u8fc1\u79fb\u6765\u66f4\u65b0\u6570\u636e\u5e93\u67b6\u6784\uff09\uff0c\u5219\u6b64\u4ee3\u7801\u5c06\u5f15\u53d1\u5f02\u5e38\u3002\u6211\u6ca1\u6709\u5728\u8fd9\u91cc\u5c55\u793a\u5b83\uff0c\u4f46\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5904\u7406\u8fd9\u4e9b\u5f02\u5e38\u5f88\u91cd\u8981\uff0c\u8fd9\u6837\u60a8\u5c31\u4e0d\u4f1a\u5728\u51fa\u73b0\u95ee\u9898\u65f6\u5411\u7528\u6237\u663e\u793a\u96be\u770b\u7684\u9519\u8bef\u6d88\u606f\u3002<\/p>\n<p>Assuming that all goes well, EF Core updates all the autogenerated IDs of your entities (RecipeId on Recipe, and both RecipeId and IngredientId on Ingredient). Return the recipe ID to the endpoint handler so the handler can use it\u2014to return the ID in the API response, for example.<\/p>\n<p>\u5047\u8bbe\u4e00\u5207\u987a\u5229\uff0cEF Core \u4f1a\u66f4\u65b0\u5b9e\u4f53\u7684\u6240\u6709\u81ea\u52a8\u751f\u6210\u7684 ID\uff08Recipe \u4e0a\u7684 RecipeId\uff0c\u4ee5\u53ca Ingredient \u4e0a\u7684 RecipeId \u548c IngredientId\uff09\u3002\u5c06\u914d\u65b9 ID \u8fd4\u56de\u7ed9\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\uff0c\u4ee5\u4fbf\u5904\u7406\u7a0b\u5e8f\u53ef\u4ee5\u4f7f\u7528\u5b83\uff0c\u4f8b\u5982\uff0c\u5728 API \u54cd\u5e94\u4e2d\u8fd4\u56de ID\u3002<\/p>\n<p><strong>Tip<\/strong> The DbContext type is an implementation of both the unit-of-work and repository patterns, so you generally don\u2019t need to implement these patterns manually in your apps. You can read more about these patterns at <a href=\"https:\/\/martinfowler.com\/eaaCatalog\">https:\/\/martinfowler.com\/eaaCatalog<\/a>.<br \/>\n<strong>\u63d0\u793a<\/strong> DbContext \u7c7b\u578b\u662f\u5de5\u4f5c\u5355\u5143\u6a21\u5f0f\u548c\u5b58\u50a8\u5e93\u6a21\u5f0f\u7684\u5b9e\u73b0\uff0c\u56e0\u6b64\u60a8\u901a\u5e38\u4e0d\u9700\u8981\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u624b\u52a8\u5b9e\u73b0\u8fd9\u4e9b\u6a21\u5f0f\u3002\u60a8\u53ef\u4ee5\u5728 <a href=\"https:\/\/martinfowler.com\/eaaCatalog\">https:\/\/martinfowler.com\/eaaCatalog<\/a> \u4e0a\u9605\u8bfb\u6709\u5173\u8fd9\u4e9b\u6a21\u5f0f\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<p>And there you have it. You\u2019ve created your first entity with EF Core. In section 12.4.2 we\u2019ll look at loading these entities from the database so you can fetch them all in a list.<\/p>\n<p>\u4f60\u6709\u5b83\u3002\u4f60\u5df2\u4f7f\u7528 EF Core \u521b\u5efa\u4e86\u7b2c\u4e00\u4e2a\u5b9e\u4f53\u3002\u5728 Section 12.4.2 \u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u5982\u4f55\u4ece\u6570\u636e\u5e93\u4e2d\u52a0\u8f7d\u8fd9\u4e9b\u5b9e\u4f53\uff0c\u4ee5\u4fbf\u60a8\u53ef\u4ee5\u5728\u5217\u8868\u4e2d\u83b7\u53d6\u5b83\u4eec\u3002<\/p>\n<h3>12.4.2 Loading a list of records<\/h3>\n<h3>12.4.2 \u52a0\u8f7d\u8bb0\u5f55\u5217\u8868<\/h3>\n<p>Now that you can create recipes, you need to write the code to view them. Luckily, loading data is simple in EF Core, relying heavily on LINQ methods to control the fields you need. For your app, you\u2019ll create a method on RecipeService that returns a summary view of a recipe, consisting of RecipeId, Name, and TimeToCook as a RecipeSummaryViewModel, as shown in figure 12.12.<\/p>\n<p>\u73b0\u5728\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u914d\u65b9\uff0c\u60a8\u9700\u8981\u7f16\u5199\u4ee3\u7801\u6765\u67e5\u770b\u5b83\u4eec\u3002\u5e78\u8fd0\u7684\u662f\uff0c\u5728 EF Core \u4e2d\u52a0\u8f7d\u6570\u636e\u5f88\u7b80\u5355\uff0c\u5728\u5f88\u5927\u7a0b\u5ea6\u4e0a\u4f9d\u8d56\u4e8e LINQ \u65b9\u6cd5\u6765\u63a7\u5236\u6240\u9700\u7684\u5b57\u6bb5\u3002\u5bf9\u4e8e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u60a8\u5c06\u5728 RecipeService \u4e0a\u521b\u5efa\u4e00\u4e2a\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u8fd4\u56de\u914d\u65b9\u7684\u6458\u8981\u89c6\u56fe\uff0c\u5176\u4e2d\u5305\u542b RecipeId\u3001Name \u548c TimeToCook \u4f5c\u4e3a RecipeSummaryViewModel\uff0c\u5982\u56fe 12.12 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1211.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.12 Calling the GET list endpoint and querying the database to retrieve a list of RecipeSummaryViewModels. EF Core generates the SQL to retrieve the necessary fields from the database and maps them to view model objects.<br \/>\n\u56fe 12.12 \u8c03\u7528 GET \u5217\u8868\u7ec8\u7aef\u8282\u70b9\u5e76\u67e5\u8be2\u6570\u636e\u5e93\u4ee5\u68c0\u7d22 RecipeSummaryViewModels \u5217\u8868\u3002EF Core \u751f\u6210 SQL \u4ee5\u4ece\u6570\u636e\u5e93\u4e2d\u68c0\u7d22\u5fc5\u8981\u7684\u5b57\u6bb5\uff0c\u5e76\u5c06\u5b83\u4eec\u6620\u5c04\u5230\u89c6\u56fe\u6a21\u578b\u5bf9\u8c61\u3002<\/p>\n<p><strong>Note<\/strong> Creating a view model is technically a UI concern rather than a business-logic concern. I\u2019m returning a view model directly from RecipeService here mostly to hammer home the fact that you shouldn\u2019t be using EF Core entities directly in your endpoint\u2019s public API. Alternatively, you might return the Recipe entity directly from the RecipeService and then build and return the RecipeSummaryViewModel inside your endpoint handler code.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u4ece\u6280\u672f\u4e0a\u8bb2\uff0c\u521b\u5efa\u89c6\u56fe\u6a21\u578b\u662f\u4e00\u4e2a UI \u95ee\u9898\uff0c\u800c\u4e0d\u662f\u4e1a\u52a1\u903b\u8f91\u95ee\u9898\u3002\u6211\u5728\u8fd9\u91cc\u76f4\u63a5\u4ece RecipeService \u8fd4\u56de\u4e00\u4e2a\u89c6\u56fe\u6a21\u578b\uff0c\u4e3b\u8981\u662f\u4e3a\u4e86\u5f3a\u8c03\u60a8\u4e0d\u5e94\u8be5\u76f4\u63a5\u5728\u7ec8\u7aef\u8282\u70b9\u7684\u516c\u5171 API \u4e2d\u4f7f\u7528 EF Core \u5b9e\u4f53\u7684\u4e8b\u5b9e\u3002\u6216\u8005\uff0c\u60a8\u53ef\u4ee5\u76f4\u63a5\u4ece RecipeService \u8fd4\u56de Recipe \u5b9e\u4f53\uff0c\u7136\u540e\u5728\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u4ee3\u7801\u4e2d\u6784\u5efa\u5e76\u8fd4\u56de RecipeSummaryViewModel\u3002<\/p>\n<p>The GetRecipes method in RecipeService is conceptually simple and follows a common pattern for querying an EF Core database, as shown in figure 12.13. EF Core uses a fluent chain of LINQ commands to define the query to return on the database. The DbSet<Recipe> property on AppDataContext is an IQueryable, so you can use all the usual Select() and Where() clauses that you would with other IQueryable providers. EF Core converts these LINQ methods into a SQL statement to query the database when you call an execute function such as ToListAsync(), ToArrayAsync(), or SingleAsync(), or their non-async brethren.<\/p>\n<p>RecipeService \u4e2d\u7684 GetRecipes \u65b9\u6cd5\u5728\u6982\u5ff5\u4e0a\u5f88\u7b80\u5355\uff0c\u5e76\u9075\u5faa\u67e5\u8be2 EF Core \u6570\u636e\u5e93\u7684\u5e38\u89c1\u6a21\u5f0f\uff0c\u5982\u56fe 12.13 \u6240\u793a\u3002EF Core \u4f7f\u7528 Fluent LINQ \u547d\u4ee4\u94fe\u6765\u5b9a\u4e49\u8981\u5728\u6570\u636e\u5e93\u4e0a\u8fd4\u56de\u7684\u67e5\u8be2\u3002AppDataContext \u4e0a\u7684 DbSet<Recipe> \u5c5e\u6027\u662f IQueryable\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u4f7f\u7528\u5176\u4ed6 IQueryable \u63d0\u4f9b\u7a0b\u5e8f\u7684\u6240\u6709\u5e38\u7528 Select\uff08\uff09 \u548c Where\uff08\uff09 \u5b50\u53e5\u3002EF Core \u5c06\u8fd9\u4e9b LINQ \u65b9\u6cd5\u8f6c\u6362\u4e3a SQL \u8bed\u53e5\uff0c\u4ee5\u4fbf\u5728\u8c03\u7528\u6267\u884c\u51fd\u6570\uff08\u5982 ToListAsync\uff08\uff09\u3001ToArrayAsync\uff08\uff09 \u6216 SingleAsync\uff08\uff09\uff09\u6216\u5176\u975e\u5f02\u6b65\u5144\u5f1f\u65f6\u67e5\u8be2\u6570\u636e\u5e93\u3002<\/p>\n<p>You can also use the Select() extension method to map to objects other than your entities as part of the SQL query. You can use this technique to query the database efficiently by fetching only the columns you need.<\/p>\n<p>\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 Select\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\u6620\u5c04\u5230\u5b9e\u4f53\u4ee5\u5916\u7684\u5bf9\u8c61\uff0c\u4f5c\u4e3a SQL \u67e5\u8be2\u7684\u4e00\u90e8\u5206\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528\u6b64\u6280\u672f\u901a\u8fc7\u4ec5\u83b7\u53d6\u6240\u9700\u7684\u5217\u6765\u9ad8\u6548\u5730\u67e5\u8be2\u6570\u636e\u5e93\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1213.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.13 The three parts of an EF Core database query<br \/>\n\u56fe 12.13 EF Core \u6570\u636e\u5e93\u67e5\u8be2\u7684\u4e09\u4e2a\u90e8\u5206<\/p>\n<p>Listing 12.7 shows the code to fetch a list of RecipeSummaryViewModels, following the same basic pattern as figure 12.12. It uses a Where LINQ expression to filter out recipes marked as deleted and a Select clause to map to the view models. The ToListAsync() command instructs EF Core to generate the SQL query, execute it on the database, and build RecipeSummaryViewModels from the data returned.<\/p>\n<p>\u6e05\u5355 12.7 \u663e\u793a\u4e86\u83b7\u53d6 RecipeSummaryViewModel\u5217\u8868\u7684\u4ee3\u7801\uff0c\u9075\u5faa\u4e0e\u56fe 12.12 \u76f8\u540c\u7684\u57fa\u672c\u6a21\u5f0f\u3002\u5b83\u4f7f\u7528 Where LINQ \u8868\u8fbe\u5f0f\u7b5b\u9009\u51fa\u6807\u8bb0\u4e3a\u5df2\u5220\u9664\u7684\u914d\u65b9\uff0c\u5e76\u4f7f\u7528 Select \u5b50\u53e5\u6620\u5c04\u5230\u89c6\u56fe\u6a21\u578b\u3002ToListAsync\uff08\uff09 \u547d\u4ee4\u6307\u793a EF Core \u751f\u6210 SQL \u67e5\u8be2\uff0c\u5728\u6570\u636e\u5e93\u4e0a\u6267\u884c\u8be5\u67e5\u8be2\uff0c\u5e76\u6839\u636e\u8fd4\u56de\u7684\u6570\u636e\u751f\u6210 RecipeSummaryViewModels\u3002<\/p>\n<p>Listing 12.7 Loading a list of items using EF Core in RecipeService<br \/>\n\u6e05\u5355 12.7 \u5728\u914d\u65b9\u670d\u52a1<\/p>\n<pre><code>public async Task&lt;ICollection&lt;RecipeSummaryViewModel&gt;&gt; GetRecipes()\n{\n    return await _context.Recipes    \u2776\n        .Where(r =&gt; !r.IsDeleted)\n        .Select(r =&gt; new RecipeSummaryViewModel               \u2777 \n        {                                                     \u2777 \n            Id = r.RecipeId,                                  \u2777 \n            Name = r.Name,                                    \u2777 \n            TimeToCook = $&quot;{r.TimeToCook.TotalMinutes}mins&quot;   \u2777 \n        })\n        .ToListAsync();      \u2778\n}<\/code><\/pre>\n<p>\u2776 A query starts from a DbSet property.<br \/>\n\u67e5\u8be2\u4ece DbSet \u5c5e\u6027\u5f00\u59cb\u3002<br \/>\n\u2777 EF Core queries only the Recipe columns it needs to map the view model<br \/>\ncorrectly.<br \/>\nEF Core \u4ec5\u67e5\u8be2\u6b63\u786e\u6620\u5c04\u89c6\u56fe\u6a21\u578b\u6240\u9700\u7684 Recipe \u5217\u3002<br \/>\n\u2778 Executes the SQL query and creates the final view models<br \/>\n\u6267\u884c SQL \u67e5\u8be2\u5e76\u521b\u5efa\u6700\u7ec8\u89c6\u56fe\u6a21\u578b<\/p>\n<p>Notice that in the Select method you convert the TimeToCook property from a TimeSpan to a string by using string interpolation:<\/p>\n<p>\u8bf7\u6ce8\u610f\uff0c\u5728 Select \u65b9\u6cd5\u4e2d\uff0c\u901a\u8fc7\u4f7f\u7528\u5b57\u7b26\u4e32\u63d2\u503c\u5c06 TimeToCook \u5c5e\u6027\u4ece TimeSpan \u8f6c\u6362\u4e3a\u5b57\u7b26\u4e32\uff1a<\/p>\n<pre><code>TimeToCook = $&quot;{r.TimeToCook.TotalMinutes}mins&quot;<\/code><\/pre>\n<p>I said before that EF Core converts the series of LINQ expressions to SQL, but that statement is a half-truth: EF Core can\u2019t or doesn\u2019t know how to convert some expressions to SQL. In those cases, such as this example, EF Core finds the fields from the DB that it needs to run the expression on the client side, selects them from the database, and then runs the expression in C#. This approach lets you combine the power and performance of database-side evaluation without compromising the functionality of C#.<\/p>\n<p>\u6211\u4e4b\u524d\u8bf4\u8fc7 EF Core \u5c06\u4e00\u7cfb\u5217 LINQ \u8868\u8fbe\u5f0f\u8f6c\u6362\u4e3a SQL\uff0c\u4f46\u8be5\u8bf4\u6cd5\u662f\u534a\u771f\u534a\u5047\uff1aEF Core \u4e0d\u80fd\u6216\u4e0d\u77e5\u9053\u5982\u4f55\u5c06\u67d0\u4e9b\u8868\u8fbe\u5f0f\u8f6c\u6362\u4e3a SQL\u3002\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff08\u4f8b\u5982\u672c\u793a\u4f8b\uff09\uff0cEF Core \u4ece\u6570\u636e\u5e93\u4e2d\u627e\u5230\u5728\u5ba2\u6237\u7aef\u8fd0\u884c\u8868\u8fbe\u5f0f\u6240\u9700\u7684\u5b57\u6bb5\uff0c\u4ece\u6570\u636e\u5e93\u4e2d\u9009\u62e9\u8fd9\u4e9b\u5b57\u6bb5\uff0c\u7136\u540e\u5728 C# \u4e2d\u8fd0\u884c\u8868\u8fbe\u5f0f\u3002\u6b64\u65b9\u6cd5\u5141\u8bb8\u60a8\u5c06\u6570\u636e\u5e93\u7aef\u8bc4\u4f30\u7684\u5f3a\u5927\u529f\u80fd\u548c\u6027\u80fd\u76f8\u7ed3\u5408\uff0c\u800c\u4e0d\u4f1a\u5f71\u54cd C# \u7684\u529f\u80fd\u3002<\/p>\n<p><strong>Warning<\/strong> Client-side evaluation is both powerful and useful but has the potential to cause problems. In general, recent versions of EF Core throw an exception if a query requires dangerous client-side evaluation, ensuring (for example) that you can\u2019t accidentally return all records to the client before filtering. For more examples, including ways to avoid these problems, see the documentation at <a href=\"http:\/\/mng.bz\/zxP6\">http:\/\/mng.bz\/zxP6<\/a>.<br \/>\n<strong>\u8b66\u544a<\/strong> \u5ba2\u6237\u7aef\u8bc4\u4f30\u529f\u80fd\u5f3a\u5927\u4e14\u6709\u7528\uff0c\u4f46\u53ef\u80fd\u4f1a\u5bfc\u81f4\u95ee\u9898\u3002\u901a\u5e38\uff0c\u5982\u679c\u67e5\u8be2\u9700\u8981\u5371\u9669\u7684\u5ba2\u6237\u7aef\u8bc4\u4f30\uff0c\u6700\u65b0\u7248\u672c\u7684 EF Core \u4f1a\u5f15\u53d1\u5f02\u5e38\uff0c\u4f8b\u5982\uff0c\u786e\u4fdd\u5728\u7b5b\u9009\u4e4b\u524d\u4e0d\u4f1a\u610f\u5916\u5730\u5c06\u6240\u6709\u8bb0\u5f55\u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef\u3002\u6709\u5173\u66f4\u591a\u793a\u4f8b\uff0c\u5305\u62ec\u907f\u514d\u8fd9\u4e9b\u95ee\u9898\u7684\u65b9\u6cd5\uff0c\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/zxP6\">http:\/\/mng.bz\/zxP6<\/a> \u4e2d\u7684\u6587\u6863\u3002<\/p>\n<p>At this point, you have a list of records displaying a summary of the recipe\u2019s data, so the obvious next step is loading the detail for a single record.<\/p>\n<p>\u6b64\u65f6\uff0c\u60a8\u6709\u4e00\u4e2a\u8bb0\u5f55\u5217\u8868\uff0c\u5176\u4e2d\u663e\u793a\u4e86\u914d\u65b9\u6570\u636e\u7684\u6458\u8981\uff0c\u56e0\u6b64\u663e\u800c\u6613\u89c1\u7684\u4e0b\u4e00\u6b65\u662f\u52a0\u8f7d\u5355\u4e2a\u8bb0\u5f55\u7684\u8be6\u7ec6\u4fe1\u606f\u3002<\/p>\n<h3>12.4.3 Loading a single record<\/h3>\n<h3>12.4.3 \u52a0\u8f7d\u5355\u4e2a\u8bb0\u5f55<\/h3>\n<p>For most intents and purposes, loading a single record is the same as loading a list of records. Both approaches have the same common structure you saw in figure 12.13, but when you\u2019re loading a single record, you typically use a Where clause that restricts the data to a single entity.<\/p>\n<p>\u5bf9\u4e8e\u5927\u591a\u6570 intent \u548c\u76ee\u7684\uff0c\u52a0\u8f7d\u5355\u4e2a\u8bb0\u5f55\u4e0e\u52a0\u8f7d\u8bb0\u5f55\u5217\u8868\u76f8\u540c\u3002\u8fd9\u4e24\u79cd\u65b9\u6cd5\u90fd\u5177\u6709\u60a8\u5728\u56fe 12.13 \u4e2d\u770b\u5230\u7684\u76f8\u540c\u7684\u901a\u7528\u7ed3\u6784\uff0c\u4f46\u662f\u5f53\u60a8\u52a0\u8f7d\u5355\u4e2a\u8bb0\u5f55\u65f6\uff0c\u60a8\u901a\u5e38\u4f1a\u4f7f\u7528 Where \u5b50\u53e5\u5c06\u6570\u636e\u9650\u5236\u4e3a\u5355\u4e2a\u5b9e\u4f53\u3002<\/p>\n<p>Listing 12.8 shows the code to fetch a recipe by ID, following the same basic pattern as before (figure 12.12). It uses a Where() LINQ expression to restrict the query to a single recipe, where RecipeId == id, and a Select clause to map to RecipeDetailViewModel. The SingleOrDefaultAsync() clause causes EF Core to generate the SQL query, execute it on the database, and build the view model.<\/p>\n<p>\u6e05\u5355 12.8 \u663e\u793a\u4e86\u901a\u8fc7 ID \u83b7\u53d6\u914d\u65b9\u7684\u4ee3\u7801\uff0c\u9075\u5faa\u4e0e\u4e4b\u524d\u76f8\u540c\u7684\u57fa\u672c\u6a21\u5f0f\uff08\u56fe 12.12\uff09\u3002\u5b83\u4f7f\u7528 Where\uff08\uff09 LINQ \u8868\u8fbe\u5f0f\u5c06\u67e5\u8be2\u9650\u5236\u4e3a\u5355\u4e2a\u914d\u65b9\uff0c\u5176\u4e2d RecipeId == id\uff0c\u5e76\u4f7f\u7528 Select \u5b50\u53e5\u6620\u5c04\u5230 RecipeDetailViewModel\u3002SingleOrDefaultAsync\uff08\uff09 \u5b50\u53e5\u4f7f EF Core \u751f\u6210 SQL \u67e5\u8be2\uff0c\u5728\u6570\u636e\u5e93\u4e0a\u6267\u884c\u8be5\u67e5\u8be2\uff0c\u5e76\u751f\u6210\u89c6\u56fe\u6a21\u578b\u3002<\/p>\n<p><strong>Note<\/strong> SingleOrDefaultAsync()throws an exception if the previous Where clause returns more than one record.<br \/>\n<strong>\u6ce8\u610f<\/strong> SingleOrDefaultAsync\uff08\uff09 \u5982\u679c\u524d\u9762\u7684 Where \u5b50\u53e5\u8fd4\u56de\u591a\u6761\u8bb0\u5f55\uff0c\u5219\u5f15\u53d1\u5f02\u5e38\u3002<\/p>\n<p>Listing 12.8 Loading a single item using EF Core in RecipeService<br \/>\n\u6e05\u5355 12.8 \u5728\u914d\u65b9\u670d\u52a1<\/p>\n<pre><code>public async Task&lt;RecipeDetailViewModel&gt; GetRecipeDetail(int id)     \u2776\n{\n    return await _context.Recipes         \u2777\n        .Where(x =&gt; x.RecipeId == id)    \u2778\n        .Select(x =&gt; new RecipeDetailViewModel    \u2779\n        {                                         \u2779\n            Id = x.RecipeId,                      \u2779\n            Name = x.Name,                        \u2779\n            Method = x.Method,                    \u2779\n            Ingredients = x.Ingredients                       \u277a\n            .Select(item =&gt; new RecipeDetailViewModel.Item    \u277a\n            {                                                 \u277a\n                Name = item.Name,                             \u277a\n                Quantity = $&quot;{item.Quantity} {item.Unit}&quot;     \u277a\n            })                                                \u277a\n        })\n        .SingleOrDefaultAsync();     \u277b\n}<\/code><\/pre>\n<p>\u2776 The id of the recipe to load is passed as a parameter.<br \/>\n\u8981\u52a0\u8f7d\u7684\u914d\u65b9\u7684 id \u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u3002<br \/>\n\u2777 As before, a query starts from a DbSet property.<br \/>\n\u4e0e\u4ee5\u524d\u4e00\u6837\uff0c\u67e5\u8be2\u4ece DbSet \u5c5e\u6027\u5f00\u59cb\u3002<br \/>\n\u2778 Limits the query to the recipe with the provided id<br \/>\n\u5c06\u67e5\u8be2\u9650\u5236\u4e3a\u5177\u6709\u63d0\u4f9b\u7684 ID \u7684\u914d\u65b9<br \/>\n\u2779 Maps the Recipe to a RecipeDetailViewModel<br \/>\n\u5c06\u914d\u65b9\u6620\u5c04\u5230 RecipeDetailViewModel<br \/>\n\u277a Loads and maps linked Ingredients as part of the same query<br \/>\n\u52a0\u8f7d\u548c\u6620\u5c04\u94fe\u63a5\u7684 Ingredients \u4f5c\u4e3a\u540c\u4e00\u67e5\u8be2\u7684\u4e00\u90e8\u5206<br \/>\n\u277b Executes the query and maps the data to the view model<br \/>\n\u6267\u884c\u67e5\u8be2\u5e76\u5c06\u6570\u636e\u6620\u5c04\u5230\u89c6\u56fe\u6a21\u578b<\/p>\n<p>Notice that as well as mapping the Recipe to a RecipeDetailViewModel, you map the related Ingredients for a Recipe, as though you\u2019re working with the objects directly in memory. One advantage of using an ORM is that you can easily map child objects and let EF Core decide how best to build the underlying queries to fetch the data.<\/p>\n<p>\u8bf7\u6ce8\u610f\uff0c\u9664\u4e86\u5c06 Recipe \u6620\u5c04\u5230 RecipeDetailViewModel \u4e4b\u5916\uff0c\u60a8\u8fd8\u53ef\u4ee5\u6620\u5c04 Recipe \u7684\u76f8\u5173 Ingredient\uff0c\u5c31\u50cf\u60a8\u76f4\u63a5\u5728\u5185\u5b58\u4e2d\u5904\u7406\u5bf9\u8c61\u4e00\u6837\u3002\u4f7f\u7528 ORM \u7684\u4e00\u4e2a\u4f18\u70b9\u662f\uff0c\u60a8\u53ef\u4ee5\u8f7b\u677e\u6620\u5c04\u5b50\u5bf9\u8c61\uff0c\u5e76\u8ba9 EF Core \u51b3\u5b9a\u5982\u4f55\u6700\u597d\u5730\u6784\u5efa\u57fa\u7840\u67e5\u8be2\u6765\u63d0\u53d6\u6570\u636e\u3002<\/p>\n<p><strong>Note<\/strong> EF Core logs all the SQL statements it runs as LogLevel.Information events by default, so you can easily see what queries are running against the database.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cEF Core \u5c06\u5176\u8fd0\u884c\u7684\u6240\u6709 SQL \u8bed\u53e5\u8bb0\u5f55\u4e3a LogLevel.Information \u4e8b\u4ef6\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u8f7b\u677e\u67e5\u770b\u9488\u5bf9\u6570\u636e\u5e93\u8fd0\u884c\u7684\u67e5\u8be2\u3002<\/p>\n<p>Your app is definitely shaping up. You can create new recipes, view them all in a list, and drill down to view individual recipes with their ingredients and method. Soon, though, someone\u2019s going to introduce a typo and want to change their data, so you\u2019ll have to implement the U in CRUD: update.<\/p>\n<p>\u60a8\u7684\u5e94\u7528\u80af\u5b9a\u6b63\u5728\u6210\u578b\u3002\u60a8\u53ef\u4ee5\u521b\u5efa\u65b0\u914d\u65b9\uff0c\u5728\u5217\u8868\u4e2d\u67e5\u770b\u6240\u6709\u914d\u65b9\uff0c\u5e76\u5411\u4e0b\u94bb\u53d6\u4ee5\u67e5\u770b\u5355\u4e2a\u914d\u65b9\u98df\u8c31\u53ca\u5176\u6210\u5206\u548c\u65b9\u6cd5\u3002\u4e0d\u8fc7\uff0c\u5f88\u5feb\uff0c\u6709\u4eba\u4f1a\u5f15\u5165\u4e00\u4e2a\u62fc\u5199\u9519\u8bef\u5e76\u60f3\u8981\u66f4\u6539\u4ed6\u4eec\u7684\u6570\u636e\uff0c\u56e0\u6b64\u60a8\u5fc5\u987b\u5728 CRUD\uff1a update \u4e2d\u5b9e\u73b0 U\u3002<\/p>\n<h3>12.4.4 Updating a model with changes<\/h3>\n<h3>12.4.4 \u4f7f\u7528\u66f4\u6539\u66f4\u65b0\u6a21\u578b<\/h3>\n<p>Updating entities when they\u2019ve changed generally is the hardest part of CRUD operations, as there are so many variables. Figure 12.14 shows an overview of this process as it applies to your recipe app.<\/p>\n<p>\u5728\u5b9e\u4f53\u53d1\u751f\u66f4\u6539\u65f6\u66f4\u65b0\u5b9e\u4f53\u901a\u5e38\u662f CRUD\u4f5c\u4e2d\u6700\u56f0\u96be\u7684\u90e8\u5206\uff0c\u56e0\u4e3a\u53d8\u91cf\u592a\u591a\u4e86\u3002\u56fe 12.14 \u663e\u793a\u4e86\u9002\u7528\u4e8e\u60a8\u7684\u914d\u65b9\u5e94\u7528\u7a0b\u5e8f\u7684\u6b64\u8fc7\u7a0b\u7684\u6982\u8ff0\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/1214.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 12.14 Updating an entity involves three steps: read the entity using EF Core, update the properties of the entity, and call SaveChangesAsync() on the DbContext to generate the SQL to update the correct rows in the database.<\/p>\n<p>\u56fe 12.14 \u66f4\u65b0\u5b9e\u4f53\u6d89\u53ca\u4e09\u4e2a\u6b65\u9aa4\uff1a\u4f7f\u7528 EF Core \u8bfb\u53d6\u5b9e\u4f53\uff0c\u66f4\u65b0\u5b9e\u4f53\u7684\u5c5e\u6027\uff0c\u5e76\u5728 DbContext \u4e0a\u8c03\u7528 SaveChangesAsync\uff08\uff09 \u4ee5\u751f\u6210 SQL \u4ee5\u66f4\u65b0\u6570\u636e\u5e93\u4e2d\u7684\u6b63\u786e\u884c\u3002<\/p>\n<p>I\u2019m not going to handle the relationship aspect in this book because that problem generally is complex, and how you tackle it depends on the specifics of your data model. Instead, I\u2019ll focus on updating properties on the Recipe entity itself.<\/p>\n<p>\u6211\u4e0d\u6253\u7b97\u5728\u672c\u4e66\u4e2d\u5904\u7406\u5173\u7cfb\u65b9\u9762\uff0c\u56e0\u4e3a\u8fd9\u4e2a\u95ee\u9898\u901a\u5e38\u5f88\u590d\u6742\uff0c\u5982\u4f55\u89e3\u51b3\u5b83\u53d6\u51b3\u4e8e\u6570\u636e\u6a21\u578b\u7684\u5177\u4f53\u60c5\u51b5\u3002\u76f8\u53cd\uff0c\u6211\u5c06\u91cd\u70b9\u4ecb\u7ecd\u66f4\u65b0 Recipe \u4e0a\u7684\u5c5e\u6027\u5b9e\u4f53\u672c\u8eab\u3002<\/p>\n<p><strong>Note<\/strong> For a detailed discussion of handling relationship updates in EF Core, see Entity Framework Core in Action, 2nd ed., by Jon P. Smith (Manning, 2021; <a href=\"http:\/\/mng.bz\/w9D2\">http:\/\/mng.bz\/w9D2<\/a>).<br \/>\n<strong>\u6ce8\u610f<\/strong> \u6709\u5173\u5728 EF Core \u4e2d\u5904\u7406\u5173\u7cfb\u66f4\u65b0\u7684\u8be6\u7ec6\u8ba8\u8bba\uff0c\u8bf7\u53c2\u9605 Jon P. Smith \u7684 Entity Framework Core in Action\uff0c\u7b2c 2 \u7248\uff08Manning\uff0c2021 \u5e74; <a href=\"http:\/\/mng.bz\/w9D2\">http:\/\/mng.bz\/w9D2<\/a>\uff09\u3002<\/p>\n<p>For web applications, when you update an entity you typically follow the steps outlined in figure 12.14:<br \/>\n\u5bf9\u4e8e Web \u5e94\u7528\u7a0b\u5e8f\uff0c\u5f53\u60a8\u66f4\u65b0\u5b9e\u4f53\u65f6\uff0c\u901a\u5e38\u4f1a\u6309\u7167\u56fe 12.14 \u4e2d\u6982\u8ff0\u7684\u6b65\u9aa4\u8fdb\u884c\u4f5c\uff1a<\/p>\n<ol>\n<li>Read the entity from the database.<br \/>\n\u4ece\u6570\u636e\u5e93\u4e2d\u8bfb\u53d6\u5b9e\u4f53\u3002<\/li>\n<li>Modify the entity\u2019s properties.<br \/>\n\u4fee\u6539\u5b9e\u4f53\u7684\u5c5e\u6027\u3002<br \/>\n3.Save the changes to the database.<br \/>\n\u4fdd\u5b58\u5bf9\u6570\u636e\u5e93\u7684\u66f4\u6539\u3002<\/li>\n<\/ol>\n<p>You\u2019ll encapsulate these three steps in a method on RecipeService called UpdateRecipe. This method takes an UpdateRecipeCommand parameter and contains the code to change the Recipe entity.<\/p>\n<p>\u60a8\u5c06\u628a\u8fd9\u4e09\u4e2a\u6b65\u9aa4\u5c01\u88c5\u5728 RecipeService \u4e0a\u540d\u4e3a UpdateRecipe \u7684\u65b9\u6cd5\u4e2d\u3002\u6b64\u65b9\u6cd5\u91c7\u7528 UpdateRecipeCommand \u53c2\u6570\uff0c\u5e76\u5305\u542b\u7528\u4e8e\u66f4\u6539 Recipe \u5b9e\u4f53\u7684\u4ee3\u7801\u3002<\/p>\n<p><strong>Note<\/strong> As with the Create command, you don\u2019t modify the entities directly in the minimal API endpoint handler, ensuring that you keep the UI\/API concern separate from the business logic.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u4e0e Create \u547d\u4ee4\u4e00\u6837\uff0c\u60a8\u4e0d\u4f1a\u76f4\u63a5\u5728\u6700\u5c0f API \u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u4fee\u6539\u5b9e\u4f53\uff0c\u4ece\u800c\u786e\u4fdd\u5c06 UI\/API \u5173\u6ce8\u70b9\u4e0e\u4e1a\u52a1\u903b\u8f91\u5206\u5f00\u3002<\/p>\n<p>Listing 12.9 shows the RecipeService.UpdateRecipe method, which updates the Recipe entity. It performs the three steps we defined previously to read, modify, and save the entity. I\u2019ve extracted the code to update the recipe with the new values to a helper method for clarity.<\/p>\n<p>\u6e05\u5355 12.9 \u663e\u793a\u4e86 RecipeService.UpdateRecipe \u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u66f4\u65b0\u4e86 Recipe \u5b9e\u4f53\u3002\u5b83\u6267\u884c\u6211\u4eec\u4e4b\u524d\u5b9a\u4e49\u7684\u4e09\u4e2a\u6b65\u9aa4\u6765\u8bfb\u53d6\u3001\u4fee\u6539\u548c\u4fdd\u5b58\u5b9e\u4f53\u3002\u4e3a\u6e05\u695a\u8d77\u89c1\uff0c\u6211\u63d0\u53d6\u4e86\u4ee3\u7801\uff0c\u4ee5\u4f7f\u7528\u65b0\u503c\u5c06\u914d\u65b9\u66f4\u65b0\u4e3a\u5e2e\u52a9\u7a0b\u5e8f\u65b9\u6cd5\u3002<\/p>\n<p>Listing 12.9 Updating an existing entity with EF Core in RecipeService<br \/>\n\u6e05\u5355 12.9 \u4f7f\u7528 EF Core \u66f4\u65b0\u73b0\u6709\u5b9e\u4f53\u5728 RecipeService \u4e2d<\/p>\n<pre><code>public async Task UpdateRecipe(UpdateRecipeCommand cmd)\n{\n    var recipe = await _context.Recipes.FindAsync(cmd.Id);  \u2776\n    if(recipe is null) {                                    \u2777\n        throw new Exception(&quot;Unable to find the recipe&quot;);   \u2777\n    }                                                       \u2777\n    UpdateRecipe(recipe, cmd);             \u2778\n    await _context.SaveChangesAsync();    \u2779\n}\n\nstatic void UpdateRecipe(Recipe recipe, UpdateRecipeCommand cmd)   \u277a\n{                                                                  \u277a\n    recipe.Name = cmd.Name;                                        \u277a\n    recipe.TimeToCook =                                            \u277a\n        new TimeSpan(cmd.TimeToCookHrs, cmd.TimeToCookMins, 0);    \u277a\n    recipe.Method = cmd.Method;                                    \u277a\n    recipe.IsVegetarian = cmd.IsVegetarian;                        \u277a\n    recipe.IsVegan = cmd.IsVegan;                                  \u277a\n}                                                                  \u277a<\/code><\/pre>\n<p>\u2776 Find is exposed directly by Recipes and simplifies reading an entity by id.<br \/>\nFind \u7531 Recipes \u76f4\u63a5\u516c\u5f00\uff0c\u5e76\u7b80\u5316\u4e86\u6309 ID \u8bfb\u53d6\u5b9e\u4f53\u7684\u8fc7\u7a0b\u3002<br \/>\n\u2777 If an invalid id is provided, recipe will be null.<br \/>\n\u5982\u679c\u63d0\u4f9b\u7684 ID \u65e0\u6548\uff0c\u5219 recipe \u5c06\u4e3a null\u3002<br \/>\n\u2778 Sets the new values on the Recipe entity<br \/>\n\u5728 Recipe \u5b9e\u4f53\u4e0a\u8bbe\u7f6e\u65b0\u503c<br \/>\n\u2779 Executes the SQL to save the changes to the database<br \/>\n\u6267\u884c SQL \u4ee5\u4fdd\u5b58\u5bf9\u6570\u636e\u5e93\u7684\u66f4\u6539<br \/>\n\u277a A helper method for setting the new properties on the Recipe entity<br \/>\n\u7528\u4e8e\u5728 Recipe \u5b9e\u4f53\u4e0a\u8bbe\u7f6e\u65b0\u5c5e\u6027\u7684\u8f85\u52a9\u65b9\u6cd5<\/p>\n<p>In this example I read the Recipe entity using the FindAsync(id) method exposed by DbSet. This simple helper method loads an entity by its ID\u2014in this case, RecipeId. I could have written a similar query with LINQ:<\/p>\n<p>\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u6211\u4f7f\u7528 DbSet \u516c\u5f00\u7684 FindAsync\uff08id\uff09 \u65b9\u6cd5\u8bfb\u53d6 Recipe \u5b9e\u4f53\u3002\u8fd9\u4e2a\u7b80\u5355\u7684\u5e2e\u52a9\u7a0b\u5e8f\u65b9\u6cd5\u6309\u5b9e\u4f53\u7684 ID \u52a0\u8f7d\u5b9e\u4f53\uff0c\u5728\u672c\u4f8b\u4e2d\u4e3a RecipeId\u3002\u6211\u672c\u53ef\u4ee5\u4f7f\u7528 LINQ \u7f16\u5199\u7c7b\u4f3c\u7684\u67e5\u8be2\uff1a<\/p>\n<pre><code>_context.Recipes.Where(r=&gt;r.RecipeId == cmd.Id).FirstOrDefault();<\/code><\/pre>\n<p>Using FindAsync() or Find() is a little more declarative and concise, however.<br \/>\n\u4f46\u662f\uff0c\u4f7f\u7528 FindAsync\uff08\uff09 \u6216 Find\uff08\uff09 \u7684\u58f0\u660e\u6027\u548c\u7b80\u6d01\u6027\u66f4\u5f3a\u4e00\u4e9b\u3002<\/p>\n<p><strong>Tip<\/strong> Find is a bit more complicated. Find first checks to see whether the entity is already being tracked in EF Core\u2019s DbContext. If so (because the entity was previously loaded in this request), the entity is returned immediately without calling the database. Using Find can obviously be faster if the entity is tracked, but it can be slower if you know that the entity isn\u2019t being tracked yet.<br \/>\n<strong>\u63d0\u793a<\/strong> Find \u7a0d\u5fae\u590d\u6742\u4e00\u4e9b\u3002\u201c\u67e5\u627e\u4f18\u5148\u201d\u68c0\u67e5\u662f\u5426\u5df2\u5728 EF Core \u7684 DbContext \u4e2d\u8ddf\u8e2a\u5b9e\u4f53\u3002\u5982\u679c\u662f\u8fd9\u6837\uff08\u56e0\u4e3a\u4e4b\u524d\u5728\u6b64\u8bf7\u6c42\u4e2d\u52a0\u8f7d\u4e86\u5b9e\u4f53\uff09\uff0c\u5219\u7acb\u5373\u8fd4\u56de\u8be5\u5b9e\u4f53\uff0c\u800c\u4e0d\u8c03\u7528\u6570\u636e\u5e93\u3002\u5982\u679c\u8ddf\u8e2a\u5b9e\u4f53\uff0c\u5219\u4f7f\u7528 Find \u663e\u7136\u4f1a\u66f4\u5feb\uff0c\u4f46\u5982\u679c\u60a8\u77e5\u9053\u5c1a\u672a\u8ddf\u8e2a\u5b9e\u4f53\uff0c\u5219\u4f7f\u7528 Find \u53ef\u80fd\u4f1a\u66f4\u6162\u3002<\/p>\n<p>You may wonder how EF Core knows which columns to update when you call SaveChangesAsync(). The simplest approach would be to update every column. If the field hasn\u2019t changed, it doesn\u2019t matter if you write the same value again. But EF Core is cleverer than that.<\/p>\n<p>\u4f60\u53ef\u80fd\u60f3\u77e5\u9053 EF Core \u5728\u8c03\u7528 SaveChangesAsync\uff08\uff09 \u65f6\u5982\u4f55\u77e5\u9053\u8981\u66f4\u65b0\u54ea\u4e9b\u5217\u3002\u6700\u7b80\u5355\u7684\u65b9\u6cd5\u662f\u66f4\u65b0\u6bcf\u4e00\u5217\u3002\u5982\u679c\u5b57\u6bb5\u672a\u66f4\u6539\uff0c\u5219\u518d\u6b21\u5199\u5165\u76f8\u540c\u7684\u503c\u5e76\u4e0d\u91cd\u8981\u3002\u4f46 EF Core \u6bd4\u8fd9\u66f4\u806a\u660e\u3002<\/p>\n<p>EF Core internally tracks the state of any entities it loads from the database and creates a snapshot of all the entity\u2019s property values so that it can track which ones have changed. When you call SaveChanges(), EF Core compares the state of any tracked entities (in this case, the Recipe entity) with the tracking snapshot. Any properties that have been changed are included in the UPDATE statement sent to the database, and unchanged properties are ignored.<\/p>\n<p>EF Core \u5728\u5185\u90e8\u8ddf\u8e2a\u5b83\u4ece\u6570\u636e\u5e93\u52a0\u8f7d\u7684\u4efb\u4f55\u5b9e\u4f53\u7684\u72b6\u6001\uff0c\u5e76\u521b\u5efa\u6240\u6709\u5b9e\u4f53\u5c5e\u6027\u503c\u7684\u5feb\u7167\uff0c\u4ee5\u4fbf\u5b83\u53ef\u4ee5\u8ddf\u8e2a\u54ea\u4e9b\u5b9e\u4f53\u5df2\u66f4\u6539\u3002\u8c03\u7528 SaveChanges\uff08\uff09 \u65f6\uff0cEF Core \u4f1a\u5c06\u4efb\u4f55\u8ddf\u8e2a\u5b9e\u4f53\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a Recipe \u5b9e\u4f53\uff09\u7684\u72b6\u6001\u4e0e\u8ddf\u8e2a\u5feb\u7167\u8fdb\u884c\u6bd4\u8f83\u3002\u4efb\u4f55\u5df2\u66f4\u6539\u7684\u5c5e\u6027\u90fd\u5305\u542b\u5728\u53d1\u9001\u5230\u6570\u636e\u5e93\u7684 UPDATE \u8bed\u53e5\u4e2d\uff0c\u800c\u672a\u66f4\u6539\u7684\u5c5e\u6027\u5c06\u88ab\u5ffd\u7565\u3002<\/p>\n<p><strong>Note<\/strong> EF Core provides other mechanisms to track changes, as well as options to disable change tracking. See the documentation or chapter 3 of Jon P. Smith\u2019s Entity Framework Core in Action, 2nd ed., (Manning, 2021; <a href=\"http:\/\/mng.bz\/q9PJ\">http:\/\/mng.bz\/q9PJ<\/a>) for details. You can view which details the DbContext is tracking by accessing DbContext.ChangeTracer.DebugView, as described in the documentation at <a href=\"http:\/\/mng.bz\/8rlz\">http:\/\/mng.bz\/8rlz<\/a>.<br \/>\n<strong>\u6ce8\u610f<\/strong> EF Core \u63d0\u4f9b\u4e86\u5176\u4ed6\u673a\u5236\u6765\u8ddf\u8e2a\u66f4\u6539\uff0c\u4ee5\u53ca\u7528\u4e8e\u7981\u7528\u66f4\u6539\u8ddf\u8e2a\u7684\u9009\u9879\u3002\u8bf7\u53c2\u9605 Jon P. Smith \u7684 Entity Framework Core in Action\uff0c 2nd ed.\uff08Manning\uff0c2021 \u5e74;<a href=\"http:\/\/mng.bz\/q9PJ\uff09\u4e86\u89e3\u8be6\u60c5\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8bbf\u95ee\">http:\/\/mng.bz\/q9PJ\uff09\u4e86\u89e3\u8be6\u60c5\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u8bbf\u95ee<\/a> DbContext.ChangeTracer.DebugView \u6765\u67e5\u770b DbContext \u6b63\u5728\u8ddf\u8e2a\u7684\u8be6\u7ec6\u4fe1\u606f\uff0c\u5982 <a href=\"http:\/\/mng.bz\/8rlz\">http:\/\/mng.bz\/8rlz<\/a> \u4e2d\u7684\u6587\u6863\u4e2d\u6240\u8ff0\u3002<\/p>\n<p>With the ability to update recipes, you\u2019re almost done with your recipe app. \u201cBut wait!\u201d I hear you cry. \u201cwe haven\u2019t handled the D in CRUD: delete!\u201d That\u2019s true, but in reality, I\u2019ve found only a few occasions to delete data. Let\u2019s consider the requirements for deleting a recipe from the application:<\/p>\n<p>\u501f\u52a9\u66f4\u65b0\u98df\u8c31\u7684\u529f\u80fd\uff0c\u60a8\u51e0\u4e4e\u5b8c\u6210\u4e86\u98df\u8c31\u5e94\u7528\u7a0b\u5e8f\u3002\u201c\u4f46\u662f\u7b49\u7b49\uff01\u201d\u6211\u542c\u5230\u4f60\u54ed\u6ce3\u3002\u201c\u6211\u4eec\u8fd8\u6ca1\u6709\u5904\u7406 CRUD \u4e2d\u7684 D\uff1a\u5220\u9664\uff01\u201d\u8fd9\u662f\u771f\u7684\uff0c\u4f46\u5b9e\u9645\u4e0a\uff0c\u6211\u53ea\u53d1\u73b0\u4e86\u5c11\u6570\u5220\u9664\u6570\u636e\u7684\u673a\u4f1a\u3002\u8ba9\u6211\u4eec\u8003\u8651\u4e00\u4e0b\u4ece\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5220\u9664\u914d\u65b9\u7684\u8981\u6c42\uff1a<\/p>\n<ul>\n<li>\n<p>You need to provide an API that deletes a recipe.<br \/>\n\u60a8\u9700\u8981\u63d0\u4f9b\u7528\u4e8e\u5220\u9664\u914d\u65b9\u7684 API\u3002<\/p>\n<\/li>\n<li>\n<p>After a recipe is deleted, it must not appear in the recipe list and can\u2019t be retrieved.<br \/>\n\u5220\u9664\u914d\u65b9\u540e\uff0c\u5b83\u4e0d\u5f97\u663e\u793a\u5728\u914d\u65b9\u5217\u8868\u4e2d\uff0c\u4e5f\u65e0\u6cd5\u68c0\u7d22\u3002<\/p>\n<\/li>\n<\/ul>\n<p>You could achieve these requirements by deleting the recipe from the database, but the problem with data is that when it\u2019s gone, it\u2019s gone! What if a user accidentally deletes a record? Also, deleting a row from a relational database typically has implications on other entities. You can\u2019t delete a row from the Recipe table in your application, for example, without also deleting all the Ingredient rows that reference it, thanks to the foreign-key constraint on Ingredient.RecipeId.<\/p>\n<p>\u60a8\u53ef\u4ee5\u901a\u8fc7\u4ece\u6570\u636e\u5e93\u4e2d\u5220\u9664\u914d\u65b9\u6765\u5b9e\u73b0\u8fd9\u4e9b\u8981\u6c42\uff0c\u4f46\u6570\u636e\u7684\u95ee\u9898\u5728\u4e8e\uff0c\u5f53\u5b83\u6d88\u5931\u65f6\uff0c\u5b83\u5c31\u6d88\u5931\u4e86\uff01\u5982\u679c\u7528\u6237\u4e0d\u5c0f\u5fc3\u5220\u9664\u4e86\u8bb0\u5f55\u600e\u4e48\u529e\uff1f\u6b64\u5916\uff0c\u4ece\u5173\u7cfb\u6570\u636e\u5e93\u4e2d\u5220\u9664\u884c\u901a\u5e38\u4f1a\u5bf9\u5176\u4ed6\u5b9e\u4f53\u4ea7\u751f\u5f71\u54cd\u3002\u4f8b\u5982\uff0c\u7531\u4e8e\u5bf9 Ingredient.RecipeId \u7684\u5916\u952e\u7ea6\u675f\uff0c\u60a8\u65e0\u6cd5\u4ece\u5e94\u7528\u7a0b\u5e8f\u7684 Recipe \u8868\u4e2d\u5220\u9664\u4e00\u884c\uff0c\u800c\u65e0\u9700\u5220\u9664\u5f15\u7528\u8be5\u884c\u7684\u6240\u6709 Ingredient \u884c\u3002<\/p>\n<p>EF Core can easily handle these true deletion scenarios for you with the DbContext .Remove(entity) command, but often what you mean when you find a need to delete data is to archive it or hide it from the UI. A common approach to handling this scenario is to include some sort of \u201cIs this entity deleted?\u201d flag on your entity, such as the IsDeleted flag I included on the Recipe entity:<\/p>\n<p>EF Core \u53ef\u4ee5\u4f7f\u7528 DbContext \u8f7b\u677e\u5904\u7406\u8fd9\u4e9b\u771f\u6b63\u7684\u5220\u9664\u65b9\u6848\u3002Remove\uff08entity\uff09 \u547d\u4ee4\uff0c\u4f46\u5f53\u60a8\u53d1\u73b0\u9700\u8981\u5220\u9664\u6570\u636e\u65f6\uff0c\u60a8\u7684\u610f\u601d\u662f\u5c06\u5176\u5b58\u6863\u6216\u4ece UI \u4e2d\u9690\u85cf\u6570\u636e\u3002\u5904\u7406\u8fd9\u79cd\u60c5\u51b5\u7684\u5e38\u89c1\u65b9\u6cd5\u662f\u5305\u62ec\u67d0\u79cd\u201cIs this entity deleted\uff1f\u201d \u6807\u5fd7\uff0c\u4f8b\u5982IsDeleted \u6807\u5fd7\uff1a<\/p>\n<pre><code>public bool IsDeleted { get; set; }<\/code><\/pre>\n<p>If you take this approach, deleting data suddenly becomes simpler, as it\u2019s nothing more than an update to the entity\u2014no more problems of lost data and no more referential-integrity problems.<\/p>\n<p>\u5982\u679c\u60a8\u91c7\u7528\u8fd9\u79cd\u65b9\u6cd5\uff0c\u5220\u9664\u6570\u636e\u4f1a\u7a81\u7136\u53d8\u5f97\u66f4\u52a0\u7b80\u5355\uff0c\u56e0\u4e3a\u5b83\u53ea\u4e0d\u8fc7\u662f\u5bf9\u5b9e\u4f53\u7684\u66f4\u65b0\u2014\u2014\u4e0d\u518d\u6709\u6570\u636e\u4e22\u5931\u7684\u95ee\u9898\uff0c\u4e5f\u4e0d\u518d\u6709\u5f15\u7528\u5b8c\u6574\u6027\u95ee\u9898\u3002<\/p>\n<p><strong>Note<\/strong> The main exception I\u2019ve found to this pattern is when you\u2019re storing your users\u2019 personally identifying information. In these cases, you may be duty-bound (and potentially legally bound) to scrub their information from your database on request.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u6211\u53d1\u73b0\u8fd9\u79cd\u6a21\u5f0f\u7684\u4e3b\u8981\u4f8b\u5916\u662f\u5f53\u60a8\u5b58\u50a8\u7528\u6237\u7684\u4e2a\u4eba\u8eab\u4efd\u4fe1\u606f\u65f6\u3002\u5728\u8fd9\u4e9b\u60c5\u51b5\u4e0b\uff0c\u60a8\u53ef\u80fd\u6709\u4e49\u52a1\uff08\u5e76\u4e14\u53ef\u80fd\u53d7\u6cd5\u5f8b\u7ea6\u675f\uff09\u6839\u636e\u8981\u6c42\u4ece\u60a8\u7684\u6570\u636e\u5e93\u4e2d\u5220\u9664\u4ed6\u4eec\u7684\u4fe1\u606f\u3002<\/p>\n<p>With this approach, you can create a delete method on RecipeService that updates the IsDeleted flag, as shown in listing 12.10. In addition, make sure that you have Where() clauses in all the other methods in your RecipeService to ensure you can\u2019t return a deleted Recipe, as you saw in listing 12.9 for the GetRecipes() method.<\/p>\n<p>\u4f7f\u7528\u8fd9\u79cd\u65b9\u6cd5\uff0c\u4f60\u53ef\u4ee5\u5728 RecipeService \u4e0a\u521b\u5efa\u4e00\u4e2a delete \u65b9\u6cd5\u6765 \u66f4\u65b0 IsDeleted \u6807\u5fd7\uff0c\u5982\u6e05\u5355 12.10 \u6240\u793a\u3002\u6b64\u5916\uff0c\u8bf7\u786e\u4fdd\u5728 RecipeService \u7684\u6240\u6709\u5176\u4ed6\u65b9\u6cd5\u4e2d\u90fd\u6709 Where\uff08\uff09 \u5b50\u53e5\uff0c\u4ee5\u786e\u4fdd\u65e0\u6cd5\u8fd4\u56de\u5df2\u5220\u9664\u7684 Recipe\uff0c\u5982\u6e05\u5355 12.9 \u4e2d\u7684 GetRecipes\uff08\uff09 \u65b9\u6cd5\u6240\u793a\u3002<\/p>\n<p>Listing 12.10 Marking entities as deleted in EF Core<br \/>\n\u5217\u8868 12.10 \u5728 EF Core \u4e2d\u5c06\u5b9e\u4f53\u6807\u8bb0\u4e3a\u5df2\u5220\u9664<\/p>\n<pre><code>public async Task DeleteRecipe(int recipeId)\n{\n    var recipe = await _context.Recipes.FindAsync(recipeId);     \u2776\n    if(recipe is null) {                                    \u2777\n        throw new Exception(&quot;Unable to find the recipe&quot;);   \u2777\n    }                                                       \u2777\n    recipe.IsDeleted = true;   \u2778\n    await _context.SaveChangesAsync();   \u2779\n}<\/code><\/pre>\n<p>\u2776 Fetches the Recipe entity by id<br \/>\n\u6309 id \u83b7\u53d6 Recipe \u5b9e\u4f53<br \/>\n\u2777 If an invalid id is provided, recipe will be null.<br \/>\n\u5982\u679c\u63d0\u4f9b\u7684 ID \u65e0\u6548\uff0c\u5219 recipe \u5c06\u4e3a null\u3002<br \/>\n\u2778 Marks the Recipe as deleted<br \/>\n\u5c06\u914d\u65b9\u6807\u8bb0\u4e3a\u5df2\u5220\u9664<br \/>\n\u2779 Executes the SQL to save the changes to the database<br \/>\n\u884c SQL \u4ee5\u4fdd\u5b58\u5bf9\u6570\u636e\u5e93\u7684\u66f4\u6539<\/p>\n<p>This approach satisfies the requirements\u2014it removes the recipe from exposure by the API\u2014but it simplifies several things. This soft-delete approach won\u2019t work for all scenarios, but I\u2019ve found it to be a common pattern in projects I\u2019ve worked on.<\/p>\n<p>\u8fd9\u79cd\u65b9\u6cd5\u6ee1\u8db3\u4e86\u8981\u6c42\uff0c\u5b83\u6d88\u9664\u4e86 API \u516c\u5f00\u7684\u914d\u65b9\uff0c\u4f46\u5b83\u7b80\u5316\u4e86\u51e0\u4ef6\u4e8b\u3002\u8fd9\u79cd\u8f6f\u5220\u9664\u65b9\u6cd5\u5e76\u4e0d\u9002\u7528\u4e8e\u6240\u6709\u60c5\u51b5\uff0c\u4f46\u6211\u53d1\u73b0\u5b83\u662f\u6211\u53c2\u4e0e\u8fc7\u7684\u9879\u76ee\u4e2d\u7684\u5e38\u89c1\u6a21\u5f0f\u3002<\/p>\n<p><strong>Tip<\/strong> EF Core has a handy feature called global query filters. These filters allow\\ you to specify a Where clause at the model level. You could ensure, for example, that EF Core never loads Recipes for which IsDeleted is true. This feature is also useful for segregating data in a multitenant environment. See the documentation for details: <a href=\"http:\/\/mng.bz\/EQxd\">http:\/\/mng.bz\/EQxd<\/a>.<br \/>\n<strong>\u63d0\u793a<\/strong> EF Core \u6709\u4e00\u4e2a\u79f0\u4e3a\u5168\u5c40\u67e5\u8be2\u7b5b\u9009\u5668\u7684\u4fbf\u6377\u529f\u80fd\u3002\u8fd9\u4e9b\u7b5b\u9009\u6761\u4ef6\u5141\u8bb8\u60a8\u5728\u6a21\u578b\u7ea7\u522b\u6307\u5b9a Where \u5b50\u53e5\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u786e\u4fdd EF Core \u6c38\u8fdc\u4e0d\u4f1a\u52a0\u8f7d IsDeleted \u4e3a true \u7684\u914d\u65b9\u3002\u6b64\u529f\u80fd\u5bf9\u4e8e\u5728\u591a\u79df\u6237\u73af\u5883\u4e2d\u9694\u79bb\u6570\u636e\u4e5f\u5f88\u6709\u7528\u3002\u6709\u5173\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605\u6587\u6863\uff1a<a href=\"http:\/\/mng.bz\/EQxd\">http:\/\/mng.bz\/EQxd<\/a>\u3002<\/p>\n<p>We\u2019re almost at the end of this chapter on EF Core. We\u2019ve covered the basics of adding EF Core to your project and using it to simplify data access, but you\u2019ll likely need to learn more about EF Core as your apps become more complex. In the final section of this chapter, I\u2019d like to pinpoint a few things you need to take into consideration before using EF Core in your own applications so that you\u2019ll be familiar with some of the problems you\u2019ll face as your apps grow.<\/p>\n<p>\u5173\u4e8e EF Core \u7684\u672c\u7ae0\u5373\u5c06\u7ed3\u675f\u3002\u6211\u4eec\u5df2\u7ecf\u4ecb\u7ecd\u4e86\u5c06 EF Core \u6dfb\u52a0\u5230\u9879\u76ee\u5e76\u4f7f\u7528\u5b83\u6765\u7b80\u5316\u6570\u636e\u8bbf\u95ee\u7684\u57fa\u7840\u77e5\u8bc6\uff0c\u4f46\u968f\u7740\u5e94\u7528\u53d8\u5f97\u8d8a\u6765\u8d8a\u590d\u6742\uff0c\u4f60\u53ef\u80fd\u9700\u8981\u4e86\u89e3\u6709\u5173 EF Core \u7684\u66f4\u591a\u4fe1\u606f\u3002\u5728\u672c\u7ae0\u7684\u6700\u540e\u4e00\u90e8\u5206\u4e2d\uff0c\u6211\u60f3\u6307\u51fa\u5728\u60a8\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 EF Core \u4e4b\u524d\u9700\u8981\u8003\u8651\u7684\u4e00\u4e9b\u4e8b\u9879\uff0c\u4ee5\u4fbf\u60a8\u719f\u6089\u968f\u7740\u5e94\u7528\u7a0b\u5e8f\u7684\u589e\u957f\u800c\u9762\u4e34\u7684\u4e00\u4e9b\u95ee\u9898\u3002<\/p>\n<h2>12.5 Using EF Core in production applications<\/h2>\n<h2>12.5 \u5728\u751f\u4ea7\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 EF Core<\/h2>\n<p>This book is about ASP.NET Core, not EF Core, so I didn\u2019t want to spend too much time exploring EF Core. This chapter should\u2019ve given you enough information to get up and running, but you definitely need to learn more before you even think about putting EF Core into production. As I\u2019ve said several times, I recommend reading Entity Framework Core in Action, 2nd ed., by Jon P. Smith (Manning, 2021), or exploring the EF Core documentation site at <a href=\"https:\/\/docs.microsoft.com\/ef\/core\">https:\/\/docs.microsoft.com\/ef\/core<\/a>.<\/p>\n<p>\u8fd9\u672c\u4e66\u662f\u5173\u4e8e ASP.NET Core \u7684\uff0c\u800c\u4e0d\u662f EF Core\uff0c\u56e0\u6b64\u6211\u4e0d\u60f3\u82b1\u592a\u591a\u65f6\u95f4\u63a2\u7d22 EF Core\u3002\u672c\u7ae0\u5e94\u8be5\u5df2\u7ecf\u4e3a\u4f60\u63d0\u4f9b\u4e86\u8db3\u591f\u7684\u4fe1\u606f\u6765\u542f\u52a8\u548c\u8fd0\u884c\uff0c\u4f46\u4f60\u80af\u5b9a\u9700\u8981\u5728\u8003\u8651\u5c06 EF Core \u6295\u5165\u751f\u4ea7\u4e4b\u524d\u4e86\u89e3\u66f4\u591a\u4fe1\u606f\u3002\u6b63\u5982\u6211\u591a\u6b21\u8bf4\u8fc7\u7684\uff0c\u6211\u5efa\u8bae\u9605\u8bfb Jon P. Smith \u7684 Entity Framework Core in Action\uff0c\u7b2c 2 \u7248\uff08Manning\uff0c2021 \u5e74\uff09\uff0c\u6216\u6d4f\u89c8 <a href=\"https:\/\/docs.microsoft.com\/ef\/core\">https:\/\/docs.microsoft.com\/ef\/core<\/a> \u7684 EF Core \u6587\u6863\u7ad9\u70b9\u3002<\/p>\n<p>The following topics aren\u2019t essential for getting started with EF Core, but you\u2019ll quickly run up against them if you build a production-ready app. This section isn\u2019t a prescriptive guide to tackling each of these items, but more a set of things to consider before you dive into production:<\/p>\n<p>\u4ee5\u4e0b\u4e3b\u9898\u5bf9\u4e8e\u5f00\u59cb\u4f7f\u7528 EF Core \u4e0d\u662f\u5fc5\u9700\u7684\uff0c\u4f46\u5982\u679c\u60a8\u6784\u5efa\u751f\u4ea7\u5c31\u7eea\u578b\u5e94\u7528\u7a0b\u5e8f\uff0c\u60a8\u5c06\u5f88\u5feb\u9047\u5230\u8fd9\u4e9b\u4e3b\u9898\u3002\u672c\u8282\u4e0d\u662f\u5904\u7406\u8fd9\u4e9b\u9879\u76ee\u7684\u89c4\u8303\u6027\u6307\u5357\uff0c\u800c\u662f\u5728\u6df1\u5165\u7814\u7a76\u751f\u4ea7\u4e4b\u524d\u9700\u8981\u8003\u8651\u7684\u4e00\u7ec4\u4e8b\u9879\uff1a<\/p>\n<ul>\n<li>\n<p>Scaffolding of columns\u2014EF Core uses conservative values for things like string columns by allowing strings of large or unlimited length. In practice, you may want to restrict these and other data types to sensible values.<br \/>\n\u5217\u7684\u57fa\u67b6 \u2013 EF Core \u901a\u8fc7\u5141\u8bb8\u8f83\u5927\u6216\u65e0\u9650\u957f\u5ea6\u7684\u5b57\u7b26\u4e32\uff0c\u5bf9\u5b57\u7b26\u4e32\u5217\u7b49\u5185\u5bb9\u4f7f\u7528\u4fdd\u5b88\u503c\u3002\u5728\u5b9e\u8df5\u4e2d\uff0c\u60a8\u53ef\u80fd\u5e0c\u671b\u5c06\u8fd9\u4e9b\u6570\u636e\u7c7b\u578b\u548c\u5176\u4ed6\u6570\u636e\u7c7b\u578b\u9650\u5236\u4e3a\u5408\u7406\u7684\u503c\u3002<\/p>\n<\/li>\n<li>\n<p>Validation\u2014You can decorate your entities with DataAnnotations validation attributes, but EF Core won\u2019t validate the values automatically before saving to the database. This behavior differs from EF 6.x behavior, in which validation was automatic.<br \/>\n\u9a8c\u8bc1 \u2013 \u60a8\u53ef\u4ee5\u4f7f\u7528 DataAnnotations \u9a8c\u8bc1\u5c5e\u6027\u4fee\u9970\u5b9e\u4f53\uff0c\u4f46 EF Core \u4e0d\u4f1a\u5728\u4fdd\u5b58\u5230\u6570\u636e\u5e93\u4e4b\u524d\u81ea\u52a8\u9a8c\u8bc1\u503c\u3002\u6b64\u884c\u4e3a\u4e0d\u540c\u4e8e EF 6.x \u884c\u4e3a\uff0c\u5728 EF 6.x \u4e2d\uff0c\u9a8c\u8bc1\u662f\u81ea\u52a8\u7684\u3002<\/p>\n<\/li>\n<li>\n<p>Handling concurrency\u2014EF Core provides a few ways to handle concurrency, which occurs when multiple users attempt to update an entity at the same time. One partial solution is to use Timestamp columns on your entities.<br \/>\n\u5904\u7406\u5e76\u53d1 \u2013 EF Core \u63d0\u4f9b\u4e86\u51e0\u79cd\u5904\u7406\u5e76\u53d1\u7684\u65b9\u6cd5\uff0c\u5f53\u591a\u4e2a\u7528\u6237\u5c1d\u8bd5\u540c\u65f6\u66f4\u65b0\u5b9e\u4f53\u65f6\uff0c\u4f1a\u53d1\u751f\u5e76\u53d1\u3002\u4e00\u79cd\u90e8\u5206\u89e3\u51b3\u65b9\u6848\u662f\u5728\u5b9e\u4f53\u4e0a\u4f7f\u7528 Timestamp \u5217\u3002<\/p>\n<\/li>\n<li>\n<p>Handling errors\u2014Databases and networks are inherently flaky, so you\u2019ll always have to account for transient errors. EF Core includes various features to maintain connection resiliency by retrying on network failures.<br \/>\n\u5904\u7406\u9519\u8bef - \u6570\u636e\u5e93\u548c\u7f51\u7edc\u672c\u8d28\u4e0a\u662f\u4e0d\u7a33\u5b9a\u7684\uff0c\u56e0\u6b64\u60a8\u59cb\u7ec8\u5fc5\u987b\u8003\u8651\u6682\u65f6\u6027\u9519\u8bef\u3002EF Core \u5305\u542b\u5404\u79cd\u529f\u80fd\uff0c\u53ef\u901a\u8fc7\u5728\u7f51\u7edc\u6545\u969c\u65f6\u91cd\u8bd5\u6765\u4fdd\u6301\u8fde\u63a5\u590d\u539f\u80fd\u529b\u3002<\/p>\n<\/li>\n<li>\n<p>Synchronous vs. asynchronous\u2014EF Core provides both synchronous and asynchronous commands for interacting with the database. Often, async is better for web apps, but this argument has nuances that make it impossible to recommend one approach over the other in all situations.<br \/>\n\u540c\u6b65\u4e0e\u5f02\u6b65 \u2013 EF Core \u63d0\u4f9b\u7528\u4e8e\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\u7684\u540c\u6b65\u548c\u5f02\u6b65\u547d\u4ee4\u3002\u901a\u5e38\uff0casync \u66f4\u9002\u5408 Web \u5e94\u7528\u7a0b\u5e8f\uff0c\u4f46\u6b64\u53c2\u6570\u5177\u6709\u7ec6\u5fae\u5dee\u522b\uff0c\u56e0\u6b64\u4e0d\u53ef\u80fd\u5728\u6240\u6709\u60c5\u51b5\u4e0b\u90fd\u63a8\u8350\u4e00\u79cd\u65b9\u6cd5\u800c\u4e0d\u662f\u53e6\u4e00\u79cd\u65b9\u6cd5\u3002<\/p>\n<\/li>\n<\/ul>\n<p>EF Core is a great tool for being productive in writing data-access code, but some aspects of working with a database are unavoidably awkward. Database management is one of the thorniest problems to tackle. Most web applications use some sort of database, so the following problems are likely to affect ASP.NET Core developers at some point:<\/p>\n<p>EF Core \u662f\u9ad8\u6548\u7f16\u5199\u6570\u636e\u8bbf\u95ee\u4ee3\u7801\u7684\u7edd\u4f73\u5de5\u5177\uff0c\u4f46\u4f7f\u7528\u6570\u636e\u5e93\u7684\u67d0\u4e9b\u65b9\u9762\u4e0d\u53ef\u907f\u514d\u5730\u4f1a\u9047\u5230\u56f0\u96be\u3002\u6570\u636e\u5e93\u7ba1\u7406\u662f\u9700\u8981\u89e3\u51b3\u7684\u6700\u68d8\u624b\u7684\u95ee\u9898\u4e4b\u4e00\u3002\u5927\u591a\u6570 Web \u5e94\u7528\u7a0b\u5e8f\u90fd\u4f7f\u7528\u67d0\u79cd\u7c7b\u578b\u7684\u6570\u636e\u5e93\uff0c\u56e0\u6b64\u4ee5\u4e0b\u95ee\u9898\u53ef\u80fd\u4f1a\u5728\u67d0\u4e2a\u65f6\u5019\u5f71\u54cd ASP.NET Core \u5f00\u53d1\u4eba\u5458\uff1a<\/p>\n<ul>\n<li>\n<p>Automatic migrations\u2014If you deploy your app to production automatically as part of some sort of DevOps pipeline, you\u2019ll inevitably need some way to apply migrations to a database automatically. You can tackle this situation in several ways, such as scripting the .NET tool, applying migrations in your app\u2019s startup code, using EF Core bundles, or using a custom tool. Each approach has its pros and cons.<br \/>\n\u81ea\u52a8\u8fc1\u79fb \u2014 \u5982\u679c\u60a8\u5c06\u5e94\u7528\u7a0b\u5e8f\u4f5c\u4e3a\u67d0\u79cd DevOps \u7ba1\u9053\u7684\u4e00\u90e8\u5206\u81ea\u52a8\u90e8\u7f72\u5230\u751f\u4ea7\u73af\u5883\uff0c\u90a3\u4e48\u60a8\u4e0d\u53ef\u907f\u514d\u5730\u9700\u8981\u67d0\u79cd\u65b9\u6cd5\u6765\u81ea\u52a8\u5c06\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u591a\u79cd\u65b9\u5f0f\u89e3\u51b3\u8fd9\u79cd\u60c5\u51b5\uff0c\u4f8b\u5982\u4e3a .NET \u5de5\u5177\u7f16\u5199\u811a\u672c\u3001\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u542f\u52a8\u4ee3\u7801\u4e2d\u5e94\u7528\u8fc1\u79fb\u3001\u4f7f\u7528 EF Core \u6346\u7ed1\u5305\u6216\u4f7f\u7528\u81ea\u5b9a\u4e49\u5de5\u5177\u3002\u6bcf\u79cd\u65b9\u6cd5\u90fd\u6709\u5176\u4f18\u70b9\u548c\u7f3a\u70b9\u3002<\/p>\n<\/li>\n<li>\n<p>Multiple web hosts\u2014One specific consideration is whether you have multiple web servers hosting your app, all pointing to the same database. If so, applying migrations in your app\u2019s startup code becomes harder, as you must ensure that only one app can migrate the database at a time.<br \/>\n\u591a\u4e2a Web \u4e3b\u673a - \u4e00\u4e2a\u7279\u522b\u7684\u8003\u8651\u56e0\u7d20\u662f\u60a8\u662f\u5426\u6709\u591a\u4e2a Web \u670d\u52a1\u5668\u6258\u7ba1\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u6240\u6709\u670d\u52a1\u5668\u90fd\u6307\u5411\u540c\u4e00\u4e2a\u6570\u636e\u5e93\u3002\u5982\u679c\u662f\u8fd9\u6837\uff0c\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u542f\u52a8\u4ee3\u7801\u4e2d\u5e94\u7528\u8fc1\u79fb\u4f1a\u53d8\u5f97\u66f4\u52a0\u56f0\u96be\uff0c\u56e0\u4e3a\u60a8\u5fc5\u987b\u786e\u4fdd\u4e00\u6b21\u53ea\u6709\u4e00\u4e2a\u5e94\u7528\u7a0b\u5e8f\u53ef\u4ee5\u8fc1\u79fb\u6570\u636e\u5e93\u3002<\/p>\n<\/li>\n<li>\n<p>Making backward-compatible schema changes\u2014A corollary of the multiple-web-host approach is that you\u2019ll often be in a situation in which your app accesses a database that has a newer schema than the app thinks. Normally, you should endeavor to make schema changes backward-compatible wherever possible.<br \/>\n\u8fdb\u884c\u5411\u540e\u517c\u5bb9\u7684\u67b6\u6784\u66f4\u6539 \u2013 \u591a Web \u4e3b\u673a\u65b9\u6cd5\u7684\u4e00\u4e2a\u5fc5\u7136\u7ed3\u679c\u662f\uff0c\u60a8\u7ecf\u5e38\u4f1a\u9047\u5230\u8fd9\u6837\u7684\u60c5\u51b5\uff1a\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u8bbf\u95ee\u7684\u6570\u636e\u5e93\u5177\u6709\u6bd4\u5e94\u7528\u7a0b\u5e8f\u8ba4\u4e3a\u7684\u67b6\u6784\u66f4\u65b0\u7684\u67b6\u6784\u3002\u901a\u5e38\uff0c\u60a8\u5e94\u8be5\u5c3d\u53ef\u80fd\u52aa\u529b\u4f7f\u67b6\u6784\u66f4\u6539\u5411\u540e\u517c\u5bb9\u3002<\/p>\n<\/li>\n<li>\n<p>Storing migrations in a different assembly\u2014In this chapter I included all my logic in a single project, but in larger apps, data access is often in a different project from the web app. For apps with this structure, you must use slightly different commands when using .NET CLI or PowerShell cmdlets.<br \/>\n\u5c06\u8fc1\u79fb\u5b58\u50a8\u5728\u4e0d\u540c\u7684\u7a0b\u5e8f\u96c6\u4e2d \u2013 \u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u5c06\u6240\u6709\u903b\u8f91\u5305\u542b\u5728\u4e00\u4e2a\u9879\u76ee\u4e2d\uff0c\u4f46\u5728\u8f83\u5927\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u6570\u636e\u8bbf\u95ee\u901a\u5e38\u4e0e Web \u5e94\u7528\u7a0b\u5e8f\u4f4d\u4e8e\u4e0d\u540c\u7684\u9879\u76ee\u4e2d\u3002\u5bf9\u4e8e\u5177\u6709\u6b64\u7ed3\u6784\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u5728\u4f7f\u7528 .NET CLI \u6216 PowerShell cmdlet \u65f6\uff0c\u5fc5\u987b\u4f7f\u7528\u7565\u6709\u4e0d\u540c\u7684\u547d\u4ee4\u3002<\/p>\n<\/li>\n<li>\n<p>Seeding data\u2014When you first create a database, you often want it to have some initial seed data, such as a default user. EF 6.x had a mechanism for seeding data built in, whereas EF Core requires you to seed your database explicitly yourself.<br \/>\n\u79cd\u5b50\u8bbe\u5b9a\u6570\u636e \u2013 \u9996\u6b21\u521b\u5efa\u6570\u636e\u5e93\u65f6\uff0c\u60a8\u901a\u5e38\u5e0c\u671b\u5b83\u5177\u6709\u4e00\u4e9b\u521d\u59cb\u79cd\u5b50\u6570\u636e\uff0c\u4f8b\u5982\u9ed8\u8ba4\u7528\u6237\u3002EF 6.x \u5185\u7f6e\u4e86\u7528\u4e8e\u8bbe\u5b9a\u6570\u636e\u79cd\u5b50\u7684\u673a\u5236\uff0c\u800c EF Core \u8981\u6c42\u60a8\u81ea\u5df1\u663e\u5f0f\u8bbe\u5b9a\u6570\u636e\u5e93\u79cd\u5b50\u3002<\/p>\n<\/li>\n<\/ul>\n<p>How you choose to handle each of these problems depends on the infrastructure and the deployment approach you take with your app. None is particularly fun to tackle, but all are unfortunate necessities. Take heart, though; all these problems can be solved one way or another!<\/p>\n<p>\u60a8\u9009\u62e9\u5982\u4f55\u5904\u7406\u8fd9\u4e9b\u95ee\u9898\u53d6\u51b3\u4e8e\u60a8\u5bf9\u5e94\u7528\u7a0b\u5e8f\u91c7\u7528\u7684\u57fa\u7840\u8bbe\u65bd\u548c\u90e8\u7f72\u65b9\u6cd5\u3002\u6ca1\u6709\u4e00\u4e2a\u662f\u7279\u522b\u6709\u8da3\u7684\uff0c\u4f46\u90fd\u662f\u4e0d\u5e78\u7684\u5fc5\u9700\u54c1\u3002\u4e0d\u8fc7\uff0c\u8bf7\u653e\u5fc3;\u6240\u6709\u8fd9\u4e9b\u95ee\u9898\u90fd\u53ef\u4ee5\u4ee5\u67d0\u79cd\u65b9\u5f0f\u89e3\u51b3\uff01<\/p>\n<p>That brings us to the end of this chapter on EF Core and part 2 of the book. In part 3 we move away from minimal APIs to look at building server-rendered page-based applications with Razor Pages.<\/p>\n<p>\u8fd9\u6837\uff0c\u6211\u4eec\u5c31\u7ed3\u675f\u4e86\u672c\u7ae0\u7684 EF Core \u548c\u672c\u4e66\u7684\u7b2c 2 \u90e8\u5206\u3002\u5728\u7b2c 3 \u90e8\u5206\u4e2d\uff0c\u6211\u4eec\u5c06\u4ece\u6700\u5c0f\u7684 API \u8f6c\u5411\u4f7f\u7528 Razor Pages \u6784\u5efa\u670d\u52a1\u5668\u5448\u73b0\u7684\u57fa\u4e8e\u9875\u9762\u7684\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<h2>12.6 Summary<\/h2>\n<h2>12.6 \u603b\u7ed3<\/h2>\n<p>EF Core is an ORM that lets you interact with a database by manipulating standard POCO classes called entities in your application, reducing the amount of SQL and database knowledge you need to be productive.<br \/>\nEF Core \u662f\u4e00\u79cd ORM\uff0c\u5141\u8bb8\u60a8\u901a\u8fc7\u4f5c\u5e94\u7528\u7a0b\u5e8f\u4e2d\u79f0\u4e3a\u5b9e\u4f53\u7684\u6807\u51c6 POCO \u7c7b\u6765\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\uff0c\u4ece\u800c\u51cf\u5c11\u63d0\u9ad8\u5de5\u4f5c\u6548\u7387\u6240\u9700\u7684 SQL \u548c\u6570\u636e\u5e93\u77e5\u8bc6\u91cf\u3002<\/p>\n<p>EF Core maps entity classes to tables, properties on the entity to columns in the tables, and instances of entity objects to rows in these tables. Even if you use EF Core to avoid working with a database directly, you need to keep this mapping in mind.<br \/>\nEF Core \u5c06\u5b9e\u4f53\u7c7b\u6620\u5c04\u5230\u8868\uff0c\u5c06\u5b9e\u4f53\u7684\u5c5e\u6027\u6620\u5c04\u5230\u8868\u4e2d\u7684\u5217\uff0c\u5e76\u5c06\u5b9e\u4f53\u5bf9\u8c61\u7684\u5b9e\u4f8b\u6620\u5c04\u5230\u8fd9\u4e9b\u8868\u4e2d\u7684\u884c\u3002\u5373\u4f7f\u4f7f\u7528 EF Core \u6765\u907f\u514d\u76f4\u63a5\u4f7f\u7528\u6570\u636e\u5e93\uff0c\u4e5f\u9700\u8981\u7262\u8bb0\u6b64\u6620\u5c04\u3002<\/p>\n<p>EF Core uses a database-provider model that lets you change the underlying database without changing any of your object manipulation code. EF Core has database providers for Microsoft SQL Server, SQLite, PostgreSQL, MySQL, and many others.<br \/>\nEF Core \u4f7f\u7528\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u6a21\u578b\uff0c\u8be5\u6a21\u578b\u5141\u8bb8\u60a8\u5728\u4e0d\u66f4\u6539\u4efb\u4f55\u5bf9\u8c61\u4f5c\u4ee3\u7801\u7684\u60c5\u51b5\u4e0b\u66f4\u6539\u57fa\u7840\u6570\u636e\u5e93\u3002EF Core \u5177\u6709\u9002\u7528\u4e8e Microsoft SQL Server\u3001SQLite\u3001PostgreSQL\u3001MySQL \u7b49\u7684\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u3002<\/p>\n<p>EF Core is cross-platform and has good performance for an ORM, but it has a different feature set from EF 6.x. Nevertheless, EF Core is recommended for all new applications after EF 6.x.<br \/>\nEF Core \u662f\u8de8\u5e73\u53f0\u7684\uff0c\u5bf9\u4e8e ORM \u5177\u6709\u826f\u597d\u7684\u6027\u80fd\uff0c\u4f46\u5b83\u7684\u529f\u80fd\u96c6\u4e0e EF 6.x \u4e0d\u540c\u3002\u4e0d\u8fc7\uff0c\u5efa\u8bae\u5c06 EF Core \u7528\u4e8e EF 6.x \u4e4b\u540e\u7684\u6240\u6709\u65b0\u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>EF Core stores an internal representation of the entities in your application and how they map to the database, based on the DbSet<T> properties on your application\u2019s DbContext. EF Core builds a model based on the entity classes themselves and any other entities they reference.<br \/>\nEF Core \u6839\u636e\u5e94\u7528\u7a0b\u5e8f\u7684 DbContext \u4e0a\u7684 DbSet<T> \u5c5e\u6027\u5b58\u50a8\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5b9e\u4f53\u7684\u5185\u90e8\u8868\u793a\u5f62\u5f0f\u4ee5\u53ca\u5b83\u4eec\u5982\u4f55\u6620\u5c04\u5230\u6570\u636e\u5e93\u3002EF Core \u57fa\u4e8e\u5b9e\u4f53\u7c7b\u672c\u8eab\u53ca\u5176\u5f15\u7528\u7684\u4efb\u4f55\u5176\u4ed6\u5b9e\u4f53\u6784\u5efa\u6a21\u578b\u3002<\/p>\n<p>You add EF Core to your app by adding a NuGet database provider package. You should also install the design packages for EF Core, which works in conjunction with the .NET tools to generate and apply migrations to a database.<br \/>\n\u901a\u8fc7\u6dfb\u52a0 NuGet \u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u5305\uff0c\u5c06 EF Core \u6dfb\u52a0\u5230\u5e94\u7528\u3002\u60a8\u8fd8\u5e94\u8be5\u5b89\u88c5 EF Core \u7684\u8bbe\u8ba1\u5305\uff0c\u5b83\u4e0e .NET \u5de5\u5177\u7ed3\u5408\u4f7f\u7528\uff0c\u4ee5\u751f\u6210\u8fc1\u79fb\u5e76\u5c06\u5176\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u3002<\/p>\n<p>EF Core includes many conventions for how entities are defined, such as primary keys and foreign keys. You can customize how entities are defined declaratively, by using DataAnnotations, or by using a fluent API.<br \/>\nEF Core \u5305\u62ec\u8bb8\u591a\u5173\u4e8e\u5982\u4f55\u5b9a\u4e49\u5b9e\u4f53\u7684\u7ea6\u5b9a\uff0c\u4f8b\u5982\u4e3b\u952e\u548c\u5916\u952e\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 DataAnnotations \u6216\u4f7f\u7528 Fluent API \u81ea\u5b9a\u4e49\u4ee5\u58f0\u660e\u65b9\u5f0f\u5b9a\u4e49\u5b9e\u4f53\u7684\u65b9\u5f0f\u3002<\/p>\n<p>Your application uses a DbContext to interact with EF Core and the database. You register it with a DI container using AddDbContext<T>, defining the database provider and providing a connection string. This approach makes your DbContext available in the DI container throughout your app.<br \/>\n\u5e94\u7528\u7a0b\u5e8f\u4f7f\u7528 DbContext \u4e0e EF Core \u548c\u6570\u636e\u5e93\u4ea4\u4e92\u3002\u4f7f\u7528 AddDbContext<T> \u5c06\u5176\u6ce8\u518c\u5230 DI \u5bb9\u5668\uff0c\u5b9a\u4e49\u6570\u636e\u5e93\u63d0\u4f9b\u7a0b\u5e8f\u5e76\u63d0\u4f9b\u8fde\u63a5\u5b57\u7b26\u4e32\u3002\u6b64\u65b9\u6cd5\u4f7f\u60a8\u7684 DbContext \u5728\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 DI \u5bb9\u5668\u4e2d\u53ef\u7528\u3002<\/p>\n<p>EF Core uses migrations to track changes to your entity definitions. They\u2019re used to ensure that your entity definitions, EF Core\u2019s internal model, and the database schema match.<br \/>\nEF Core \u4f7f\u7528\u8fc1\u79fb\u6765\u8ddf\u8e2a\u5bf9\u5b9e\u4f53\u5b9a\u4e49\u7684\u66f4\u6539\u3002\u5b83\u4eec\u7528\u4e8e\u786e\u4fdd\u5b9e\u4f53\u5b9a\u4e49\u3001EF Core \u7684\u5185\u90e8\u6a21\u578b\u548c\u6570\u636e\u5e93\u67b6\u6784\u5339\u914d\u3002<\/p>\n<p>After changing an entity, you can create a migration using either the .NET tool or Visual Studio PowerShell cmdlets. To create a new migration with the .NET command-line interface, run dotnet ef migrations add NAME in your project folder, where NAME is the name you want to give the migration. This command compares your current DbContext snapshot with the previous version and generates the necessary SQL statements to update your database.<br \/>\n\u66f4\u6539\u5b9e\u4f53\u540e\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528 .NET \u5de5\u5177\u6216 Visual Studio PowerShell cmdlet \u521b\u5efa\u8fc1\u79fb\u3002\u8981\u4f7f\u7528 .NET \u547d\u4ee4\u884c\u754c\u9762\u521b\u5efa\u65b0\u7684\u8fc1\u79fb\uff0c\u8bf7\u5728\u9879\u76ee\u6587\u4ef6\u5939\u4e2d\u8fd0\u884c dotnet ef migrations add NAME\uff0c\u5176\u4e2d NAME \u662f\u8981\u4e3a\u8fc1\u79fb\u63d0\u4f9b\u7684\u540d\u79f0\u3002\u6b64\u547d\u4ee4\u5c06\u5f53\u524d DbContext \u5feb\u7167\u4e0e\u4ee5\u524d\u7684\u7248\u672c\u8fdb\u884c\u6bd4\u8f83\uff0c\u5e76\u751f\u6210\u5fc5\u8981\u7684 SQL \u8bed\u53e5\u6765\u66f4\u65b0\u6570\u636e\u5e93\u3002<\/p>\n<p>You can apply the migration to the database by using dotnet ef database update. This command creates the database if it doesn\u2019t already exist and applies any outstanding migrations.<br \/>\n\u53ef\u4ee5\u4f7f\u7528 dotnet ef database update \u5c06\u8fc1\u79fb\u5e94\u7528\u4e8e\u6570\u636e\u5e93\u3002\u6b64\u547d\u4ee4\u5c06\u521b\u5efa\u6570\u636e\u5e93\uff08\u5982\u679c\u5c1a\u4e0d\u5b58\u5728\uff09\u5e76\u5e94\u7528\u4efb\u4f55\u672a\u5b8c\u6210\u7684\u8fc1\u79fb\u3002<\/p>\n<p>EF Core doesn\u2019t interact with the database when it creates migrations\u2014only when you update the database explicitly\u2014so you can still create migrations when you\u2019re offline.<br \/>\nEF Core \u5728\u521b\u5efa\u8fc1\u79fb\u65f6\u4e0d\u4e0e\u6570\u636e\u5e93\u4ea4\u4e92\uff0c\u4ec5\u5728\u663e\u5f0f\u66f4\u65b0\u6570\u636e\u5e93\u65f6\u4ea4\u4e92\uff0c\u56e0\u6b64\u5728\u8131\u673a\u65f6\u4ecd\u53ef\u4ee5\u521b\u5efa\u8fc1\u79fb\u3002<\/p>\n<p>You can add entities to an EF Core database by creating a new entity, e, calling _context.Add(e) on an instance of your application\u2019s data context, _context, and calling _context.SaveChangesAsync(). This technique generates the necessary SQL INSERT statements to add the new rows to the database.<br \/>\n\u53ef\u4ee5\u901a\u8fc7\u521b\u5efa\u65b0\u5b9e\u4f53 e \u5e76\u5c06\u5b9e\u4f53\u6dfb\u52a0\u5230 EF Core \u6570\u636e\u5e93\uff0c\u8c03\u7528_\u4e0a\u4e0b\u6587\u3002Add\uff08e\uff09 \u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u6570\u636e\u4e0a\u4e0b\u6587\u7684\u5b9e\u4f8b\u4e0a\uff0c_context\uff0c\u7136\u540e\u8c03\u7528_SaveChangesAsync\uff08\uff09\u4e0a\u4e0b\u6587\u751f\u6210\u5fc5\u8981\u7684 SQL INSERT \u8bed\u53e5\u4ee5\u5c06\u65b0\u884c\u6dfb\u52a0\u5230\u6570\u636e\u5e93\u4e2d\u3002<\/p>\n<p>You can load records from a database by using the DbSet<T> properties on your app\u2019s DbContext. These properties expose the IQueryable interface so you can use LINQ statements to filter and transform the data in the database before it\u2019s returned.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728\u5e94\u7528\u7a0b\u5e8f\u7684 DbContext \u4e0a\u4f7f\u7528 DbSet<T> \u5c5e\u6027\u4ece\u6570\u636e\u5e93\u52a0\u8f7d\u8bb0\u5f55\u3002\u8fd9\u4e9b\u5c5e\u6027\u516c\u5f00 IQueryable \u63a5\u53e3\uff0c\u4ee5\u4fbf\u60a8\u53ef\u4ee5\u5728\u8fd4\u56de\u6570\u636e\u5e93\u4e2d\u7684\u6570\u636e\u4e4b\u524d\u4f7f\u7528 LINQ \u8bed\u53e5\u5bf9\u5176\u8fdb\u884c\u7b5b\u9009\u548c\u8f6c\u6362\u3002<\/p>\n<p>Updating an entity consists of three steps: reading the entity from the database, modifying the entity, and saving the changes to the database. EF Core keeps track of which properties have changed so that it can optimize the SQL it generates.<br \/>\n\u66f4\u65b0\u5b9e\u4f53\u5305\u62ec\u4e09\u4e2a\u6b65\u9aa4\uff1a\u4ece\u6570\u636e\u5e93\u4e2d\u8bfb\u53d6\u5b9e\u4f53\u3001\u4fee\u6539\u5b9e\u4f53\u4ee5\u53ca\u4fdd\u5b58\u5bf9\u6570\u636e\u5e93\u7684\u66f4\u6539\u3002EF Core \u4f1a\u8ddf\u8e2a\u54ea\u4e9b\u5c5e\u6027\u5df2\u66f4\u6539\uff0c\u4ee5\u4fbf\u53ef\u4ee5\u4f18\u5316\u5b83\u751f\u6210\u7684 SQL\u3002<\/p>\n<p>You can delete entities in EF Core by using the Remove method, but you should consider carefully whether you need this function. Often. a soft delete using an IsDeleted flag on entities is safer and easier to implement.<br \/>\n\u53ef\u4ee5\u4f7f\u7528 Remove \u65b9\u6cd5\u5220\u9664 EF Core \u4e2d\u7684\u5b9e\u4f53\uff0c\u4f46\u5e94\u4ed4\u7ec6\u8003\u8651\u662f\u5426\u9700\u8981\u6b64\u51fd\u6570\u3002\u901a\u5e38\uff0c\u5728\u5b9e\u4f53\u4e0a\u4f7f\u7528 IsDeleted \u6807\u5fd7\u7684\u8f6f\u5220\u9664\u66f4\u5b89\u5168\u4e14\u66f4\u6613\u4e8e\u5b9e\u73b0\u3002<\/p>\n<p>This chapter covers only a subset of the problems you must consider when using EF Core in your applications. Before using it in a production app, you should consider (among other things) the data types generated for fields, validation, handling concurrency, the seeding of initial data, handling migrations on a running application, and handling migrations in a web-farm scenario.<br \/>\n\u672c\u7ae0\u4ec5\u4ecb\u7ecd\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 EF Core \u65f6\u5fc5\u987b\u8003\u8651\u7684\u95ee\u9898\u7684\u5b50\u96c6\u3002\u5728\u751f\u4ea7\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u5b83\u4e4b\u524d\uff0c\u60a8\u5e94\u8be5\u8003\u8651\uff08\u9664\u5176\u4ed6\u4e8b\u9879\u5916\uff09\u4e3a\u5b57\u6bb5\u751f\u6210\u7684\u6570\u636e\u7c7b\u578b\u3001\u9a8c\u8bc1\u3001\u5904\u7406\u5e76\u53d1\u3001\u521d\u59cb\u6570\u636e\u7684\u79cd\u5b50\u8bbe\u5b9a\u3001\u5728\u6b63\u5728\u8fd0\u884c\u7684\u5e94\u7528\u7a0b\u5e8f\u4e0a\u5904\u7406\u8fc1\u79fb\u4ee5\u53ca\u5728 Web \u573a\u65b9\u6848\u4e2d\u5904\u7406\u8fc1\u79fb\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>12 Saving data with Entity Framework Core 12 \u4f7f\u7528 Entity Framework Core \u4fdd\u5b58\u6570\u636e This chapter covers \u672c\u7ae0\u6db5\u76d6 Understanding what Entity Framework Core is and why you should use it \u4e86\u89e3\u4ec0\u4e48\u662f Entity Framework Core \u4ee5\u53ca\u4e3a\u4ec0\u4e48\u5e94\u8be5\u4f7f\u7528\u5b83 Adding Entity Framework Core to an ASP.NET Core application \u5c06 Entity Framework Core \u6dfb\u52a0\u5230 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f Building a data model [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[19],"class_list":["post-595","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-asp-net-core-in-action"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/595","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=595"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/595\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=595"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=595"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=595"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}