{"id":587,"date":"2025-04-05T11:36:36","date_gmt":"2025-04-05T03:36:36","guid":{"rendered":"https:\/\/www.hyy.net\/?p=587"},"modified":"2025-04-05T11:36:36","modified_gmt":"2025-04-05T03:36:36","slug":"asp-net-core-in-action-8-an-introduction-to-dependency-injection","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=587","title":{"rendered":"ASP.NET Core in Action 8 An introduction to dependency injection"},"content":{"rendered":"<h1>Part 2 Building complete applications<\/h1>\n<h2>\u7b2c 2 \u90e8\u5206\uff1a\u6784\u5efa\u5b8c\u6574\u7684\u5e94\u7528\u7a0b\u5e8f<\/h2>\n<p>We covered a lot of ground in part 1. You saw how an ASP.NET Core application is composed of middleware, and we focused heavily on minimal API endpoints. You saw how to use them to build JSON APIs, how to extract common code using filters and route groups, and how to validate your input data.<\/p>\n<p>\u6211\u4eec\u5728\u7b2c 1 \u90e8\u5206\u4e2d\u6db5\u76d6\u4e86\u5f88\u591a\u5185\u5bb9\u3002\u60a8\u4e86\u89e3\u4e86 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u662f\u5982\u4f55\u7531\u4e2d\u95f4\u4ef6\u7ec4\u6210\u7684\uff0c\u6211\u4eec\u4e3b\u8981\u5173\u6ce8\u6700\u5c11\u7684 API \u7aef\u70b9\u3002\u60a8\u4e86\u89e3\u4e86\u5982\u4f55\u4f7f\u7528\u5b83\u4eec\u6784\u5efa JSON API\uff0c\u5982\u4f55\u4f7f\u7528\u7b5b\u9009\u6761\u4ef6\u548c\u8def\u7531\u7ec4\u63d0\u53d6\u901a\u7528\u4ee3\u7801\uff0c\u4ee5\u53ca\u5982\u4f55\u9a8c\u8bc1\u8f93\u5165\u6570\u636e\u3002<\/p>\n<p>In part 2 we\u2019ll dive deeper into the framework and look at a variety of components that you\u2019ll inevitably need when you want to build more complex apps. By the end of this part, you\u2019ll be able to build dynamic applications that can be deployed to multiple environments, each with a different configuration, saving data to a database.<\/p>\n<p>\u5728\u7b2c 2 \u90e8\u5206\u4e2d\uff0c\u6211\u4eec\u5c06\u66f4\u6df1\u5165\u5730\u7814\u7a76\u8be5\u6846\u67b6\uff0c\u5e76\u4e86\u89e3\u5728\u6784\u5efa\u66f4\u590d\u6742\u7684\u5e94\u7528\u7a0b\u5e8f\u65f6\u4e0d\u53ef\u907f\u514d\u5730\u9700\u8981\u7684\u5404\u79cd\u7ec4\u4ef6\u3002\u5728\u672c\u90e8\u5206\u7ed3\u675f\u65f6\uff0c\u60a8\u5c06\u80fd\u591f\u6784\u5efa\u53ef\u90e8\u7f72\u5230\u591a\u4e2a\u73af\u5883\u7684\u52a8\u6001\u5e94\u7528\u7a0b\u5e8f\uff0c\u6bcf\u4e2a\u73af\u5883\u5177\u6709\u4e0d\u540c\u7684\u914d\u7f6e\uff0c\u4ece\u800c\u5c06\u6570\u636e\u4fdd\u5b58\u5230\u6570\u636e\u5e93\u3002<\/p>\n<p>ASP.NET Core uses dependency injection (DI) throughout its libraries, so it\u2019s important that you understand how this design pattern works. In chapter 8 I introduce DI and discuss why it is used. In chapter 9 you\u2019ll learn how to configure the services in your applications to use DI.<\/p>\n<p>ASP.NET Core \u5728\u5176\u6574\u4e2a\u5e93\u4e2d\u4f7f\u7528\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165 \uff08DI\uff09\uff0c\u56e0\u6b64\u4e86\u89e3\u6b64\u8bbe\u8ba1\u6a21\u5f0f\u7684\u5de5\u4f5c\u539f\u7406\u975e\u5e38\u91cd\u8981\u3002\u5728\u7b2c 8 \u7ae0\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd DI \u5e76\u8ba8\u8bba\u4f7f\u7528\u5b83\u7684\u539f\u56e0\u3002\u5728\u7b2c 9 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u914d\u7f6e\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u670d\u52a1\u4ee5\u4f7f\u7528 DI\u3002<\/p>\n<p>Chapter 10 looks at the ASP.NET Core configuration system, which lets you pass configuration values to your app from a range of sources\u2014JSON files, environment variables, and many more. You\u2019ll learn how to configure your app to use different values depending on the environment in which it is running, and how to bind strongly typed objects to your configuration to help reduce runtime errors.<\/p>\n<p>\u7b2c 10 \u7ae0\u4ecb\u7ecd\u4e86 ASP.NET Core \u914d\u7f6e\u7cfb\u7edf\uff0c\u5b83\u5141\u8bb8\u60a8\u5c06\u914d\u7f6e\u503c\u4ece\u4e00\u7cfb\u5217\u6765\u6e90\uff08JSON \u6587\u4ef6\u3001\u73af\u5883\u53d8\u91cf\u7b49\uff09\u4f20\u9012\u7ed9\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5c06\u5e94\u7528\u7a0b\u5e8f\u914d\u7f6e\u4e3a\u6839\u636e\u8fd0\u884c\u73af\u5883\u4f7f\u7528\u4e0d\u540c\u7684\u503c\uff0c\u4ee5\u53ca\u5982\u4f55\u5c06\u5f3a\u7c7b\u578b\u5bf9\u8c61\u7ed1\u5b9a\u5230\u914d\u7f6e\u4ee5\u5e2e\u52a9\u51cf\u5c11\u8fd0\u884c\u65f6\u9519\u8bef\u3002<\/p>\n<p>In chapter 11 you\u2019ll learn how to document your minimal APIs applications using the OpenAPI specification. Adding an OpenAPI document to your application makes it easier for others to interact with your app, but it has other benefits too. You\u2019ll learn how to use Swagger UI to easily test your app from the browser, and code generation to automatically generate strongly-typed libraries for interacting with your API.<\/p>\n<p>\u5728\u7b2c 11 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528 OpenAPI \u89c4\u8303\u6765\u8bb0\u5f55\u60a8\u7684\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u3002\u5c06 OpenAPI \u6587\u6863\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u53ef\u4ee5\u4f7f\u5176\u4ed6\u4eba\u66f4\u8f7b\u677e\u5730\u4e0e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4ea4\u4e92\uff0c\u4f46\u5b83\u8fd8\u6709\u5176\u4ed6\u597d\u5904\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528 Swagger UI \u4ece\u6d4f\u89c8\u5668\u8f7b\u677e\u6d4b\u8bd5\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u4ee5\u53ca\u5982\u4f55\u751f\u6210\u4ee3\u7801\u4ee5\u81ea\u52a8\u751f\u6210\u7528\u4e8e\u4e0e\u60a8\u7684 API \u4ea4\u4e92\u7684\u5f3a\u7c7b\u578b\u5e93\u3002<\/p>\n<p>Most web applications require some sort of data storage, so in chapter 12 I\u2019ll introduce Entity Framework Core (EF Core). This is a cross-platform library that makes it easier to connect your app to a database. EF Core is worthy of a book in and of itself, so I\u2019ll only provide a brief introduction and point you to John Smith\u2019s excellent book Entity Framework Core in Action, second edition (Manning, 2021). I\u2019ll show you how to create a database and how to insert, update, and query simple data.<\/p>\n<p>\u5927\u591a\u6570 Web \u5e94\u7528\u7a0b\u5e8f\u90fd\u9700\u8981\u67d0\u79cd\u7c7b\u578b\u7684\u6570\u636e\u5b58\u50a8\uff0c\u56e0\u6b64\u5728\u7b2c 12 \u7ae0\u4e2d\uff0c\u6211\u5c06\u4ecb\u7ecd Entity Framework Core \uff08EF Core\uff09\u3002\u8fd9\u662f\u4e00\u4e2a\u8de8\u5e73\u53f0\u5e93\uff0c\u53ef\u4ee5\u66f4\u8f7b\u677e\u5730\u5c06\u5e94\u7528\u7a0b\u5e8f\u8fde\u63a5\u5230\u6570\u636e\u5e93\u3002EF Core \u672c\u8eab\u5c31\u503c\u5f97\u4e00\u672c\u4e66\uff0c\u56e0\u6b64\u6211\u53ea\u63d0\u4f9b\u4e00\u4e2a\u7b80\u77ed\u7684\u4ecb\u7ecd\uff0c\u5e76\u5411\u60a8\u4ecb\u7ecd John Smith \u7684\u4f18\u79c0\u8457\u4f5c Entity Framework Core in Action\uff0c\u7b2c\u4e8c\u7248\uff08Manning\uff0c2021 \u5e74\uff09\u3002\u6211\u5c06\u5411\u60a8\u5c55\u793a\u5982\u4f55\u521b\u5efa\u6570\u636e\u5e93\u4ee5\u53ca\u5982\u4f55\u63d2\u5165\u3001\u66f4\u65b0\u548c\u67e5\u8be2\u7b80\u5355\u6570\u636e\u3002<\/p>\n<h1>8 An introduction to dependency injection<\/h1>\n<h1>8 \u4f9d\u8d56\u6ce8\u5165\u7b80\u4ecb<\/h1>\n<h2>This chapter covers<\/h2>\n<h2>\u672c\u7ae0\u6db5\u76d6<\/h2>\n<ul>\n<li>\n<p>Understanding the benefits of dependency injection<br \/>\n\u4e86\u89e3\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165\u7684\u597d\u5904<\/p>\n<\/li>\n<li>\n<p>Seeing how ASP.NET Core uses dependency injection<br \/>\n\u4e86\u89e3 ASP.NET Core \u5982\u4f55\u4f7f\u7528\u4f9d\u8d56\u9879\u6ce8\u5165<\/p>\n<\/li>\n<li>\n<p>Retrieving services from the DI container<br \/>\n\u4ece DI \u5bb9\u5668\u4e2d\u68c0\u7d22\u670d\u52a1<\/p>\n<\/li>\n<\/ul>\n<p>In part 1 of this book you saw the bare bones of how to build applications with ASP.NET Core. You learned how to compose middleware to create your application and how to create minimal API endpoints to handle HTTP requests. This part gave you the tools to start building simple API applications.<\/p>\n<p>\u5728\u672c\u4e66\u7684\u7b2c 1 \u90e8\u5206\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u5982\u4f55\u4f7f\u7528 ASP.NET Core \u6784\u5efa\u5e94\u7528\u7a0b\u5e8f\u7684\u57fa\u672c\u5185\u5bb9\u3002\u60a8\u5b66\u4e60\u4e86\u5982\u4f55\u7f16\u5199\u4e2d\u95f4\u4ef6\u6765\u521b\u5efa\u5e94\u7528\u7a0b\u5e8f\uff0c\u4ee5\u53ca\u5982\u4f55\u521b\u5efa\u6700\u5c0f\u7684 API \u7aef\u70b9\u6765\u5904\u7406 HTTP \u8bf7\u6c42\u3002\u8fd9\u90e8\u5206\u4e3a\u60a8\u63d0\u4f9b\u4e86\u5f00\u59cb\u6784\u5efa\u7b80\u5355 API \u5e94\u7528\u7a0b\u5e8f\u7684\u5de5\u5177\u3002<\/p>\n<p>In this chapter you\u2019ll see how to use dependency injection (DI)\u2014a design pattern that helps you develop loosely coupled code\u2014in your ASP.NET Core applications. ASP.NET Core uses the pattern extensively, both internally in the framework and in the applications you build, so you\u2019ll need to use it in all but the most trivial applications.<\/p>\n<p>\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u5728 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165 \uff08DI\uff09\uff0c\u8fd9\u662f\u4e00\u79cd\u5e2e\u52a9\u60a8\u5f00\u53d1\u677e\u6563\u8026\u5408\u4ee3\u7801\u7684\u8bbe\u8ba1\u6a21\u5f0f\u3002ASP.NET Core \u5728\u6846\u67b6\u5185\u90e8\u548c\u60a8\u6784\u5efa\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u5e7f\u6cdb\u4f7f\u7528\u8be5\u6a21\u5f0f\uff0c\u56e0\u6b64\u60a8\u9700\u8981\u5728\u9664\u6700\u7410\u788e\u7684\u5e94\u7528\u7a0b\u5e8f\u4e4b\u5916\u7684\u6240\u6709\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u5b83\u3002<\/p>\n<p>You may have heard of DI and possibly even used it in your own applications. If so, this chapter shouldn\u2019t hold many surprises for you. If you haven\u2019t used DI, never fear; I\u2019ll make sure you\u2019re up to speed by the time the chapter is done!<\/p>\n<p>\u60a8\u53ef\u80fd\u542c\u8bf4\u8fc7 DI\uff0c\u751a\u81f3\u53ef\u80fd\u5728\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u5b83\u3002\u5982\u679c\u662f\u8fd9\u6837\uff0c\u8fd9\u4e00\u7ae0\u5e94\u8be5\u4e0d\u4f1a\u7ed9\u4f60\u5e26\u6765\u592a\u591a\u60ca\u559c\u3002\u5982\u679c\u60a8\u8fd8\u6ca1\u6709\u4f7f\u7528\u8fc7 DI\uff0c\u8bf7\u4e0d\u8981\u62c5\u5fc3;\u6211\u4f1a\u786e\u4fdd\u4f60\u5728\u7ae0\u8282\u5b8c\u6210\u65f6\u8ddf\u4e0a\u8fdb\u5ea6\uff01<\/p>\n<p>This chapter introduces DI in general, the principles it drives, and why you should care about it. You\u2019ll see how ASP.NET Core has embraced DI throughout its implementation and why you should do the same when writing your own applications. Finally, you\u2019ll learn how to retrieve services from DI in your app.<\/p>\n<p>\u672c\u7ae0\u4ecb\u7ecd\u4e86 DI \u7684\u4e00\u822c\u60c5\u51b5\u3001\u5b83\u9a71\u52a8\u7684\u539f\u5219\u4ee5\u53ca\u60a8\u5e94\u8be5\u5173\u6ce8\u5b83\u7684\u539f\u56e0\u3002\u60a8\u5c06\u4e86\u89e3 ASP.NET Core \u5982\u4f55\u5728\u6574\u4e2a\u5b9e\u65bd\u8fc7\u7a0b\u4e2d\u91c7\u7528 DI\uff0c\u4ee5\u53ca\u4e3a\u4ec0\u4e48\u5728\u7f16\u5199\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u65f6\u4e5f\u5e94\u8be5\u8fd9\u6837\u505a\u3002\u6700\u540e\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4ece\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684 DI \u68c0\u7d22\u670d\u52a1\u3002<\/p>\n<p>When you finish this chapter, you\u2019ll have a solid understanding of the DI concept. In chapter 9 you\u2019ll see how to apply DI to your own classes. You\u2019ll learn how to configure your app so that the ASP.NET Core framework can create your classes for you, removing the pain of having to create new objects in your code manually. You\u2019ll learn how to control how long your objects are used and some of the pitfalls to be aware of when you write your own applications. In chapter 31 we\u2019ll look at some advanced ways to use DI, including how to wire up a third-party DI container.<\/p>\n<p>\u5b8c\u6210\u672c\u7ae0\u540e\uff0c\u60a8\u5c06\u5bf9 DI \u6982\u5ff5\u6709\u6df1\u5165\u7684\u7406\u89e3\u3002\u5728\u7b2c 9 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u770b\u5230\u5982\u4f55\u5c06 DI \u5e94\u7528\u4e8e\u60a8\u81ea\u5df1\u7684\u7c7b\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u914d\u7f6e\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\uff0c\u4ee5\u4fbf ASP.NET Core \u6846\u67b6\u53ef\u4ee5\u4e3a\u60a8\u521b\u5efa\u7c7b\uff0c\u4ece\u800c\u6d88\u9664\u5fc5\u987b\u5728\u4ee3\u7801\u4e2d\u624b\u52a8\u521b\u5efa\u65b0\u5bf9\u8c61\u7684\u75db\u82e6\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u63a7\u5236\u5bf9\u8c61\u7684\u4f7f\u7528\u65f6\u95f4\uff0c\u4ee5\u53ca\u5728\u7f16\u5199\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u65f6\u9700\u8981\u6ce8\u610f\u7684\u4e00\u4e9b\u9677\u9631\u3002\u5728\u7b2c 31 \u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u4e00\u4e9b\u4f7f\u7528 DI \u7684\u9ad8\u7ea7\u65b9\u6cd5\uff0c\u5305\u62ec\u5982\u4f55\u8fde\u63a5\u7b2c\u4e09\u65b9 DI \u5bb9\u5668\u3002<\/p>\n<p>For now, though, let\u2019s get back to basics. What is DI, and why should you care about it?<\/p>\n<p>\u4e0d\u8fc7\uff0c\u73b0\u5728\u8ba9\u6211\u4eec\u56de\u5230\u57fa\u7840\u3002\u4ec0\u4e48\u662f DI\uff0c\u4e3a\u4ec0\u4e48\u8981\u5173\u5fc3\u5b83\uff1f<\/p>\n<h2>8.1 Understanding the benefits of dependency injection<\/h2>\n<h2>8.1 \u4e86\u89e3\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165\u7684\u597d\u5904<\/h2>\n<p>This section aims to give you a basic understanding of what DI is and why you should care about it. The topic itself extends far beyond the reach of this single chapter. If you want a deeper background, I highly recommend checking out Martin Fowler\u2019s articles online. This article from 2004, for example, is a classic: <a href=\"http:\/\/mng.bz\/pPJ8\">http:\/\/mng.bz\/pPJ8<\/a>.<\/p>\n<p>\u672c\u8282\u65e8\u5728\u8ba9\u60a8\u5bf9 DI \u662f\u4ec0\u4e48\u4ee5\u53ca\u4e3a\u4ec0\u4e48\u60a8\u5e94\u8be5\u5173\u5fc3\u5b83\u6709\u4e00\u4e2a\u57fa\u672c\u7684\u4e86\u89e3\u3002\u8fd9\u4e2a\u8bdd\u9898\u672c\u8eab\u8fdc\u8fdc\u8d85\u51fa\u4e86\u8fd9\u4e00\u7ae0\u7684\u8303\u56f4\u3002\u5982\u679c\u60a8\u60f3\u8981\u66f4\u6df1\u5165\u7684\u80cc\u666f\u77e5\u8bc6\uff0c\u6211\u5f3a\u70c8\u5efa\u8bae\u60a8\u5728\u7ebf\u67e5\u770b Martin Fowler \u7684\u6587\u7ae0\u3002\u4f8b\u5982\uff0c2004 \u5e74\u7684\u8fd9\u7bc7\u6587\u7ae0\u5c31\u662f\u7ecf\u5178\u4e4b\u4f5c\uff1a<a href=\"http:\/\/mng.bz\/pPJ8\">http:\/\/mng.bz\/pPJ8<\/a>\u3002<\/p>\n<p><strong>Tip<\/strong> For a more directly applicable read with many examples in C#, I recommend picking up Dependency Injection Principles, Practices, and Patterns, by Steven van Deursen and Mark Seemann (Manning, 2019).<br \/>\n<strong>\u63d0\u793a<\/strong> \u8981\u83b7\u5f97\u66f4\u76f4\u63a5\u9002\u7528\u7684 C# \u4e2d\u8bb8\u591a\u793a\u4f8b\uff0c\u6211\u5efa\u8bae\u60a8\u9605\u8bfb Steven van Deursen \u548c Mark Seemann \u7f16\u5199\u7684 Dependency Injection Principles\uff0c Practices\uff0c and Patterns\uff08Manning\uff0c2019 \u5e74\uff09\u3002<\/p>\n<p>The ASP.NET Core framework has been designed from the ground up to be modular and to adhere to good software engineering practices. As with anything in software, what is considered to be best practice varies over time, but for object-oriented programming, the SOLID principles have held up well.<\/p>\n<p>ASP.NET Core \u6846\u67b6\u662f\u4ece\u5934\u5f00\u59cb\u8bbe\u8ba1\u7684\uff0c\u91c7\u7528\u6a21\u5757\u5316\u8bbe\u8ba1\uff0c\u5e76\u9075\u5faa\u826f\u597d\u7684\u8f6f\u4ef6\u5de5\u7a0b\u5b9e\u8df5\u3002\u4e0e\u8f6f\u4ef6\u4e2d\u7684\u4efb\u4f55\u4e8b\u7269\u4e00\u6837\uff0c\u88ab\u8ba4\u4e3a\u662f\u6700\u4f73\u5b9e\u8df5\u7684\u5185\u5bb9\u4f1a\u968f\u7740\u65f6\u95f4\u7684\u63a8\u79fb\u800c\u53d8\u5316\uff0c\u4f46\u5bf9\u4e8e\u9762\u5411\u5bf9\u8c61\u7684\u7f16\u7a0b\uff0cSOLID \u539f\u5219\u4e00\u76f4\u4fdd\u6301\u5f97\u5f88\u597d\u3002<\/p>\n<p><strong>Definition<\/strong> SOLID is a mnemonic for \u201csingle responsibility principle, open-closed, Liskov substitution, interface segregation, and dependency inversion.\u201d This course by Steve Smith introduces the principles using C#: <a href=\"http:\/\/mng.bz\/Ox1R\">http:\/\/mng.bz\/Ox1R<\/a>.<br \/>\n<strong>\u5b9a\u4e49<\/strong> SOLID \u662f\u201c\u5355\u4e00\u8d23\u4efb\u539f\u5219\u3001\u5f00\u95ed\u3001Liskov \u66ff\u6362\u3001\u63a5\u53e3\u5206\u79bb\u548c\u4f9d\u8d56\u5173\u7cfb\u5012\u7f6e\u201d\u7684\u52a9\u8bb0\u8bcd\u3002Steve Smith \u7684\u8fd9\u95e8\u8bfe\u7a0b\u4ecb\u7ecd\u4e86\u4f7f\u7528 C# \u7684\u539f\u5219\uff1a<a href=\"http:\/\/mng.bz\/Ox1R\">http:\/\/mng.bz\/Ox1R<\/a>\u3002<\/p>\n<p>On that basis, ASP.NET Core has DI (sometimes called dependency inversion or inversion of control [IoC]) baked into the heart of the framework. Regardless of whether you want to use DI within your own application code, the framework libraries themselves depend on it as a concept.<\/p>\n<p>\u5728\u6b64\u57fa\u7840\u4e0a\uff0cASP.NET Core \u5c06 DI\uff08\u6709\u65f6\u79f0\u4e3a\u4f9d\u8d56\u5173\u7cfb\u53cd\u8f6c\u6216\u63a7\u5236\u53cd\u8f6c [IoC]\uff09\u878d\u5165\u5230\u6846\u67b6\u7684\u6838\u5fc3\u4e2d\u3002\u65e0\u8bba\u60a8\u662f\u5426\u60f3\u5728\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u4e2d\u4f7f\u7528 DI\uff0c\u6846\u67b6\u5e93\u672c\u8eab\u90fd\u5c06\u5176\u4f5c\u4e3a\u4e00\u4e2a\u6982\u5ff5\u3002<\/p>\n<p><strong>Note<\/strong> Although related, dependency injection and dependency inversion are two different things. I cover both in a general sense in this chapter, but for a good explanation of the differences, see this post by Derick Bailey, titled \u201cDependency Injection Is NOT the Same As the Dependency Inversion Principle\u201d: <a href=\"http:\/\/mng.bz\/5jvB\">http:\/\/mng.bz\/5jvB<\/a>.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5c3d\u7ba1\u76f8\u5173\uff0c\u4f46\u4f9d\u8d56\u6ce8\u5165\u548c\u4f9d\u8d56\u5012\u7f6e\u662f\u4e24\u4e2a\u4e0d\u540c\u7684\u4e1c\u897f\u3002\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u5c06\u4ece\u4e00\u822c\u610f\u4e49\u4e0a\u4ecb\u7ecd\u4e24\u8005\uff0c\u4f46\u8981\u5f88\u597d\u5730\u89e3\u91ca\u8fd9\u4e9b\u5dee\u5f02\uff0c\u8bf7\u53c2\u9605 Derick Bailey \u7684\u8fd9\u7bc7\u6587\u7ae0\uff0c\u6807\u9898\u4e3a\u201c\u4f9d\u8d56\u6ce8\u5165\u4e0e\u4f9d\u8d56\u5012\u7f6e\u539f\u5219\u4e0d\u540c\u201d\uff1a<a href=\"http:\/\/mng.bz\/5jvB\">http:\/\/mng.bz\/5jvB<\/a>\u3002<\/p>\n<p>When you started programming, chances are that you didn\u2019t use a DI framework immediately. That\u2019s not surprising or even a bad thing; DI adds a certain amount of extra wiring that\u2019s often not warranted in simple applications or when you\u2019re getting started. But when things start to get more complex, DI comes into its own as a great tool to help keep that complexity under control.<\/p>\n<p>\u5f53\u60a8\u5f00\u59cb\u7f16\u7a0b\u65f6\uff0c\u60a8\u5f88\u53ef\u80fd\u6ca1\u6709\u7acb\u5373\u4f7f\u7528 DI \u6846\u67b6\u3002\u8fd9\u5e76\u4e0d\u5947\u602a\uff0c\u751a\u81f3\u4e0d\u662f\u4e00\u4ef6\u574f\u4e8b;DI \u6dfb\u52a0\u4e86\u4e00\u5b9a\u6570\u91cf\u7684\u989d\u5916\u5e03\u7ebf\uff0c\u8fd9\u4e9b\u5e03\u7ebf\u5728\u7b80\u5355\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6216\u60a8\u5f00\u59cb\u65f6\u901a\u5e38\u662f\u4e0d\u9700\u8981\u7684\u3002\u4f46\u662f\u5f53\u4e8b\u60c5\u5f00\u59cb\u53d8\u5f97\u66f4\u52a0\u590d\u6742\u65f6\uff0cDI \u5c31\u4f1a\u6210\u4e3a\u5e2e\u52a9\u63a7\u5236\u8fd9\u79cd\u590d\u6742\u6027\u7684\u7edd\u4f73\u5de5\u5177\u3002<\/p>\n<p>Let\u2019s consider a simple example, written without any sort of DI. Suppose that a user has registered on your web app, and you want to send them an email. This listing shows how you might approach this task initially, using a minimal API endpoint handler.<\/p>\n<p>\u8ba9\u6211\u4eec\u8003\u8651\u4e00\u4e2a\u7b80\u5355\u7684\u4f8b\u5b50\uff0c\u5b83\u6ca1\u6709\u4efb\u4f55\u7c7b\u578b\u7684 DI \u7f16\u5199\u3002\u5047\u8bbe\u7528\u6237\u5df2\u5728\u60a8\u7684 Web \u5e94\u7528\u7a0b\u5e8f\u4e0a\u6ce8\u518c\uff0c\u5e76\u4e14\u60a8\u5e0c\u671b\u5411\u4ed6\u4eec\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002\u6b64\u6e05\u5355\u663e\u793a\u4e86\u6700\u521d\u5982\u4f55\u4f7f\u7528\u6700\u5c0f\u7684 API \u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u6765\u5904\u7406\u6b64\u4efb\u52a1\u3002<\/p>\n<p>Listing 8.1 Sending an email without DI when there are no dependencies<br \/>\n\u6e05\u5355 8.1 \u5728\u6ca1\u6709\u4f9d\u8d56\u9879\u7684\u60c5\u51b5\u4e0b\u53d1\u9001\u6ca1\u6709 DI \u7684\u90ae\u4ef6<\/p>\n<pre><code>var builder = WebApplication.CreateBuilder(args);\nvar app = builder.Build();\n\napp.MapGet(&quot;\/register\/{username}&quot;, RegisterUser); \u2776\n\napp.Run();\n\nstring RegisterUser(string username) \u2777\n{\n    var emailSender = new EmailSender(); \u2778\n    emailSender.SendEmail(username); \u2779\n    return $&quot;Email sent to {username}!&quot;;\n}\n<\/code><\/pre>\n<p>\u2776 The endpoint is called when a new user is created.<br \/>\n\u521b\u5efa\u65b0\u7528\u6237\u65f6\u8c03\u7528 endpoint\u3002<\/p>\n<p>\u2777 The RegisterUser function is the handler for the endpoint.<br \/>\nRegisterUser \u51fd\u6570\u662f\u7aef\u70b9\u7684\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>\u2778 Creates a new instance of EmailSender<br \/>\n\u521b\u5efa EmailSender \u7684\u65b0\u5b9e\u4f8b<\/p>\n<p>\u2779 Uses the new instance to send the email<br \/>\n\u4f7f\u7528\u65b0\u5b9e\u4f8b\u53d1\u9001\u7535\u5b50\u90ae\u4ef6<\/p>\n<p>In this example, the RegisterUser handler executes when a new user registers on your app, creating a new instance of an EmailSender class and calling SendEmail() to send the email. The EmailSender class is the class that actually sends the email. For the purposes of this example, you can imagine that it looks something like this:<\/p>\n<p>\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u5f53\u65b0\u7528\u6237\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e0a\u6ce8\u518c\u65f6\uff0c\u5c06\u6267\u884c\u5904\u7406\u7a0b\u5e8f\uff0c\u521b\u5efa\u7c7b\u7684\u65b0\u5b9e\u4f8b\u5e76\u8c03\u7528\u4ee5\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002\u7c7b\u662f\u5b9e\u9645\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u7684\u7c7b\u3002\u5bf9\u4e8e\u6b64\u793a\u4f8b\uff0c\u60a8\u53ef\u4ee5\u60f3\u8c61\u5b83\u770b\u8d77\u6765\u50cf\u8fd9\u6837\uff1a<\/p>\n<pre><code>public class EmailSender\n{\n    public void SendEmail(string username)\n    {\n        Console.WriteLine($&quot;Email sent to {username}!&quot;);\n    }\n}<\/code><\/pre>\n<p>Console.WriteLine stands in here for the real process of sending the email.<\/p>\n<p>Console.WriteLine \u5728\u6b64\u5904\u4ee3\u66ff\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u7684\u771f\u5b9e\u8fc7\u7a0b\u3002<\/p>\n<p><strong>Note<\/strong> Although I\u2019m using sending email as a simple example, in practice you may want to move this code out of your handler method. This type of asynchronous task is well suited to using message queues and a background process. For more details, see <a href=\"http:\/\/mng.bz\/Y1AB\">http:\/\/mng.bz\/Y1AB<\/a>.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u867d\u7136\u6211\u4f7f\u7528\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u4f5c\u4e3a\u4e00\u4e2a\u7b80\u5355\u7684\u793a\u4f8b\uff0c\u4f46\u5b9e\u9645\u4e0a\u60a8\u53ef\u80fd\u5e0c\u671b\u5c06\u6b64\u4ee3\u7801\u4ece handler \u65b9\u6cd5\u4e2d\u79fb\u51fa\u3002\u8fd9\u79cd\u7c7b\u578b\u7684\u5f02\u6b65\u4efb\u52a1\u975e\u5e38\u9002\u5408\u4f7f\u7528\u6d88\u606f\u961f\u5217\u548c\u540e\u53f0\u8fdb\u7a0b\u3002\u6709\u5173\u66f4\u591a\u8be6\u7ec6\u4fe1\u606f\uff0c\u8bf7\u53c2\u9605 <a href=\"http:\/\/mng.bz\/Y1AB\">http:\/\/mng.bz\/Y1AB<\/a>\u3002<\/p>\n<p>If the EmailSender class is as simple as the previous example and has no dependencies, you may not see any need to adopt a different approach to creating objects. And to an extent, you\u2019d be right. But what if you later update your implementation of EmailSender so that some of the email-sending logic is implemented by a different class?<\/p>\n<p>\u5982\u679c EmailSender \u7c7b\u4e0e\u4e0a\u4e00\u4e2a\u793a\u4f8b\u4e00\u6837\u7b80\u5355\uff0c\u5e76\u4e14\u6ca1\u6709\u4f9d\u8d56\u9879\uff0c\u5219\u60a8\u53ef\u80fd\u4e0d\u9700\u8981\u91c7\u7528\u5176\u4ed6\u65b9\u6cd5\u6765\u521b\u5efa\u5bf9\u8c61\u3002\u5728\u67d0\u79cd\u7a0b\u5ea6\u4e0a\uff0c\u4f60\u662f\u5bf9\u7684\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u7a0d\u540e\u66f4\u65b0 EmailSender \u7684\u5b9e\u73b0\uff0c\u4ee5\u4fbf\u67d0\u4e9b\u7535\u5b50\u90ae\u4ef6\u53d1\u9001\u903b\u8f91\u7531\u4e0d\u540c\u7684\u7c7b\u5b9e\u73b0\uff0c\u8be5\u600e\u4e48\u529e\uff1f<\/p>\n<p>Currently, EmailSender would need to do many things to send an email. It would need to<\/p>\n<p>\u76ee\u524d\uff0cEmailSender \u9700\u8981\u505a\u5f88\u591a\u4e8b\u60c5\u6765\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002\u5b83\u9700\u8981<\/p>\n<ul>\n<li>\n<p>Create an email message.<br \/>\n\u521b\u5efa\u7535\u5b50\u90ae\u4ef6\u3002<\/p>\n<\/li>\n<li>\n<p>Configure the settings of the email server.<br \/>\n\u914d\u7f6e\u7535\u5b50\u90ae\u4ef6\u670d\u52a1\u5668\u7684\u8bbe\u7f6e\u3002<\/p>\n<\/li>\n<li>\n<p>Send the email to the email server.<br \/>\n\u5c06\u7535\u5b50\u90ae\u4ef6\u53d1\u9001\u5230\u7535\u5b50\u90ae\u4ef6\u670d\u52a1\u5668\u3002<\/p>\n<\/li>\n<\/ul>\n<p>Doing all that in one class would go against the single-responsibility principle (SRP), so you\u2019d likely end up with EmailSender depending on other services. Figure 8.1 shows how this web of dependencies might look. RegisterUser wants to send an email using EmailSender, but to do so, it also needs to create the MessageFactory, NetworkClient, and EmailServerSettings objects that EmailSender depends on.<br \/>\n\u5728\u4e00\u4e2a\u7c7b\u4e2d\u5b8c\u6210\u6240\u6709\u8fd9\u4e9b\u4f5c\u5c06\u8fdd\u53cd\u5355\u4e00\u8d23\u4efb\u539f\u5219 \uff08SRP\uff09\uff0c\u56e0\u6b64\u60a8\u6700\u7ec8\u53ef\u80fd\u4f1a\u4f7f\u7528 EmailSender\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u5176\u4ed6\u670d\u52a1\u3002\u56fe 8.1 \u663e\u793a\u4e86\u8fd9\u4e2a\u4f9d\u8d56\u5173\u7cfb\u7f51\u7edc\u53ef\u80fd\u662f\u4ec0\u4e48\u6837\u5b50\u3002RegisterUser \u5e0c\u671b\u4f7f\u7528 EmailSender \u53d1\u9001\u7535\u5b50\u90ae\u4ef6\uff0c\u4f46\u4e3a\u6b64\uff0c\u5b83\u8fd8\u9700\u8981\u521b\u5efa EmailSender \u6240\u4f9d\u8d56\u7684 MessageFactory\u3001NetworkClient \u548c EmailServerSettings \u5bf9\u8c61\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0801.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 8.1 Dependency diagram without dependency injection. RegisterUser indirectly depends on all the other classes, so it must create them all.<br \/>\n\u56fe 8.1 \u6ca1\u6709\u4f9d\u8d56\u6ce8\u5165\u7684\u4f9d\u8d56\u56fe\u3002RegisterUser \u95f4\u63a5\u4f9d\u8d56\u4e8e\u6240\u6709\u5176\u4ed6\u7c7b\uff0c\u56e0\u6b64\u5b83\u5fc5\u987b\u521b\u5efa\u6240\u6709\u7c7b\u3002<\/p>\n<p>Each class has several dependencies, so the \u201croot\u201d caller\u2014in this case, the RegisterUser handler\u2014needs to know how to create every class it depends on, as well as every class its dependencies depend on. This is sometimes called the dependency graph.<\/p>\n<p>\u6bcf\u4e2a\u7c7b\u90fd\u6709\u591a\u4e2a\u4f9d\u8d56\u9879\uff0c\u56e0\u6b64\u201c\u6839\u201d\u8c03\u7528\u65b9\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a RegisterUser \u5904\u7406\u7a0b\u5e8f\uff09\u9700\u8981\u77e5\u9053\u5982\u4f55\u4f5c\u521b\u5efa\u5b83\u6240\u4f9d\u8d56\u7684\u6bcf\u4e2a\u7c7b\uff0c\u4ee5\u53ca\u5b83\u7684\u4f9d\u8d56\u9879\u6240\u4f9d\u8d56\u7684\u6bcf\u4e2a\u7c7b\u3002\u8fd9\u6709\u65f6\u79f0\u4e3a\u4f9d\u8d56\u5173\u7cfb\u56fe\u3002<\/p>\n<p><strong>Definition<\/strong> The dependency graph is the set of objects that must be created to create a specific requested \u201croot\u201d object.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u4f9d\u8d56\u5173\u7cfb\u56fe\u662f\u521b\u5efa\u7279\u5b9a\u8bf7\u6c42\u7684 \u201croot\u201d \u5bf9\u8c61\u65f6\u5fc5\u987b\u521b\u5efa\u7684\u5bf9\u8c61\u96c6\u3002<\/p>\n<p>EmailSender depends on the MessageFactory and NetworkClient objects, so they\u2019re provided via the constructor, as shown in the following listing.<\/p>\n<p>EmailSender \u4f9d\u8d56\u4e8e MessageFactory \u548c NetworkClient \u5bf9\u8c61\uff0c\u56e0\u6b64\u5b83\u4eec\u662f\u901a\u8fc7\u6784\u9020\u51fd\u6570\u63d0\u4f9b\u7684\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 8.2 A service with multiple dependencies<br \/>\n\u6e05\u5355 8.2 \u5177\u6709\u591a\u4e2a\u4f9d\u8d56\u9879\u7684\u670d\u52a1<\/p>\n<pre><code>public class EmailSender\n{\n    private readonly NetworkClient _client; \u2776\n    private readonly MessageFactory _factory; \u2776\n    public EmailSender(MessageFactory factory, NetworkClient client) \u2777\n    { \u2777\n        _factory = factory; \u2777\n        _client = client; \u2777\n    } \u2777\n    public void SendEmail(string username)\n    {\n        var email = _factory.Create(username); \u2778\n        _client.SendEmail(email); \u2778\n        Console.WriteLine($&quot;Email sent to {username}!&quot;);\n    }\n}<\/code><\/pre>\n<p>\u2776 Now the EmailSender depends on two other classes.<br \/>\n\u73b0\u5728 EmailSender \u4f9d\u8d56\u4e8e\u5176\u4ed6\u4e24\u4e2a\u7c7b\u3002<\/p>\n<p>\u2777 Instances of the dependencies are provided in the constructor.<br \/>\n\u6784\u9020\u51fd\u6570\u4e2d\u63d0\u4f9b\u4e86\u4f9d\u8d56\u9879\u7684\u5b9e\u4f8b\u3002<\/p>\n<p>\u2778 The EmailSender coordinates the dependencies to create and send an email.<br \/>\nEmailSender \u534f\u8c03\u4f9d\u8d56\u9879\u4ee5\u521b\u5efa\u548c\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002<\/p>\n<p>On top of that, the NetworkClient class that EmailSender depends on also has a dependency on an EmailServerSettings object:<br \/>\n\u6700\u91cd\u8981\u7684\u662f\uff0cNetworkClient \u7c7bEmailSender \u4e5f\u4f9d\u8d56\u4e8eEmailServerSettings \u5bf9\u8c61\uff1a<\/p>\n<pre><code>public class NetworkClient\n{\n    private readonly EmailServerSettings _settings;\n    public NetworkClient(EmailServerSettings settings)\n    {\n        _settings = settings;\n    }\n}<\/code><\/pre>\n<p>This example might feel a little contrived, but it\u2019s common to find this sort of chain of dependencies. In fact, if you don\u2019t have it in your code, it\u2019s probably a sign that your classes are too big and aren\u2019t following the SRP.<\/p>\n<p>\u8fd9\u4e2a\u4f8b\u5b50\u53ef\u80fd\u611f\u89c9\u6709\u70b9\u505a\u4f5c\uff0c\u4f46\u627e\u5230\u8fd9\u79cd\u4f9d\u8d56\u5173\u7cfb\u94fe\u662f\u5f88\u5e38\u89c1\u7684\u3002\u4e8b\u5b9e\u4e0a\uff0c\u5982\u679c\u60a8\u7684\u4ee3\u7801\u4e2d\u6ca1\u6709\u5b83\uff0c\u5219\u53ef\u80fd\u8868\u660e\u60a8\u7684\u7c7b\u592a\u5927\u5e76\u4e14\u6ca1\u6709\u9075\u5faa SRP\u3002<\/p>\n<p>So how does this affect the code in RegisterUser? The following listing shows how you now have to send an email if you stick to newing up objects in the handler.<\/p>\n<p>\u90a3\u4e48\u8fd9\u5bf9 RegisterUser \u4e2d\u7684\u4ee3\u7801\u6709\u4ec0\u4e48\u5f71\u54cd\u5462\uff1f\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u679c\u4f60\u575a\u6301\u5728\u5904\u7406\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u65b0\u7684ing \u5bf9\u8c61\uff0c\u4f60\u73b0\u5728\u5fc5\u987b\u5982\u4f55\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002<\/p>\n<p>Listing 8.3 Sending email without DI when you create dependencies manually<br \/>\n\u6e05\u5355 8.3 \u624b\u52a8\u521b\u5efa\u4f9d\u8d56\u9879\u65f6\u53d1\u9001\u6ca1\u6709 DI \u7684\u90ae\u4ef6<\/p>\n<pre><code>string RegisterUser(string username)\n{\n    var emailSender = new EmailSender( \u2776\n        new MessageFactory(), \u2777\n        new NetworkClient( \u2778\n            new EmailServerSettings \u2779\n            ( \u2779\n                Host: &quot;smtp.server.com&quot;, \u2779\n                Port: 25 \u2779\n            )) \u2779\n        );\n    emailSender.SendEmail(username); \u277a\n    return $&quot;Email sent to {username}!&quot;;\n}<\/code><\/pre>\n<p>\u2776 To create EmailSender, you must create all its dependencies.<br \/>\n\u8981\u521b\u5efa EmailSender\uff0c\u60a8\u5fc5\u987b\u521b\u5efa\u5176\u6240\u6709\u4f9d\u8d56\u9879\u3002<\/p>\n<p>\u2777 You need a new MessageFactory.<br \/>\n\u60a8\u9700\u8981\u4e00\u4e2a\u65b0\u7684 MessageFactory\u3002<\/p>\n<p>\u2778 The NetworkClient also has dependencies.<br \/>\nNetworkClient \u4e5f\u6709\u4f9d\u8d56\u9879\u3002<\/p>\n<p>\u2779 You\u2019re already two layers deep, but there could feasibly be more.<br \/>\n\u60a8\u5df2\u7ecf\u6709\u4e24\u5c42\u4e86\uff0c\u4f46\u53ef\u80fd\u8fd8\u6709\u66f4\u591a\u3002<\/p>\n<p>\u277a Finally, you can send the email.<br \/>\n\u6700\u540e\uff0c\u60a8\u53ef\u4ee5\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002<\/p>\n<p>This code is turning into something gnarly. Improving the design of EmailSender to separate out the responsibilities has made calling it from RegisterUser a real chore. This code has several problems:<\/p>\n<p>\u8fd9\u6bb5\u4ee3\u7801\u6b63\u5728\u53d8\u6210\u4e00\u4e9b\u7c97\u7cd9\u7684\u4e1c\u897f\u3002\u6539\u8fdb EmailSender \u7684\u8bbe\u8ba1\u4ee5\u5206\u79bb\u804c\u8d23\uff0c\u8fd9\u4f7f\u5f97\u4ece RegisterUser \u8c03\u7528\u5b83\u6210\u4e3a\u4e00\u4ef6\u771f\u6b63\u7684\u82e6\u5dee\u4e8b\u3002\u6b64\u4ee3\u7801\u6709\u51e0\u4e2a\u95ee\u9898\uff1a<\/p>\n<ul>\n<li>\n<p>Not obeying the SRP\u2014Now our code is responsible for both creating an EmailSender object and using it to send an email.<br \/>\n\u4e0d\u9075\u5b88 SRP \u2013 \u73b0\u5728\uff0c\u6211\u4eec\u7684\u4ee3\u7801\u8d1f\u8d23\u521b\u5efa EmailSender \u5bf9\u8c61\u5e76\u4f7f\u7528\u5b83\u6765\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002<\/p>\n<\/li>\n<li>\n<p>Considerable ceremony\u2014Ceremony refers to code that you have to write but that isn\u2019t adding value directly. Of the 11 lines of code in the RegisterUser method, only the last two are doing anything useful, which makes it harder to read and harder to understand the intent of the methods.<br \/>\n\u76f8\u5f53\u5927\u7684\u4eea\u5f0f \u2014 \u4eea\u5f0f\u662f\u6307\u60a8\u5fc5\u987b\u7f16\u5199\u7684\u4ee3\u7801\uff0c\u4f46\u8fd9\u4e0d\u4f1a\u76f4\u63a5\u589e\u52a0\u4ef7\u503c\u3002\u5728 RegisterUser \u65b9\u6cd5\u7684 11 \u884c\u4ee3\u7801\u4e2d\uff0c\u53ea\u6709\u6700\u540e\u4e24\u884c\u4ee3\u7801\u6267\u884c\u4efb\u4f55\u6709\u7528\u7684\u4f5c\uff0c\u8fd9\u4f7f\u5f97\u5b83\u66f4\u96be\u9605\u8bfb\uff0c\u4e5f\u66f4\u96be\u7406\u89e3\u65b9\u6cd5\u7684\u610f\u56fe\u3002<\/p>\n<\/li>\n<li>\n<p>Tied to the implementation\u2014If you decide to refactor EmailSender and add another dependency, you\u2019d need to update every place it\u2019s used. Likewise, if any dependencies are refactored, you would need to update this code too.<br \/>\n\u4e0e\u5b9e\u73b0\u7ed1\u5b9a \u2014 \u5982\u679c\u60a8\u51b3\u5b9a\u91cd\u6784 EmailSender \u5e76\u6dfb\u52a0\u53e6\u4e00\u4e2a\u4f9d\u8d56\u9879\uff0c\u5219\u9700\u8981\u66f4\u65b0\u4f7f\u7528\u5b83\u7684\u6bcf\u4e2a\u4f4d\u7f6e\u3002\u540c\u6837\uff0c\u5982\u679c\u91cd\u6784\u4e86\u4efb\u4f55\u4f9d\u8d56\u9879\uff0c\u60a8\u4e5f\u9700\u8981\u66f4\u65b0\u6b64\u4ee3\u7801\u3002<\/p>\n<\/li>\n<li>\n<p>Hard to reuse instance\u2014In the example code we created new instances of all the objects. But what if creating a new NetworkClient is computationally expensive and we\u2019d like to reuse instances? We\u2019d have to add extra code to handle that task, further increasing the amount of boilerplate code.<br \/>\n\u96be\u4ee5\u91cd\u7528\u5b9e\u4f8b \u2013 \u5728\u793a\u4f8b\u4ee3\u7801\u4e2d\uff0c\u6211\u4eec\u521b\u5efa\u4e86\u6240\u6709\u5bf9\u8c61\u7684\u65b0\u5b9e\u4f8b\u3002\u4f46\u662f\uff0c\u5982\u679c\u521b\u5efa\u65b0\u7684 NetworkClient \u8ba1\u7b97\u6210\u672c\u5f88\u9ad8\uff0c\u5e76\u4e14\u6211\u4eec\u60f3\u91cd\u7528\u5b9e\u4f8b\u600e\u4e48\u529e\uff1f\u6211\u4eec\u5fc5\u987b\u6dfb\u52a0\u989d\u5916\u7684\u4ee3\u7801\u6765\u5904\u7406\u8be5\u4efb\u52a1\uff0c\u4ece\u800c\u8fdb\u4e00\u6b65\u589e\u52a0\u6837\u677f\u4ee3\u7801\u7684\u6570\u91cf\u3002<\/p>\n<\/li>\n<\/ul>\n<p>RegisterUser has an implicit dependency on the EmailSender class, as it creates the object manually itself. The only way to know that RegisterUser uses EmailSender is to look at its source code. By contrast, EmailSender has explicit dependencies on NetworkClient and MessageFactory, which must be provided in the constructor. Similarly, NetworkClient has an explicit dependency on the EmailServerSettings class.<\/p>\n<p>RegisterUser \u5bf9 EmailSender \u7c7b\u5177\u6709\u9690\u5f0f\u4f9d\u8d56\u5173\u7cfb\uff0c\u56e0\u4e3a\u5b83\u81ea\u5df1\u624b\u52a8\u521b\u5efa\u5bf9\u8c61\u3002\u8981\u77e5\u9053 RegisterUser \u4f7f\u7528 EmailSender\uff0c\u552f\u4e00\u7684\u65b9\u6cd5\u662f\u67e5\u770b\u5176\u6e90\u4ee3\u7801\u3002\u76f8\u6bd4\u4e4b\u4e0b\uff0cEmailSender \u5bf9 NetworkClient \u548c MessageFactory \u5177\u6709\u663e\u5f0f\u4f9d\u8d56\u9879\uff0c\u5fc5\u987b\u5728\u6784\u9020\u51fd\u6570\u4e2d\u63d0\u4f9b\u3002\u540c\u6837\uff0cNetworkClient \u5bf9 EmailServerSettings \u7c7b\u5177\u6709\u663e\u5f0f\u4f9d\u8d56\u9879\u3002<\/p>\n<p><strong>Tip<\/strong> Generally speaking, any dependencies in your code should be explicit, not implicit. Implicit dependencies are hard to reason about and difficult to test, so you should avoid them wherever you can. DI is useful for guiding you along this path.<br \/>\n<strong>\u63d0\u793a<\/strong> \u4e00\u822c\u6765\u8bf4\uff0c\u4ee3\u7801\u4e2d\u7684\u4efb\u4f55\u4f9d\u8d56\u9879\u90fd\u5e94\u8be5\u662f\u663e\u5f0f\u7684\uff0c\u800c\u4e0d\u662f\u9690\u5f0f\u7684\u3002\u9690\u5f0f\u4f9d\u8d56\u5173\u7cfb\u5f88\u96be\u63a8\u7406\u4e14\u96be\u4ee5\u6d4b\u8bd5\uff0c\u56e0\u6b64\u60a8\u5e94\u8be5\u5c3d\u53ef\u80fd\u907f\u514d\u4f7f\u7528\u5b83\u4eec\u3002DI \u6709\u52a9\u4e8e\u6307\u5bfc\u60a8\u6cbf\u7740\u8fd9\u6761\u9053\u8def\u524d\u8fdb\u3002<\/p>\n<p>DI aims to solve the problem of building a dependency graph by inverting the chain of dependencies. Instead of the RegisterUser handler creating its dependencies manually, deep inside the implementation details of the code, an already-created instance of EmailSender is passed as an argument to the RegisterUser method.<\/p>\n<p>DI \u65e8\u5728\u901a\u8fc7\u53cd\u8f6c\u4f9d\u8d56\u5173\u7cfb\u94fe\u6765\u89e3\u51b3\u6784\u5efa\u4f9d\u8d56\u5173\u7cfb\u56fe\u7684\u95ee\u9898\u3002RegisterUser \u5904\u7406\u7a0b\u5e8f\u4e0d\u662f\u624b\u52a8\u521b\u5efa\u5176\u4f9d\u8d56\u9879\uff0c\u800c\u662f\u5728\u4ee3\u7801\u7684\u5b9e\u73b0\u7ec6\u8282\u6df1\u5904\uff0c\u5c06\u5df2\u521b\u5efa\u7684 EmailSender \u5b9e\u4f8b\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u7ed9 RegisterUser \u65b9\u6cd5\u3002<\/p>\n<p>Now, obviously something needs to create the object, so the code to do that has to live somewhere. The service responsible for providing the instance is called a DI container or an IoC container, as shown in figure 8.2.<\/p>\n<p>\u73b0\u5728\uff0c\u663e\u7136\u9700\u8981\u521b\u5efa\u5bf9\u8c61\uff0c\u56e0\u6b64\u6267\u884c\u6b64\u4f5c\u7684\u4ee3\u7801\u5fc5\u987b\u4f4d\u4e8e\u67d0\u4e2a\u4f4d\u7f6e\u3002\u8d1f\u8d23\u63d0\u4f9b\u5b9e\u4f8b\u7684\u670d\u52a1\u79f0\u4e3a DI \u5bb9\u5668\u6216 IoC \u5bb9\u5668\uff0c\u5982\u56fe 8.2 \u6240\u793a\u3002<\/p>\n<p><strong>Definition<\/strong> The DI container or IoC container is responsible for creating instances of services. It knows how to construct an instance of a service by creating all its dependencies and passing them to the constructor. I\u2019ll refer to it as a DI container throughout this book.<br \/>\n<strong>\u5b9a\u4e49<\/strong> DI \u5bb9\u5668\u6216 IoC \u5bb9\u5668\u8d1f\u8d23\u521b\u5efa\u670d\u52a1\u5b9e\u4f8b\u3002\u5b83\u77e5\u9053\u5982\u4f55\u901a\u8fc7\u521b\u5efa\u670d\u52a1\u7684\u6240\u6709\u4f9d\u8d56\u9879\u5e76\u5c06\u5b83\u4eec\u4f20\u9012\u7ed9\u6784\u9020\u51fd\u6570\u6765\u6784\u9020\u670d\u52a1\u7684\u5b9e\u4f8b\u3002\u5728\u672c\u4e66\u4e2d\uff0c\u6211\u5c06\u5b83\u79f0\u4e3a DI \u5bb9\u5668\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0802.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 8.2 Dependency diagram using DI . RegisterUser indirectly depends on all the other classes but doesn\u2019t need to know how to create them. The RegisterUser handler declares that it requires EmailSender, and the container provides it.<br \/>\n\u56fe 8.2 \u4f7f\u7528 DI \u7684\u4f9d\u8d56\u5173\u7cfb\u56fe .RegisterUser \u95f4\u63a5\u4f9d\u8d56\u4e8e\u6240\u6709\u5176\u4ed6\u7c7b\uff0c\u4f46\u4e0d\u9700\u8981\u77e5\u9053\u5982\u4f55\u521b\u5efa\u5b83\u4eec\u3002RegisterUser \u5904\u7406\u7a0b\u5e8f\u58f0\u660e\u5b83\u9700\u8981 EmailSender\uff0c\u5e76\u4e14\u5bb9\u5668\u63d0\u4f9b\u5b83\u3002<\/p>\n<p>The term DI is often used interchangeably with IoC. But DI is a specific version of the more general principle of IoC. In the context of ASP.NET Core,<\/p>\n<p>\u672f\u8bed DI \u901a\u5e38\u4e0e IoC \u4e92\u6362\u4f7f\u7528\u3002\u4f46 DI \u662f IoC \u66f4\u666e\u904d\u539f\u5219\u7684\u7279\u5b9a\u7248\u672c\u3002\u5728 ASP.NET Core \u7684\u4e0a\u4e0b\u6587\u4e2d\uff0c<\/p>\n<ul>\n<li>\n<p>Without IoC, you\u2019d write the code to listen for requests, check which handler to invoke, and then invoke it. With IoC, the control flow is the other way around. You register your handlers with the framework, but it\u2019s up to the framework to invoke your handler. Your handler is still responsible for creating its dependencies.<br \/>\n\u5982\u679c\u6ca1\u6709 IoC\uff0c\u60a8\u5c06\u7f16\u5199\u4ee3\u7801\u6765\u4fa6\u542c\u8bf7\u6c42\uff0c\u68c0\u67e5\u8981\u8c03\u7528\u7684\u5904\u7406\u7a0b\u5e8f\uff0c\u7136\u540e\u8c03\u7528\u5b83\u3002\u5bf9\u4e8e IoC\uff0c\u63a7\u5236\u6d41\u6b63\u597d\u76f8\u53cd\u3002\u60a8\u53ef\u4ee5\u5411\u6846\u67b6\u6ce8\u518c\u5904\u7406\u7a0b\u5e8f\uff0c\u4f46\u7531\u6846\u67b6\u6765\u8c03\u7528\u60a8\u7684\u5904\u7406\u7a0b\u5e8f\u3002\u60a8\u7684\u5904\u7406\u7a0b\u5e8f\u4ecd\u8d1f\u8d23\u521b\u5efa\u5176\u4f9d\u8d56\u9879\u3002<\/p>\n<\/li>\n<li>\n<p>DI takes IoC one step further. As well as invoking your handler, with DI, the framework creates all your handler\u2019s dependencies.<br \/>\nDI \u4f7f IoC \u66f4\u8fdb\u4e00\u6b65\u3002\u9664\u4e86\u8c03\u7528\u60a8\u7684\u5904\u7406\u7a0b\u5e8f\u5916\uff0c\u6846\u67b6\u8fd8\u4f7f\u7528 DI \u521b\u5efa\u6240\u6709\u5904\u7406\u7a0b\u5e8f\u7684\u4f9d\u8d56\u9879\u3002<\/p>\n<\/li>\n<\/ul>\n<p>So when you use dependency injection, your RegisterUser handler is no longer responsible for controlling how to create an EmailSender instance. Instead, the framework provides an EmailSender to the handler directly.<br \/>\n\u56e0\u6b64\uff0c\u5f53\u60a8\u4f7f\u7528\u4f9d\u8d56\u9879\u6ce8\u5165\u65f6\uff0c\u60a8\u7684 RegisterUser \u5904\u7406\u7a0b\u5e8f\u4e0d\u518d\u8d1f\u8d23\u63a7\u5236\u5982\u4f55\u521b\u5efa EmailSender \u5b9e\u4f8b\u3002\u76f8\u53cd\uff0c\u6846\u67b6\u76f4\u63a5\u5411\u5904\u7406\u7a0b\u5e8f\u63d0\u4f9b EmailSender\u3002<\/p>\n<p><strong>Note<\/strong> Many DI containers are available for .NET, including Autofac, Lamar, Unity, Ninject, and Simple Injector, and the list goes on! In chapter 31 you\u2019ll see how to replace the default ASP.NET Core container with one of these alternatives.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u8bb8\u591a DI \u5bb9\u5668\u53ef\u7528\u4e8e .NET\uff0c\u5305\u62ec Autofac\u3001Lamar\u3001Unity\u3001Ninject \u548c Simple Injector\uff0c\u4e0d\u80dc\u679a\u4e3e\uff01\u5728\u7b2c 31 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u770b\u5230\u5982\u4f55\u5c06default ASP.NET Core \u5bb9\u5668\u4e2d\u66ff\u6362\u4e3a\u8fd9\u4e9b\u66ff\u4ee3\u9879\u4e4b\u4e00\u3002<\/p>\n<p>The advantage of adopting this pattern becomes apparent when you see how much it simplifies using dependencies. Listing 8.4 shows how the RegisterUser handler would look if you used DI to create EmailSender instead of creating it manually. All the new noise has gone, and you can focus purely on what the endpoint handler is doing: calling EmailSender and returning a string message.<\/p>\n<p>\u5f53\u60a8\u770b\u5230\u91c7\u7528\u6b64\u6a21\u5f0f\u5728\u591a\u5927\u7a0b\u5ea6\u4e0a\u7b80\u5316\u4e86\u4f9d\u8d56\u9879\u7684\u4f7f\u7528\u65f6\uff0c\u91c7\u7528\u6b64\u6a21\u5f0f\u7684\u4f18\u52bf\u5c31\u53d8\u5f97\u663e\u800c\u6613\u89c1\u4e86\u3002\u6e05\u5355 8.4 \u663e\u793a\u4e86\u5982\u679c\u4f7f\u7528 DI \u521b\u5efa EmailSender \u800c\u4e0d\u662f\u624b\u52a8\u521b\u5efa EmailSender\uff0cRegisterUser \u5904\u7406\u7a0b\u5e8f\u4f1a\u662f\u4ec0\u4e48\u6837\u5b50\u3002\u6240\u6709\u65b0\u7684\u5e72\u6270\u90fd\u5df2\u6d88\u5931\uff0c\u60a8\u53ef\u4ee5\u4e13\u6ce8\u4e8e\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u6b63\u5728\u6267\u884c\u7684\u4f5c\uff1a\u8c03\u7528 EmailSender \u5e76\u8fd4\u56de\u5b57\u7b26\u4e32\u6d88\u606f\u3002<\/p>\n<p>Listing 8.4 Sending an email using DI to inject dependencies<br \/>\n\u6e05\u5355 8.4 \u4f7f\u7528 DI \u53d1\u9001\u90ae\u4ef6\u6ce8\u5165\u4f9d\u8d56<\/p>\n<pre><code>string RegisterUser(string username, EmailSender emailSender) \u2776\n{\n    emailSender.SendEmail(username); \u2777\n    return $&quot;Email sent to {username}!&quot;; \u2777\n}\n<\/code><\/pre>\n<p>\u2776 Instead of creating the dependencies implicitly, injects them directly<br \/>\n\u4e0d\u662f\u9690\u5f0f\u521b\u5efa\u4f9d\u8d56\u9879\uff0c\u800c\u662f\u76f4\u63a5\u6ce8\u5165\u5b83\u4eec<\/p>\n<p>\u2777 The handler is easy to read and understand again.<br \/>\n\u5904\u7406\u7a0b\u5e8f\u6613\u4e8e\u9605\u8bfb\u548c\u7406\u89e3\u3002<\/p>\n<p>One advantage of a DI container is that it has a single responsibility: creating objects or services. The minimal API infrastructure asks the DI container for an instance of a service, and the container takes care of figuring out how to create the dependency graph, based on how you configure it.<\/p>\n<p>DI \u5bb9\u5668\u7684\u4e00\u4e2a\u4f18\u70b9\u662f\u5b83\u53ea\u6709\u4e00\u4e2a\u804c\u8d23\uff1a\u521b\u5efa\u5bf9\u8c61\u6216\u670d\u52a1\u3002\u6700\u5c0f\u7684 API \u57fa\u7840\u8bbe\u65bd\u5411 DI \u5bb9\u5668\u8bf7\u6c42\u670d\u52a1\u5b9e\u4f8b\uff0c\u5bb9\u5668\u8d1f\u8d23\u6839\u636e\u60a8\u7684\u914d\u7f6e\u65b9\u5f0f\u786e\u5b9a\u5982\u4f55\u521b\u5efa\u4f9d\u8d56\u5173\u7cfb\u56fe\u3002<\/p>\n<p><strong>Note<\/strong> It\u2019s common to refer to services when talking about DI containers, which is slightly unfortunate, as services is one of the most overloaded terms in software engineering! In this context, a service refers to any class or interface that the DI container creates when required.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5728\u8c08\u8bba DI \u5bb9\u5668\u65f6\uff0c\u901a\u5e38\u4f1a\u63d0\u5230\u670d\u52a1\uff0c\u8fd9\u6709\u70b9\u9057\u61be\uff0c\u56e0\u4e3a\u670d\u52a1\u662f\u8f6f\u4ef6\u5de5\u7a0b\u4e2d\u6700\u8d85\u8f7d\u7684\u672f\u8bed\u4e4b\u4e00\uff01\u5728\u8fd9\u4e2acontext\uff0c\u670d\u52a1\u662f\u6307 DI \u5bb9\u5668\u5728\u9700\u8981\u65f6\u521b\u5efa\u7684\u4efb\u4f55\u7c7b\u6216\u63a5\u53e3\u3002<\/p>\n<p>The beauty of this approach is that by using explicit dependencies, you never have to write the mess of code you saw in listing 8.3. The DI container can inspect your service\u2019s constructor and work out how to write much of the code itself. DI containers are always configurable, so if you want to describe how to create an instance of a service manually, you can, but by default you shouldn\u2019t need to.<\/p>\n<p>\u8fd9\u79cd\u65b9\u6cd5\u7684\u7f8e\u5999\u4e4b\u5904\u5728\u4e8e\uff0c\u901a\u8fc7\u4f7f\u7528\u663e\u5f0f\u4f9d\u8d56\u9879\uff0c\u60a8\u6c38\u8fdc\u4e0d\u5fc5\u7f16\u5199\u6e05\u5355 8.3 \u4e2d\u770b\u5230\u7684\u6df7\u4e71\u4ee3\u7801\u3002DI \u5bb9\u5668\u53ef\u4ee5\u68c0\u67e5\u670d\u52a1\u7684\u6784\u9020\u51fd\u6570\uff0c\u5e76\u627e\u51fa\u5982\u4f55\u7f16\u5199\u5927\u90e8\u5206\u4ee3\u7801\u672c\u8eab\u3002DI \u5bb9\u5668\u59cb\u7ec8\u662f\u53ef\u914d\u7f6e\u7684\uff0c\u56e0\u6b64\u5982\u679c\u60a8\u60f3\u63cf\u8ff0\u5982\u4f55\u624b\u52a8\u521b\u5efa\u670d\u52a1\u5b9e\u4f8b\uff0c\u60a8\u53ef\u4ee5\u8fd9\u6837\u505a\uff0c\u4f46\u9ed8\u8ba4\u60c5\u51b5\u4e0b\u60a8\u4e0d\u9700\u8981\u8fd9\u6837\u505a\u3002<\/p>\n<p><strong>Tip<\/strong> ASP.NET Core supports constructor injection and injection into endpoint handler methods out of the box. Technically, you can inject dependencies into a service in other ways, such as by using property injection, but these techniques aren\u2019t supported by the built-in DI container.<br \/>\n<strong>\u63d0\u793a<\/strong> ASP.NET Core \u652f\u6301\u6784\u9020\u51fd\u6570\u6ce8\u5165\u548c\u5f00\u7bb1\u5373\u7528\u7684\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u65b9\u6cd5\u6ce8\u5165\u3002\u4ece\u6280\u672f\u4e0a\u8bb2\uff0c\u60a8\u53ef\u4ee5\u901a\u8fc7\u5176\u4ed6\u65b9\u5f0f\u5c06\u4f9d\u8d56\u9879\u6ce8\u5165\u670d\u52a1\uff0c\u4f8b\u5982\u4f7f\u7528\u5c5e\u6027\u6ce8\u5165\uff0c\u4f46\u5185\u7f6e DI \u5bb9\u5668\u4e0d\u652f\u6301\u8fd9\u4e9b\u6280\u672f\u3002<\/p>\n<p>I hope that this example made the advantages of using DI in your code apparent, but in many ways these benefits are secondary to the main benefit of using DI. In particular, DI helps keep your code loosely coupled by coding to interfaces.<\/p>\n<p>\u6211\u5e0c\u671b\u8fd9\u4e2a\u4f8b\u5b50\u6e05\u695a\u5730\u8868\u660e\u4e86\u5728\u4ee3\u7801\u4e2d\u4f7f\u7528 DI \u7684\u4f18\u52bf\uff0c\u4f46\u5728\u8bb8\u591a\u65b9\u9762\uff0c\u8fd9\u4e9b\u597d\u5904\u662f\u6b21\u8981\u7684\uff0c\u800c\u4e0d\u662f\u4f7f\u7528 DI \u7684\u4e3b\u8981\u597d\u5904\u3002\u7279\u522b\u662f\uff0cDI \u901a\u8fc7\u7f16\u7801\u5230\u63a5\u53e3\u6765\u5e2e\u52a9\u4fdd\u6301\u4ee3\u7801\u677e\u6563\u8026\u5408\u3002<\/p>\n<h2>8.2 Creating loosely coupled code<\/h2>\n<h2>8.2 \u521b\u5efa\u677e\u6563\u8026\u5408\u7684\u4ee3\u7801<\/h2>\n<p>Coupling is an important concept in object-oriented programming, referring to how a given class depends on other classes to perform its function. Loosely coupled code doesn\u2019t need to know a lot of details about a particular component to use it.<\/p>\n<p>\u8026\u5408\u662f\u9762\u5411\u5bf9\u8c61\u7f16\u7a0b\u4e2d\u7684\u4e00\u4e2a\u91cd\u8981\u6982\u5ff5\uff0c\u6307\u7684\u662f\u7ed9\u5b9a\u7684\u7c7b\u5982\u4f55\u4f9d\u8d56\u5176\u4ed6\u7c7b\u6765\u6267\u884c\u5176\u529f\u80fd\u3002\u677e\u6563\u8026\u5408\u7684\u4ee3\u7801\u4e0d\u9700\u8981\u4e86\u89e3\u6709\u5173\u7279\u5b9a\u7ec4\u4ef6\u7684\u5927\u91cf\u8be6\u7ec6\u4fe1\u606f\u5373\u53ef\u4f7f\u7528\u5b83\u3002<\/p>\n<p>The initial example of RegisterUser and EmailSender was an example of tight coupling; you were creating the EmailSender object directly and needed to know exactly how to wire it up. On top of that, the code was difficult to test. Any attempts to test RegisterUser would result in an email\u2019s being sent. If you were testing the controller with a suite of unit tests, that approach would be a surefire way to get your email server blacklisted for spam!<\/p>\n<p>RegisterUser \u548c EmailSender \u7684\u521d\u59cb\u793a\u4f8b\u662f\u7d27\u5bc6\u8026\u5408\u7684\u793a\u4f8b;\u60a8\u76f4\u63a5\u521b\u5efa\u4e86 EmailSender \u5bf9\u8c61\uff0c\u5e76\u4e14\u9700\u8981\u786e\u5207\u5730\u77e5\u9053\u5982\u4f55\u8fde\u63a5\u5b83\u3002\u6700\u91cd\u8981\u7684\u662f\uff0c\u4ee3\u7801\u5f88\u96be\u6d4b\u8bd5\u3002\u4efb\u4f55\u6d4b\u8bd5 RegisterUser \u7684\u5c1d\u8bd5\u90fd\u4f1a\u5bfc\u81f4\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002\u5982\u679c\u4f60\u6b63\u5728\u4f7f\u7528\u4e00\u5957\u5355\u5143\u6d4b\u8bd5\u6765\u6d4b\u8bd5\u63a7\u5236\u5668\uff0c\u90a3\u4e48\u8fd9\u79cd\u65b9\u6cd5\u5c06\u662f\u8ba9\u4f60\u7684\u7535\u5b50\u90ae\u4ef6\u670d\u52a1\u5668\u88ab\u5217\u5165\u5783\u573e\u90ae\u4ef6\u9ed1\u540d\u5355\u7684\u53ef\u9760\u65b9\u6cd5\uff01<\/p>\n<p>Taking EmailSender as a parameter and removing the responsibility of creating the object helps reduce the coupling in the system. If the EmailSender implementation changes so that it has another dependency, you no longer have to update RegisterUser at the same time.<\/p>\n<p>\u5c06 EmailSender \u4f5c\u4e3a\u53c2\u6570\u5e76\u6d88\u9664\u521b\u5efa\u5bf9\u8c61\u7684\u8d23\u4efb\u6709\u52a9\u4e8e\u51cf\u5c11\u7cfb\u7edf\u4e2d\u7684\u8026\u5408\u3002\u5982\u679c EmailSender \u5b9e\u73b0\u53d1\u751f\u66f4\u6539\uff0c\u4f7f\u5176\u5177\u6709\u53e6\u4e00\u4e2a\u4f9d\u8d56\u9879\uff0c\u5219\u4e0d\u518d\u9700\u8981\u540c\u65f6\u66f4\u65b0 RegisterUser\u3002<\/p>\n<p>One problem that remains is that RegisterUser is still tied to an implementation rather than an abstraction. Coding to abstractions (often interfaces) is a common design pattern that helps further reduce the coupling of a system, as you\u2019re not tied to a single implementation. This pattern is particularly useful for making classes testable, as you can create stub or mock implementations of your dependencies for testing purposes, as shown in figure 8.3.<\/p>\n<p>\u4ecd\u7136\u5b58\u5728\u7684\u4e00\u4e2a\u95ee\u9898\u662f RegisterUser \u4ecd\u7136\u4e0e\u5b9e\u73b0\u800c\u4e0d\u662f\u62bd\u8c61\u76f8\u5173\u8054\u3002\u5bf9\u62bd\u8c61\uff08\u901a\u5e38\u662f\u63a5\u53e3\uff09\u8fdb\u884c\u7f16\u7801\u662f\u4e00\u79cd\u5e38\u89c1\u7684\u8bbe\u8ba1\u6a21\u5f0f\uff0c\u6709\u52a9\u4e8e\u8fdb\u4e00\u6b65\u51cf\u5c11\u7cfb\u7edf\u7684\u8026\u5408\uff0c\u56e0\u4e3a\u60a8\u4e0d\u53d7\u5355\u4e2a\u5b9e\u73b0\u7684\u675f\u7f1a\u3002\u6b64\u6a21\u5f0f\u5bf9\u4e8e\u4f7f\u7c7b\u53ef\u6d4b\u8bd5\u7279\u522b\u6709\u7528\uff0c\u56e0\u4e3a\u60a8\u53ef\u4ee5\u521b\u5efa\u4f9d\u8d56\u9879\u7684\u5b58\u6839\u6216\u6a21\u62df\u5b9e\u73b0\u4ee5\u8fdb\u884c\u6d4b\u8bd5\uff0c\u5982\u56fe 8.3 \u6240\u793a\u3002<\/p>\n<p><strong>Tip<\/strong> You can choose among many mocking frameworks. I\u2019m most familiar with Moq, but NSubstitute and FakeItEasy are also popular options.<br \/>\n<strong>\u63d0\u793a\uff1a<\/strong> \u60a8\u53ef\u4ee5\u5728\u8bb8\u591a\u6a21\u62df\u6846\u67b6\u4e2d\u8fdb\u884c\u9009\u62e9\u3002\u6211\u6700\u719f\u6089 Moq\uff0c\u4f46 NSubstitute \u548c FakeItEasy \u4e5f\u662f\u53d7\u6b22\u8fce\u7684\u9009\u62e9\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0803.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 8.3 By coding to interfaces instead of an explicit implementation, you can use different IEmailSender implementations in different scenarios, such as a MockEmailSender in unit tests.<br \/>\n\u56fe 8.3 \u901a\u8fc7\u5bf9\u63a5\u53e3\u8fdb\u884c\u7f16\u7801\u800c\u4e0d\u662f\u663e\u5f0f\u5b9e\u73b0\uff0c\u53ef\u4ee5\u5728\u4e0d\u540c\u7684\u573a\u666f\u4e2d\u4f7f\u7528\u4e0d\u540c\u7684 IEmailSender \u5b9e\u73b0\uff0c\u4f8b\u5982\u5355\u5143\u6d4b\u8bd5\u4e2d\u7684 MockEmailSender\u3002<\/p>\n<p>As an example, you might create an IEmailSender interface, which EmailSender would implement:<br \/>\n\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u4e00\u4e2a IEmailSender\u63a5\u53e3\uff0cEmailSender \u5c06\u5b9e\u73b0\u8be5\u63a5\u53e3\uff1a<\/p>\n<pre><code>public interface IEmailSender\n{\n    public void SendEmail(string username);\n}<\/code><\/pre>\n<p>Then RegisterUser could depend on this interface instead of the specific EmailSender implementation, as shown in the following listing, allowing you to use a different implementation during unit tests, such as a DummyEmailSender.<\/p>\n<p>\u7136\u540e RegisterUser \u53ef\u4ee5\u4f9d\u8d56\u4e8e\u6b64\u63a5\u53e3\u800c\u4e0d\u662f\u7279\u5b9a\u7684 EmailSender \u5b9e\u73b0\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\uff0c\u4ece\u800c\u5141\u8bb8\u60a8\u5728\u5355\u5143\u6d4b\u8bd5\u671f\u95f4\u4f7f\u7528\u4e0d\u540c\u7684\u5b9e\u73b0\uff0c\u4f8b\u5982 DummyEmailSender\u3002<\/p>\n<p>Listing 8.5 Using interfaces with dependency injection<br \/>\n\u6e05\u5355 8.5 \u4f7f\u7528\u5e26\u6709\u4f9d\u8d56\u6ce8\u5165\u7684\u63a5\u53e3<\/p>\n<pre><code>string RegisterUser(string username, IEmailSender emailSender) \u2776\n{\n    emailSender.SendEmail(username); \u2777\n    return $&quot;Email sent to {username}!&quot;;\n}\n<\/code><\/pre>\n<p>\u2776 Now you depend on IEmailSender instead of the specific EmailSender<br \/>\nimplementation.<br \/>\n\u73b0\u5728\u60a8\u4f9d\u8d56\u4e8e IEmailSender \u800c\u4e0d\u662f\u7279\u5b9a\u7684 EmailSender \u5b9e\u73b0\u3002<\/p>\n<p>\u2777 You don\u2019t care what the implementation is as long as it implements<br \/>\nIEmailSender.<br \/>\n\u60a8\u4e0d\u5173\u5fc3\u5b9e\u73b0\u662f\u4ec0\u4e48\uff0c\u53ea\u8981\u5b83\u5b9e\u73b0 IEmailSender \u5373\u53ef\u3002<\/p>\n<p>The key point here is that the consuming code, RegisterUser, doesn\u2019t care how the dependency is implemented\u2014only that it implements the IEmailSender interface and exposes a SendEmail method. Now the application code is independent of the implementation.<\/p>\n<p>\u6b64\u5904\u7684\u5173\u952e\u70b9\u662f\uff0c\u4f7f\u7528\u4ee3\u7801 RegisterUser \u5e76\u4e0d\u5173\u5fc3\u4f9d\u8d56\u9879\u662f\u5982\u4f55\u5b9e\u73b0\u7684\uff0c\u53ea\u5173\u5fc3\u5b83\u5b9e\u73b0 IEmailSender \u63a5\u53e3\u5e76\u516c\u5f00 SendEmail \u65b9\u6cd5\u3002\u73b0\u5728\uff0c\u5e94\u7528\u7a0b\u5e8f\u4ee3\u7801\u72ec\u7acb\u4e8e\u5b9e\u73b0\u3002<\/p>\n<p>I hope that the principles behind DI seem to be sound. Having loosely coupled code makes it easy to change or swap out implementations. But this still leaves a question: how does the application know to use EmailSender in production instead of DummyEmailSender? The process of telling your DI container \u201cWhen you need IEmailSender, use EmailSender\u201d is called registration.<\/p>\n<p>\u6211\u5e0c\u671b DI \u80cc\u540e\u7684\u539f\u5219\u4f3c\u4e4e\u662f\u5408\u7406\u7684\u3002\u62e5\u6709\u677e\u6563\u8026\u5408\u7684\u4ee3\u7801\u53ef\u4ee5\u5f88\u5bb9\u6613\u5730\u66f4\u6539\u6216\u6362\u51fa\u5b9e\u73b0\u3002\u4f46\u8fd9\u4ecd\u7136\u7559\u4e0b\u4e86\u4e00\u4e2a\u95ee\u9898\uff1a\u5e94\u7528\u7a0b\u5e8f\u5982\u4f55\u77e5\u9053\u5728\u751f\u4ea7\u73af\u5883\u4e2d\u4f7f\u7528 EmailSender \u800c\u4e0d\u662f DummyEmailSender\uff1f\u544a\u8bc9 DI \u5bb9\u5668\u201c\u5f53\u60a8\u9700\u8981 IEmailSender \u65f6\uff0c\u8bf7\u4f7f\u7528 EmailSender\u201d\u7684\u8fc7\u7a0b\u79f0\u4e3a\u6ce8\u518c\u3002<\/p>\n<p><strong>Definition<\/strong> You register services with a DI container so that it knows which implementation to use for each requested service. This registration typically takes the form \u201cFor interface X, use implementation Y.\u201d<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u60a8\u53ef\u4ee5\u5411 DI \u5bb9\u5668\u6ce8\u518c\u670d\u52a1\uff0c\u4ee5\u4fbf\u5b83\u77e5\u9053\u8981\u4e3a\u6bcf\u4e2a\u8bf7\u6c42\u7684\u670d\u52a1\u4f7f\u7528\u54ea\u4e2a\u5b9e\u73b0\u3002\u6b64\u6ce8\u518c\u901a\u5e38\u91c7\u7528\u201c\u5bf9\u4e8e\u63a5\u53e3 X\uff0c\u4f7f\u7528\u5b9e\u73b0 Y\u201d\u7684\u5f62\u5f0f\u3002<\/p>\n<p>Exactly how you register your interfaces and types with a DI container can vary depending on the specific DI container implementation, but the principles are generally the same. ASP.NET Core includes a simple DI container out of the box, so let\u2019s look at how it\u2019s used during a typical request.<br \/>\n\u5411 DI \u5bb9\u5668\u6ce8\u518c\u63a5\u53e3\u548c\u7c7b\u578b\u7684\u5177\u4f53\u65b9\u5f0f\u53ef\u80fd\u56e0\u7279\u5b9a\u7684 DI \u5bb9\u5668\u800c\u5f02\u5b9e\u73b0\uff0c\u4f46\u539f\u5219\u901a\u5e38\u662f\u76f8\u540c\u7684\u3002ASP.NET Core \u5305\u542b\u4e00\u4e2a\u7b80\u5355\u7684\u5f00\u7bb1\u5373\u7528\u7684 DI \u5bb9\u5668\uff0c\u56e0\u6b64\u8ba9\u6211\u4eec\u770b\u770b\u5728\u5178\u578b\u8bf7\u6c42\u671f\u95f4\u5982\u4f55\u4f7f\u7528\u5b83\u3002<\/p>\n<h2>8.3 Using dependency injection in ASP.NET Core<\/h2>\n<h2>\u5728 ASP.NET Core \u4e2d\u4f7f\u7528\u4f9d\u8d56\u9879\u6ce8\u5165<\/h2>\n<p>ASP.NET Core was designed from the outset to be modular and composable, with an almost plugin-style architecture, which is generally complemented by DI. Consequently, ASP.NET Core includes a simple DI container that all the framework libraries use to register themselves and their dependencies.<\/p>\n<p>ASP.NET Core \u4ece\u4e00\u5f00\u59cb\u5c31\u8bbe\u8ba1\u4e3a\u6a21\u5757\u5316\u548c\u53ef\u7ec4\u5408\u7684\uff0c\u5177\u6709\u51e0\u4e4e\u63d2\u4ef6\u5f0f\u7684\u67b6\u6784\uff0c\u901a\u5e38\u7531 DI \u8865\u5145\u3002\u56e0\u6b64\uff0cASP.NET Core \u5305\u542b\u4e00\u4e2a\u7b80\u5355\u7684 DI \u5bb9\u5668\uff0c\u6240\u6709\u6846\u67b6\u5e93\u90fd\u4f7f\u7528\u5b83\u6765\u6ce8\u518c\u81ea\u8eab\u53ca\u5176\u4f9d\u8d56\u9879\u3002<\/p>\n<p>This container is used, for example, to register the minimal API infrastructure\u2014the formatters, the Kestrel web server, and so on. It\u2019s a basic container, so it exposes only a few methods for registering services, but you have the option to replace it with a third-party DI container that gives you extra capabilities, such as autoregistration and setter injection. The DI container is built into the ASP.NET Core hosting model, as shown in figure 8.4.<\/p>\n<p>\u4f8b\u5982\uff0c\u6b64\u5bb9\u5668\u7528\u4e8e\u6ce8\u518c\u6700\u5c0f API \u57fa\u7840\u8bbe\u65bd \u2014 \u683c\u5f0f\u5316\u7a0b\u5e8f\u3001Kestrel Web \u670d\u52a1\u5668\u7b49\u3002\u5b83\u662f\u4e00\u4e2a\u57fa\u672c\u5bb9\u5668\uff0c\u56e0\u6b64\u5b83\u53ea\u516c\u5f00\u4e86\u51e0\u79cd\u7528\u4e8e\u6ce8\u518c\u670d\u52a1\u7684\u65b9\u6cd5\uff0c\u4f46\u60a8\u53ef\u4ee5\u9009\u62e9\u5c06\u5176\u66ff\u6362\u4e3a\u7b2c\u4e09\u65b9 DI \u5bb9\u5668\uff0c\u8be5\u5bb9\u5668\u4e3a\u60a8\u63d0\u4f9b\u989d\u5916\u7684\u529f\u80fd\uff0c\u4f8b\u5982\u81ea\u52a8\u6ce8\u518c\u548c setter \u6ce8\u5165\u3002DI \u5bb9\u5668\u5185\u7f6e\u4e8e ASP.NET Core \u6258\u7ba1\u6a21\u578b\u4e2d\uff0c\u5982\u56fe 8.4 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0804.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 8.4 The ASP.NET Core hosting model uses the DI container to fulfill dependencies when creating minimal API endpoint handlers.<br \/>\n\u56fe 8.4 ASP.NET Core \u6258\u7ba1\u6a21\u578b\u5728\u521b\u5efa\u6700\u5c0f API \u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u65f6\u4f7f\u7528 DI \u5bb9\u5668\u6765\u5b9e\u73b0\u4f9d\u8d56\u9879\u3002<\/p>\n<p>The hosting model pulls dependencies from the DI container when they\u2019re needed. If the framework determines that it must invoke RegisterHandler due to the incoming URL\/route, the RequestDelegateFactory responsible for creating minimal APIs asks the DI container for an IEmailSender implementation.<\/p>\n<p>\u6258\u7ba1\u6a21\u578b\u5728\u9700\u8981\u65f6\u4ece DI \u5bb9\u5668\u4e2d\u63d0\u53d6\u4f9d\u8d56\u9879\u3002\u5982\u679c\u6846\u67b6\u786e\u5b9a\u5b83\u5fc5\u987b\u8c03\u7528 RegisterHandler\uff0c\u56e0\u4e3a\u4f20\u5165URL\/\u8def\u7531\uff0c\u8d1f\u8d23\u521b\u5efa\u6700\u5c0f API \u7684 RequestDelegateFactory \u5411 DI \u5bb9\u5668\u8bf7\u6c42 IEmailSender \u5b9e\u73b0\u3002<\/p>\n<p><strong>Note<\/strong> RequestDelegateFactory is part of the minimal API framework that\u2019s responsible for invoking your minimal API handlers. You won\u2019t use or interact with it directly, but it\u2019s behind the scenes interacting with the DI container. I have a detailed series exploring this type on my blog at <a href=\"http:\/\/mng.bz\/Gy6v\">http:\/\/mng.bz\/Gy6v<\/a>. But be warned: this post goes into far more detail than most developers will ever need (or want)!<br \/>\n<strong>\u6ce8\u610f\uff1a<\/strong> RequestDelegateFactory \u662f\u8d1f\u8d23\u8c03\u7528\u6700\u5c0f API \u5904\u7406\u7a0b\u5e8f\u7684\u6700\u5c0f API \u6846\u67b6\u7684\u4e00\u90e8\u5206\u3002\u60a8\u4e0d\u4f1a\u76f4\u63a5\u4f7f\u7528\u5b83\u6216\u4e0e\u4e4b\u4ea4\u4e92\uff0c\u4f46\u5b83\u5728\u5e55\u540e\u4e0e DI \u5bb9\u5668\u4ea4\u4e92\u3002\u6211\u5728 <a href=\"http:\/\/mng.bz\/Gy6v\">http:\/\/mng.bz\/Gy6v<\/a> \u7684\u535a\u5ba2\u4e0a\u6709\u4e00\u4e2a\u8be6\u7ec6\u7684\u7cfb\u5217\u6765\u63a2\u7d22\u8fd9\u79cd\u7c7b\u578b\u7684\u7c7b\u578b\u3002\u4f46\u8bf7\u6ce8\u610f\uff1a\u8fd9\u7bc7\u6587\u7ae0\u6bd4\u5927\u591a\u6570\u5f00\u53d1\u4eba\u5458\u9700\u8981\uff08\u6216\u60f3\u8981\uff09\u7684\u8981\u8be6\u7ec6\u5f97\u591a\uff01<\/p>\n<p>The DI container needs to know what to create when asked for IEmailSender, so you must have registered an implementation, such as EmailSender, with the container. When an implementation is registered, the DI container can inject it anywhere, which means that you can inject framework-related services (such as LinkGenerator from chapter 6) into your own custom services. It also means that you can register alternative versions of framework services and have the framework automatically use those versions in place of the defaults.<\/p>\n<p>DI \u5bb9\u5668\u9700\u8981\u77e5\u9053\u5728\u8bf7\u6c42 IEmailSender \u65f6\u8981\u521b\u5efa\u4ec0\u4e48\uff0c\u56e0\u6b64\u60a8\u5fc5\u987b\u5df2\u5411\u5bb9\u5668\u6ce8\u518c\u4e00\u4e2a\u5b9e\u73b0\uff0c\u4f8b\u5982 EmailSender\u3002\u6ce8\u518c\u5b9e\u73b0\u540e\uff0cDI \u5bb9\u5668\u53ef\u4ee5\u5c06\u5176\u6ce8\u5165\u5230\u4efb\u4f55\u4f4d\u7f6e\uff0c\u8fd9\u610f\u5473\u7740\u60a8\u53ef\u4ee5\u5c06\u4e0e\u6846\u67b6\u76f8\u5173\u7684\u670d\u52a1\uff08\u4f8b\u5982\u7b2c 6 \u7ae0\u4e2d\u7684 LinkGenerator\uff09\u6ce8\u5165\u5230\u60a8\u81ea\u5df1\u7684\u81ea\u5b9a\u4e49\u670d\u52a1\u4e2d\u3002\u8fd9\u4e5f\u610f\u5473\u7740\u60a8\u53ef\u4ee5\u6ce8\u518c\u6846\u67b6\u670d\u52a1\u7684\u66ff\u4ee3\u7248\u672c\uff0c\u5e76\u8ba9\u6846\u67b6\u81ea\u52a8\u4f7f\u7528\u8fd9\u4e9b\u7248\u672c\u6765\u4ee3\u66ff\u9ed8\u8ba4\u7248\u672c\u3002<\/p>\n<p>Other ASP.NET Core infrastructure, such as the Model-View-Controller (MVC) and Razor Pages frameworks (which you learn about in part 3), uses dependency injection in a similar way to minimal APIs. These frameworks use the DI container to create the dependencies required by their own handlers, such as for a Razor Page (figure 8.5).<\/p>\n<p>\u5176\u4ed6 ASP.NET Core \u57fa\u7840\u7ed3\u6784\uff0c\u4f8b\u5982\u6a21\u578b\u89c6\u56fe\u63a7\u5236\u5668 \uff08MVC\uff09 \u548c Razor Pages \u6846\u67b6\uff08\u5728\u7b2c 3 \u90e8\u5206\u4e2d\u4ecb\u7ecd\uff09\uff0c\u4ee5\u4e0e\u6700\u5c0f API \u7c7b\u4f3c\u7684\u65b9\u5f0f\u4f7f\u7528\u4f9d\u8d56\u9879\u6ce8\u5165\u3002\u8fd9\u4e9b\u6846\u67b6\u4f7f\u7528 DI \u5bb9\u5668\u521b\u5efa\u5176\u81ea\u5df1\u7684\u5904\u7406\u7a0b\u5e8f\u6240\u9700\u7684\u4f9d\u8d56\u9879\uff0c\u4f8b\u5982 Razor Page\uff08\u56fe 8.5\uff09\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0805.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 8.5 The ASP.NET Core hosting model uses the DI container to fulfill dependencies when creating Razor Pages.<br \/>\n\u56fe 8.5 ASP.NET Core \u6258\u7ba1\u6a21\u578b\u5728\u521b\u5efa Razor Pages \u65f6\u4f7f\u7528 DI \u5bb9\u5668\u6765\u5b9e\u73b0\u4f9d\u8d56\u9879\u3002<\/p>\n<p>The flexibility to choose exactly how and which components you combine in your applications is one of the selling points of DI. In section 8.4 you\u2019ll learn how to configure DI in your own ASP.NET Core application, using the default, built-in container.<br \/>\nDI \u7684\u5356\u70b9\u4e4b\u4e00\u662f\u53ef\u4ee5\u7075\u6d3b\u5730\u9009\u62e9\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7ec4\u5408\u7684\u7ec4\u4ef6\u4ee5\u53ca\u7ec4\u5408\u54ea\u4e9b\u7ec4\u4ef6\u3002\u5728\u7b2c 8.4 \u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5728\u62e5\u6709 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\uff0c\u4f7f\u7528\u9ed8\u8ba4\u7684\u5185\u7f6e\u5bb9\u5668\u3002<\/p>\n<h2>8.4 Adding ASP.NET Core framework services to the container<\/h2>\n<h2>8.4 \u5c06 ASP.NET Core Framework \u670d\u52a1\u6dfb\u52a0\u5230\u5bb9\u5668\u4e2d<\/h2>\n<p>Before ASP.NET Core, using DI was optional. By contrast, to build all but the most trivial ASP.NET Core apps, some degree of DI is required. As I\u2019ve mentioned, the underlying framework depends on it, so features such as Razor Pages and authentication require you to configure the required services. In this section you\u2019ll see how to register these framework services with the built-in container. In chapter 9 you\u2019ll learn how to register your own services with the DI container.<\/p>\n<p>\u5728 ASP.NET Core \u4e4b\u524d\uff0c\u4f7f\u7528 DI \u662f\u53ef\u9009\u7684\u3002\u76f8\u6bd4\u4e4b\u4e0b\uff0c\u8981\u6784\u5efa\u9664\u6700\u7410\u788e\u7684 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e4b\u5916\u7684\u6240\u6709\u5e94\u7528\u7a0b\u5e8f\uff0c\u9700\u8981\u4e00\u5b9a\u7a0b\u5ea6\u7684 DI\u3002\u6b63\u5982\u6211\u6240\u63d0\u5230\u7684\uff0c\u5e95\u5c42\u6846\u67b6\u4f9d\u8d56\u4e8e\u5b83\uff0c\u56e0\u6b64 Razor Pages \u548c\u8eab\u4efd\u9a8c\u8bc1\u7b49\u529f\u80fd\u8981\u6c42\u60a8\u914d\u7f6e\u6240\u9700\u7684\u670d\u52a1\u3002\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u4f7f\u7528\u5185\u7f6e\u5bb9\u5668\u6ce8\u518c\u8fd9\u4e9b\u6846\u67b6\u670d\u52a1\u3002\u5728\u7b2c 9 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u4f7f\u7528 DI \u5bb9\u5668\u6ce8\u518c\u81ea\u5df1\u7684\u670d\u52a1\u3002<\/p>\n<p>ASP.NET Core uses DI to configure both its internal components, such as the Kestrel web server, and extra features, such as Razor Pages. To use these components at runtime, the DI container needs to know about all the classes it will need. You register these services with the Services property on the WebApplicationBuilder instance in Program.cs.<\/p>\n<p>ASP.NET Core \u4f7f\u7528 DI \u6765\u914d\u7f6e\u5176\u5185\u90e8\u7ec4\u4ef6\uff08\u5982 Kestrel Web \u670d\u52a1\u5668\uff09\u548c\u989d\u5916\u529f\u80fd\uff08\u5982 Razor Pages\uff09\u3002\u8981\u5728\u8fd0\u884c\u65f6\u4f7f\u7528\u8fd9\u4e9b\u7ec4\u4ef6\uff0cDI \u5bb9\u5668\u9700\u8981\u4e86\u89e3\u5b83\u9700\u8981\u7684\u6240\u6709\u7c7b\u3002\u4f7f\u7528 Program.cs \u4e2d WebApplicationBuilder \u5b9e\u4f8b\u4e0a\u7684 Services \u5c5e\u6027\u6ce8\u518c\u8fd9\u4e9b\u670d\u52a1\u3002<\/p>\n<p><strong>Note<\/strong> The Services property of WebApplicationBuilder is of type IServiceCollection. This is where you register the collection of services that the DI container knows about.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u7684 Services \u5c5e\u6027WebApplicationBuilder \u7684\u7c7b\u578b\u4e3aIServiceCollection \u7684 API API \u7684\u60a8\u53ef\u4ee5\u5728\u6b64\u5904\u6ce8\u518c DI \u5bb9\u5668\u77e5\u9053\u7684\u670d\u52a1\u96c6\u5408\u3002<\/p>\n<p>If you\u2019re thinking \u201cWait\u2014I have to configure all the internal components myself?\u201d, don\u2019t panic. Most of the core services are registered automatically by WebApplicationBuilder, and you don\u2019t need to do anything else. To use other features, such as Razor Pages or authentication, you do need to register the components explicitly with the container in your app, but that\u2019s not as hard as it sounds. All the common libraries you use expose handy extension methods to take care of the nitty-gritty details. These extension methods configure everything you need in one fell swoop instead of leaving you to wire everything up manually.<\/p>\n<p>\u5982\u679c\u60a8\u5728\u60f3\u201c\u7b49\u7b49 \u2014 \u6211\u5fc5\u987b\u81ea\u5df1\u914d\u7f6e\u6240\u6709\u5185\u90e8\u7ec4\u4ef6\u201d\uff0c\u8bf7\u4e0d\u8981\u60ca\u614c\u3002\u5927\u591a\u6570\u6838\u5fc3\u670d\u52a1\u90fd\u662f\u7531 WebApplicationBuilder \u81ea\u52a8\u6ce8\u518c\u7684\uff0c\u60a8\u65e0\u9700\u6267\u884c\u4efb\u4f55\u5176\u4ed6\u4f5c\u3002\u8981\u4f7f\u7528\u5176\u4ed6\u529f\u80fd\uff08\u5982 Razor Pages \u6216\u8eab\u4efd\u9a8c\u8bc1\uff09\uff0c\u60a8\u786e\u5b9e\u9700\u8981\u5411\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u5bb9\u5668\u663e\u5f0f\u6ce8\u518c\u7ec4\u4ef6\uff0c\u4f46\u8fd9\u5e76\u4e0d\u50cf\u542c\u8d77\u6765\u90a3\u4e48\u96be\u3002\u60a8\u4f7f\u7528\u7684\u6240\u6709\u516c\u5171\u5e93\u90fd\u516c\u5f00\u4e86\u65b9\u4fbf\u7684\u6269\u5c55\u65b9\u6cd5\uff0c\u4ee5\u5904\u7406\u7ec6\u8282\u3002\u8fd9\u4e9b\u6269\u5c55\u65b9\u6cd5\u4e00\u4e3e\u914d\u7f6e\u4e86\u60a8\u9700\u8981\u7684\u4e00\u5207\uff0c\u800c\u4e0d\u662f\u8ba9\u60a8\u624b\u52a8\u8fde\u63a5\u6240\u6709\u5185\u5bb9\u3002<\/p>\n<p>The Razor Pages framework exposes the AddRazorPages() extension method, for example, which adds all the necessary framework services to your app. Invoke the extension method on the Services property of WebApplicationBuilder in Program.cs, as shown in the following listing.<\/p>\n<p>\u4f8b\u5982\uff0cRazor Pages \u6846\u67b6\u516c\u5f00\u4e86 AddRazorPages\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u5c06\u6240\u6709\u5fc5\u8981\u7684\u6846\u67b6\u670d\u52a1\u6dfb\u52a0\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u3002\u5728 Program.cs \u4e2d\u8c03\u7528 WebApplicationBuilder \u7684 Services \u5c5e\u6027\u4e0a\u7684\u6269\u5c55\u65b9\u6cd5\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 8.6 Registering the Razor Pages services with the DI container<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddRazorPages(); \u2776\n\nWebApplication app = builder.Build();\napp.MapRazorPages(); \u2777\napp.Run();<\/code><\/pre>\n<p>\u2776 The AddRazorPages extension method adds all necessary services to the<br \/>\nIServiceCollection.<br \/>\nAddRazorPages \u6269\u5c55\u65b9\u6cd5\u5c06\u6240\u6709\u5fc5\u8981\u7684\u670d\u52a1\u6dfb\u52a0\u5230 IServiceCollection\u3002<\/p>\n<p>\u2777 Registers all the Razor Pages in your application as endpoints<br \/>\n\u5c06\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6240\u6709 Razor \u9875\u9762\u6ce8\u518c\u4e3a\u7ec8\u7ed3\u70b9<\/p>\n<p>It\u2019s as simple as that. Under the hood, this call is registering multiple components with the DI container, using the same APIs you\u2019ll see in chapter 9 for registering your own services.<br \/>\n\u5c31\u8fd9\u4e48\u7b80\u5355\u3002\u5728\u540e\u53f0\uff0c\u6b64\u8c03\u7528\u4f7f\u7528\u60a8\u5c06\u5728\u7b2c 9 \u7ae0\u4e2d\u770b\u5230\u7684\u76f8\u540c API \u5411 DI \u5bb9\u5668\u6ce8\u518c\u591a\u4e2a\u7ec4\u4ef6\uff0c\u4ee5\u6ce8\u518c\u60a8\u81ea\u5df1\u7684\u670d\u52a1\u3002<\/p>\n<p><strong>Note<\/strong> Don\u2019t worry about the Razor Pages aspect of this code; you\u2019ll learn how Razor Pages work in part 3. The important point of listing 8.6 is to show how to register and enable various features in ASP.NET Core.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u4e0d\u5fc5\u62c5\u5fc3\u6b64\u4ee3\u7801\u7684 Razor Pages \u65b9\u9762;\u60a8\u5c06\u5728\u7b2c 3 \u90e8\u5206\u4e2d\u4e86\u89e3 Razor Pages \u7684\u5de5\u4f5c\u539f\u7406\u3002\u5217\u51fa 8.6 \u7684\u91cd\u8981\u4e00\u70b9\u662f\u5c55\u793a\u5982\u4f55\u5728 ASP.NET Core \u4e2d\u6ce8\u518c\u548c\u542f\u7528\u5404\u79cd\u529f\u80fd\u3002<\/p>\n<p>Most nontrivial libraries that you add to your application will have services that you need to add to the DI container. By convention, each library that has necessary services should expose an Add*() extension method that you can call on WebApplicationBuilder.Services.<\/p>\n<p>\u60a8\u6dfb\u52a0\u5230\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u5927\u591a\u6570\u91cd\u8981\u5e93\u90fd\u5c06\u5305\u542b\u60a8\u9700\u8981\u6dfb\u52a0\u5230 DI \u5bb9\u5668\u4e2d\u7684\u670d\u52a1\u3002\u6309\u7167\u7ea6\u5b9a\uff0c\u6bcf\u4e2a\u5177\u6709\u5fc5\u8981\u670d\u52a1\u7684\u5e93\u90fd\u5e94\u8be5\u516c\u5f00\u4e00\u4e2a Add*\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\uff0c\u60a8\u53ef\u4ee5\u5728 WebApplicationBuilder.Services \u4e0a\u8c03\u7528\u8be5\u65b9\u6cd5\u3002<\/p>\n<p>There\u2019s no way of knowing exactly which libraries will require you to add services to the container; it\u2019s generally a case of checking the documentation for any libraries you use. If you forget to add them, you may find that the functionality doesn\u2019t work, or you might get a handy exception in your logs, like the one shown in figure 8.6. Keep an eye out for these exceptions, and be sure to register any services you need.<\/p>\n<p>\u65e0\u6cd5\u786e\u5207\u77e5\u9053\u54ea\u4e9b\u5e93\u9700\u8981\u60a8\u5411\u5bb9\u5668\u6dfb\u52a0\u670d\u52a1;\u901a\u5e38\u662f\u68c0\u67e5\u60a8\u4f7f\u7528\u7684\u4efb\u4f55\u5e93\u7684\u6587\u6863\u7684\u60c5\u51b5\u3002\u5982\u679c\u4f60\u5fd8\u8bb0\u6dfb\u52a0\u5b83\u4eec\uff0c\u4f60\u53ef\u80fd\u4f1a\u53d1\u73b0\u8be5\u529f\u80fd\u4e0d\u8d77\u4f5c\u7528\uff0c\u6216\u8005\u4f60\u53ef\u80fd\u4f1a\u5728\u65e5\u5fd7\u4e2d\u5f97\u5230\u4e00\u4e2a\u65b9\u4fbf\u7684\u5f02\u5e38\uff0c\u5982\u56fe 8.6 \u6240\u793a\u3002\u8bf7\u7559\u610f\u8fd9\u4e9b\u4f8b\u5916\u60c5\u51b5\uff0c\u5e76\u786e\u4fdd\u6ce8\u518c\u60a8\u9700\u8981\u7684\u4efb\u4f55\u670d\u52a1\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0806.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 8.6 If you fail to call AddRazorPages(), you\u2019ll get an exception when your app tries to start.<br \/>\n\u56fe 8.6 \u5982\u679c\u5728\u4f7f\u7528 Razor Pages \u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u8c03\u7528 AddRazorPages\uff08\uff09 \u5931\u8d25\uff0c\u5219\u5f53\u5e94\u7528\u7a0b\u5e8f\u5c1d\u8bd5\u542f\u52a8\u65f6\u5c06\u51fa\u73b0\u5f02\u5e38\u3002<\/p>\n<p>It\u2019s also worth noting that some of the Add*() extension methods allow you to specify additional options when you call them, often by way of a lambda expression. You can think of these options as configuring the installation of a service into your application. The AddRazorPages method, for example, provides a wealth of options for fine-tuning its behavior if you want to get your hands dirty, as shown by the IntelliSense snippet in figure 8.7.<\/p>\n<p>\u8fd8\u503c\u5f97\u6ce8\u610f\u7684\u662f\uff0c\u67d0\u4e9b Add*\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\u5141\u8bb8\u60a8\u5728\u8c03\u7528\u5b83\u4eec\u65f6\u6307\u5b9a\u5176\u4ed6\u9009\u9879\uff0c\u901a\u5e38\u662f\u901a\u8fc7 lambda \u8868\u8fbe\u5f0f\u3002\u60a8\u53ef\u4ee5\u5c06\u8fd9\u4e9b\u9009\u9879\u89c6\u4e3a\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u914d\u7f6e\u670d\u52a1\u5b89\u88c5\u3002\u4f8b\u5982\uff0cAddRazorPages \u65b9\u6cd5\u63d0\u4f9b\u4e86\u5927\u91cf\u9009\u9879\uff0c\u7528\u4e8e\u5fae\u8c03\u5176\u884c\u4e3a\uff08\u5982\u679c\u60a8\u60f3\u52a8\u624b\uff09\uff0c\u5982\u56fe 8.7 \u4e2d\u7684 IntelliSense \u4ee3\u7801\u6bb5\u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0807.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 8.7 Configuring services when adding them to the service collection. The AddRazorPages() function allows you to configure a wealth of the internals of the framework.<br \/>\n\u56fe 8.7 \u5728\u5c06\u670d\u52a1\u6dfb\u52a0\u5230\u670d\u52a1\u96c6\u5408\u65f6\u914d\u7f6e\u670d\u52a1\u3002AddRazorPages\uff08\uff09 \u51fd\u6570\u5141\u8bb8\u60a8\u914d\u7f6e\u6846\u67b6\u7684\u5927\u91cf\u5185\u90e8\u7ed3\u6784\u3002<\/p>\n<p>It\u2019s all very well registering services with the DI container, but the important question is how to use the container to get an instance of a registered service. In section 8.5 we look at two possible ways to access these services and discuss when you should choose one over the other.<\/p>\n<p>\u5411 DI \u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u4e00\u5207\u90fd\u5f88\u597d\uff0c\u4f46\u91cd\u8981\u7684\u95ee\u9898\u662f\u5982\u4f55\u4f7f\u7528\u5bb9\u5668\u6765\u83b7\u53d6\u5df2\u6ce8\u518c\u670d\u52a1\u7684\u5b9e\u4f8b\u3002\u5728 Section 8.5 \u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd\u8bbf\u95ee\u8fd9\u4e9b\u670d\u52a1\u7684\u4e24\u79cd\u53ef\u80fd\u65b9\u6cd5\uff0c\u5e76\u8ba8\u8bba\u4f55\u65f6\u5e94\u8be5\u9009\u62e9\u5176\u4e2d\u4e00\u79cd\u3002<\/p>\n<h2>8.5 Using services from the DI container<\/h2>\n<h2>8.5 \u4f7f\u7528 DI \u5bb9\u5668\u4e2d\u7684\u670d\u52a1<\/h2>\n<p>In a minimal API application, you have two main ways to access services from the DI container:<br \/>\n\u5728\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u6709\u4e24\u79cd\u4e3b\u8981\u65b9\u6cd5\u53ef\u4ee5\u4ece DI \u5bb9\u5668\u8bbf\u95ee\u670d\u52a1\uff1a<\/p>\n<ul>\n<li>\n<p>Inject services into an endpoint handler.<br \/>\n\u5c06\u670d\u52a1\u6ce8\u5165\u5230\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u3002<\/p>\n<\/li>\n<li>\n<p>Access the DI container directly in Program.cs.<br \/>\n\u76f4\u63a5\u5728 Program.cs \u4e2d\u8bbf\u95ee DI \u5bb9\u5668\u3002<\/p>\n<\/li>\n<\/ul>\n<p>The first approach\u2014injecting services into an endpoint handler\u2014is the most common way to access the root of a dependency graph. You should use this approach in almost all cases in your minimal API applications. You can inject a service into an endpoint handler by adding it as a parameter to your endpoint handler method, as you saw in chapters 6 and 7 when you injected a LinkGenerator instance into your handler.<\/p>\n<p>\u7b2c\u4e00\u79cd\u65b9\u6cd5 \u2014 \u5c06\u670d\u52a1\u6ce8\u5165\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f \u2014 \u662f\u8bbf\u95ee\u4f9d\u8d56\u5173\u7cfb\u56fe\u6839\u7684\u6700\u5e38\u7528\u65b9\u6cd5\u3002\u5728\u6700\u5c0f API \u5e94\u7528\u7a0b\u5e8f\u4e2d\uff0c\u51e0\u4e4e\u5728\u6240\u6709\u60c5\u51b5\u4e0b\u90fd\u5e94\u8be5\u4f7f\u7528\u6b64\u65b9\u6cd5\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5c06\u670d\u52a1\u4f5c\u4e3a\u53c2\u6570\u6dfb\u52a0\u5230\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u65b9\u6cd5\u4e2d\uff0c\u5c06\u670d\u52a1\u6ce8\u5165\u5230\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\uff0c\u5c31\u50cf\u60a8\u5728\u7b2c 6 \u7ae0\u548c\u7b2c 7 \u7ae0\u4e2d\u770b\u5230\u7684\u90a3\u6837\uff0c\u5f53\u60a8\u5c06 LinkGenerator \u5b9e\u4f8b\u6ce8\u5165\u5230\u5904\u7406\u7a0b\u5e8f\u4e2d\u65f6\u3002<\/p>\n<p>Listing 8.7 Injecting the LinkGenerator service in an endpoint handler<\/p>\n<pre><code>app.MapGet(&quot;\/links&quot;, (LinkGenerator links) =&gt; \u2776\n{\n    string link = links.GetPathByName(&quot;products&quot;);\n    return $&quot;View the product at {link}&quot;;\n});\n<\/code><\/pre>\n<p>\u2776 The DI container creates a LinkGenerator instance and passes it as the argument to the handler.<br \/>\nDI \u5bb9\u5668\u521b\u5efa\u4e00\u4e2a LinkGenerator \u5b9e\u4f8b\uff0c\u5e76\u5c06\u5176\u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u7ed9\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>The minimal API infrastructure sees that you need an instance of the LinkGenerator, which is a service registered in the container, and asks the DI container to provide an instance of the service. The DI container either creates a new instance of LinkGenerator (or reuses an existing one) and returns it to the minimal API infrastructure. Then the LinkGenerator is passed as an argument to invoke the endpoint handler.<\/p>\n<p>\u6700\u5c0f\u7684 API \u57fa\u7840\u7ed3\u6784\u770b\u5230\u60a8\u9700\u8981\u4e00\u4e2a LinkGenerator \u7684\u5b9e\u4f8b\uff0c\u8be5\u5b9e\u4f8b\u662f\u5728\u5bb9\u5668\u4e2d\u6ce8\u518c\u7684\u670d\u52a1\uff0c\u5e76\u8981\u6c42 DI \u5bb9\u5668\u63d0\u4f9b\u670d\u52a1\u7684\u5b9e\u4f8b\u3002DI \u5bb9\u5668\u521b\u5efa LinkGenerator \u7684\u65b0\u5b9e\u4f8b\uff08\u6216\u91cd\u7528\u73b0\u6709\u5b9e\u4f8b\uff09\u5e76\u5c06\u5176\u8fd4\u56de\u5230\u6700\u5c0f\u7684 API \u57fa\u7840\u7ed3\u6784\u3002\u7136\u540e\uff0c\u5c06 LinkGenerator \u4f5c\u4e3a\u53c2\u6570\u4f20\u9012\u4ee5\u8c03\u7528\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p><strong>Note<\/strong>  Whether the DI container creates a new instance or reuses an existing instance depends on the lifetime used to register the service. You\u2019ll learn about lifetimes in chapter 9.<br \/>\n<strong>\u6ce8\u610f<\/strong> DI \u5bb9\u5668\u662f\u521b\u5efa\u65b0\u5b9e\u4f8b\u8fd8\u662f\u91cd\u7528\u73b0\u6709\u5b9e\u4f8b\u53d6\u51b3\u4e8e\u7528\u4e8e\u6ce8\u518c\u670d\u52a1\u7684\u751f\u547d\u5468\u671f\u3002\u60a8\u5c06\u5728\u7b2c 9 \u7ae0\u4e2d\u4e86\u89e3\u4e00\u751f\u3002<\/p>\n<p>As already mentioned, the DI container creates an entire dependency graph. The LinkGenerator implementation registered with the DI container declares the dependencies it requires by having parameters in its constructor, just as the EmailSender type from section 8.1 declared its dependencies. When the DI container creates the LinkGenerator, it first creates all the service\u2019s dependencies and uses them to create the final LinkGenerator instance.<\/p>\n<p>\u5982\u524d\u6240\u8ff0\uff0cDI \u5bb9\u5668\u4f1a\u521b\u5efa\u4e00\u4e2a\u5b8c\u6574\u7684\u4f9d\u8d56\u9879\u5173\u7cfb\u56fe\u3002\u5411 DI \u5bb9\u5668\u6ce8\u518c\u7684 LinkGenerator \u5b9e\u73b0\u901a\u8fc7\u5176\u6784\u9020\u51fd\u6570\u4e2d\u7684\u53c2\u6570\u6765\u58f0\u660e\u5b83\u6240\u9700\u7684\u4f9d\u8d56\u9879\uff0c\u5c31\u50cf\u7b2c 8.1 \u8282\u4e2d\u7684 EmailSender \u7c7b\u578b\u58f0\u660e\u5176\u4f9d\u8d56\u9879\u4e00\u6837\u3002\u5f53 DI \u5bb9\u5668\u521b\u5efa LinkGenerator \u65f6\uff0c\u5b83\u9996\u5148\u521b\u5efa\u670d\u52a1\u7684\u6240\u6709\u4f9d\u8d56\u9879\uff0c\u5e76\u4f7f\u7528\u5b83\u4eec\u521b\u5efa\u6700\u7ec8\u7684 LinkGenerator \u5b9e\u4f8b\u3002<\/p>\n<p>Injecting services into your handlers is the canonical DI approach for minimal API endpoint handlers, but sometimes you need to access a service outside the context of a request. You may have lots of reasons to do this, but some of the most common relate to working with a database or logging. You may want to run some code when your app is starting to update a database\u2019s schema before the app starts handling requests, for example. If you need to access services in Program.cs outside the context of a request, you can retrieve services from the DI container directly by using the WebApplication.Services property, which exposes the container as an IServiceProvider.<\/p>\n<p>\u5c06\u670d\u52a1\u6ce8\u5165\u5904\u7406\u7a0b\u5e8f\u662f\u6700\u5c0f API \u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u7684\u89c4\u8303 DI \u65b9\u6cd5\uff0c\u4f46\u6709\u65f6\u60a8\u9700\u8981\u5728\u8bf7\u6c42\u4e0a\u4e0b\u6587\u4e4b\u5916\u8bbf\u95ee\u670d\u52a1\u3002\u60a8\u53ef\u80fd\u6709\u5f88\u591a\u7406\u7531\u8fd9\u6837\u505a\uff0c\u4f46\u5176\u4e2d\u4e00\u4e9b\u6700\u5e38\u89c1\u7684\u539f\u56e0\u4e0e\u4f7f\u7528\u6570\u636e\u5e93\u6216\u65e5\u5fd7\u8bb0\u5f55\u6709\u5173\u3002\u4f8b\u5982\uff0c\u5f53\u5e94\u7528\u7a0b\u5e8f\u5f00\u59cb\u66f4\u65b0\u6570\u636e\u5e93\u7684\u67b6\u6784\u65f6\uff0c\u60a8\u53ef\u80fd\u5e0c\u671b\u5728\u5e94\u7528\u7a0b\u5e8f\u5f00\u59cb\u5904\u7406\u8bf7\u6c42\u4e4b\u524d\u8fd0\u884c\u4e00\u4e9b\u4ee3\u7801\u3002\u5982\u679c\u9700\u8981\u5728\u8bf7\u6c42\u4e0a\u4e0b\u6587\u4e4b\u5916\u8bbf\u95ee Program.cs \u4e2d\u7684\u670d\u52a1\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528 WebApplication \u76f4\u63a5\u4ece DI \u5bb9\u5668\u4e2d\u68c0\u7d22\u670d\u52a1\u3002Services \u5c5e\u6027\uff0c\u8be5\u5c5e\u6027\u5c06\u5bb9\u5668\u516c\u5f00\u4e3a IServiceProvider\u3002<\/p>\n<p><strong>Note<\/strong> You register services with the IServiceCollection exposed on WebApplicationBuilder.Services. You request services with the IServiceProvider exposed on WebApplication.Services.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5411 IServiceCollection \u6ce8\u518c\u670d\u52a1\u5728 WebApplicationBuilder.Services \u4e0a\u516c\u5f00\u3002\u4f60\u8bf7\u6c42IServiceProvider \u7684\u670d\u52a1WebApplication.Services \u7684 Web \u5e94\u7528\u7a0b\u5e8f\u3002<\/p>\n<p>The IServiceProvider acts as a service locator, so you can request services from it directly by using GetService() and GetRequiredService():<\/p>\n<p>IServiceProvider \u5145\u5f53\u670d\u52a1\u5b9a\u4f4d\u5668\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u4f7f\u7528 GetService\uff08\uff09 \u548c GetRequiredService\uff08\uff09 \u76f4\u63a5\u5411\u5b83\u8bf7\u6c42\u670d\u52a1\uff1a<\/p>\n<ul>\n<li>\n<p><code>GetService&lt;T&gt;()<\/code>\u2014Returns the requested service T if it is available in the DI container; otherwise, returns null<br \/>\n<code>GetService&lt;T&gt;\uff08\uff09<\/code> \u2014 \u5982\u679c\u8bf7\u6c42\u7684\u670d\u52a1 T \u5728 DI \u5bb9\u5668\u4e2d\u53ef\u7528\uff0c\u5219\u8fd4\u56de\u8be5\u670d\u52a1 T;\u5426\u5219\uff0c\u8fd4\u56de null<\/p>\n<\/li>\n<li>\n<p><code>GetRequiredService&lt;T&gt;()<\/code>\u2014Returns the requested service T if it is available in the DI container; otherwise, throws an InvalidOperationException<br \/>\n<code>GetRequiredService&lt;T&gt;\uff08\uff09<\/code> \u2014 \u5982\u679c\u8bf7\u6c42\u7684\u670d\u52a1 T \u5728 DI \u5bb9\u5668\u4e2d\u53ef\u7528\uff0c\u5219\u8fd4\u56de\u8be5\u670d\u52a1 T;\u5426\u5219\uff0c\u5c06\u5f15\u53d1 InvalidOperationException<\/p>\n<\/li>\n<\/ul>\n<p>I generally favor GetRequiredService over GetService, as it immediately tells you whether you have a configuration problem with your DI container by throwing an exception, and you don\u2019t have to handle nulls.<\/p>\n<p>\u6211\u901a\u5e38\u66f4\u559c\u6b22 GetRequiredService \u800c\u4e0d\u662f GetService\uff0c\u56e0\u4e3a\u5b83\u901a\u8fc7\u5f15\u53d1\u5f02\u5e38\u7acb\u5373\u544a\u8bc9\u60a8 DI \u5bb9\u5668\u662f\u5426\u5b58\u5728\u914d\u7f6e\u95ee\u9898\uff0c\u5e76\u4e14\u60a8\u4e0d\u5fc5\u5904\u7406 null\u3002<\/p>\n<p>You can use either of these methods in Program.cs to retrieve a service from DI. The following listing shows how to retrieve a LinkGenerator from the DI container, but you can access any service registered in the DI container here.<\/p>\n<p>\u60a8\u53ef\u4ee5\u5728 Program.cs \u4e2d\u4f7f\u7528\u8fd9\u4e24\u79cd\u65b9\u6cd5\u4e2d\u7684\u4efb\u4f55\u4e00\u79cd\u4ece DI \u68c0\u7d22\u670d\u52a1\u3002\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u4ece DI \u5bb9\u5668\u4e2d\u68c0\u7d22 LinkGenerator\uff0c\u4f46\u60a8\u53ef\u4ee5\u5728\u6b64\u5904\u8bbf\u95ee\u5728 DI \u5bb9\u5668\u4e2d\u6ce8\u518c\u7684\u4efb\u4f55\u670d\u52a1\u3002<\/p>\n<p>Listing 8.8 Retrieving a service from the DI container using WebApplication.Services<br \/>\n\u6e05\u5355 8.8 \u4ece DI \u5bb9\u5668\u4e2d\u68c0\u7d22\u670d\u52a1\u4f7f\u7528 WebApplication.Services<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/&quot;, () =&gt; &quot;Hello World!&quot;);\n\nLinkGenerator links = \u2776\n    app.Services.GetRequiredService&lt;LinkGenerator&gt;(); \u2776\n\napp.Run(); \u2777<\/code><\/pre>\n<p>\u2776 Retrieves a service from the DI container using the <code>GetRequiredService&lt;T&gt;()<\/code><br \/>\nextension method<br \/>\n\u4f7f\u7528 <code>GetRequiredService&lt;T&gt;\uff08\uff09<\/code> \u6269\u5c55\u65b9\u6cd5\u4ece DI \u5bb9\u5668\u4e2d\u68c0\u7d22\u670d\u52a1<\/p>\n<p>\u2777 You must access services before app.Run(), as this call blocks until your app<br \/>\nexits.<br \/>\n\u60a8\u5fc5\u987b\u5148\u8bbf\u95ee\u670d\u52a1\uff0c\u7136\u540e\u624d\u80fd\u5e94\u7528\u3002Run\uff08\uff09\uff0c\u56e0\u4e3a\u6b64\u8c03\u7528\u4f1a\u4e00\u76f4\u963b\u6b62\uff0c\u76f4\u5230\u60a8\u7684\u5e94\u7528\u9000\u51fa\u3002<\/p>\n<p>This approach, in which you call the DI container directly to ask for a class, is called the service locator pattern. Generally speaking, you should try to avoid this pattern in your code; include your dependencies as constructor or endpoint handler arguments directly, and let the DI container provide them for you. This pattern is the only way to access DI services in the main loop of your application in Program.cs, however, so don\u2019t worry about using it here. Still, you should absolutely avoid accessing WebApplication.Services from inside your endpoint handlers or other types whenever possible.<\/p>\n<p>\u5728\u8fd9\u79cd\u65b9\u6cd5\u4e2d\uff0c\u60a8\u53ef\u4ee5\u76f4\u63a5\u8c03\u7528 DI \u5bb9\u5668\u6765\u8bf7\u6c42\u4e00\u4e2a\u7c7b\uff0c\u8fd9\u79cd\u65b9\u6cd5\u79f0\u4e3a\u670d\u52a1\u5b9a\u4f4d\u5668\u6a21\u5f0f\u3002\u4e00\u822c\u6765\u8bf4\uff0c\u60a8\u5e94\u8be5\u5c3d\u91cf\u907f\u514d\u5728\u4ee3\u7801\u4e2d\u4f7f\u7528\u8fd9\u79cd\u6a21\u5f0f;\u76f4\u63a5\u5c06\u60a8\u7684\u4f9d\u8d56\u9879\u4f5c\u4e3a\u6784\u9020\u51fd\u6570\u6216\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u53c2\u6570\u5305\u542b\u5728\u5185\uff0c\u5e76\u8ba9 DI \u5bb9\u5668\u4e3a\u60a8\u63d0\u4f9b\u5b83\u4eec\u3002\u4f46\u662f\uff0c\u6b64\u6a21\u5f0f\u662f\u5728 Program.cs \u4e2d\u8bbf\u95ee\u5e94\u7528\u7a0b\u5e8f\u4e3b\u5faa\u73af\u4e2d\u7684 DI \u670d\u52a1\u7684\u552f\u4e00\u65b9\u6cd5\uff0c\u56e0\u6b64\u4e0d\u5fc5\u62c5\u5fc3\u5728\u6b64\u5904\u4f7f\u7528\u5b83\u3002\u4e0d\u8fc7\uff0c\u60a8\u7edd\u5bf9\u5e94\u8be5\u907f\u514d\u8bbf\u95ee WebApplication \u3002\u5c3d\u53ef\u80fd\u4ece\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u6216\u5176\u4ed6\u7c7b\u578b\u5185\u90e8\u83b7\u53d6\u670d\u52a1\u3002<\/p>\n<p><strong>Note<\/strong> You can read about the service locator antipattern in Dependency Injection Principles, Practices, and Patterns, by Steven van Deursen and Mark Seemann (Manning, 2019).<br \/>\n<strong>\u6ce8\u610f<\/strong> \u60a8\u53ef\u4ee5\u5728 Steven van Deursen \u548c Mark Seemann \u7f16\u5199\u7684 Dependency Injection Principles\uff0c Practices\uff0c and Patterns\uff08Manning\uff0c2019 \u5e74\uff09\u4e2d\u9605\u8bfb\u6709\u5173\u670d\u52a1\u5b9a\u4f4d\u5668\u53cd\u6a21\u5f0f\u7684\u4fe1\u606f\u3002<\/p>\n<p>In this chapter we covered some of the reasons to use DI in your applications, how to enable optional ASP.NET Core features by adding services to the DI container, and how to access services from the DI container by using injection into your endpoint handlers. In chapter 9 you\u2019ll learn about service lifetimes and how to register your own services with the DI container.<\/p>\n<p>\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u4ecb\u7ecd\u4e86\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528 DI \u7684\u4e00\u4e9b\u539f\u56e0\uff0c\u5982\u4f55\u901a\u8fc7\u5411 DI \u5bb9\u5668\u6dfb\u52a0\u670d\u52a1\u6765\u542f\u7528\u53ef\u9009\u7684 ASP.NET Core \u529f\u80fd\uff0c\u4ee5\u53ca\u5982\u4f55\u901a\u8fc7\u4f7f\u7528\u6ce8\u5165\u5230\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u6765\u8bbf\u95ee DI \u5bb9\u5668\u4e2d\u7684\u670d\u52a1\u3002\u5728\u7b2c 9 \u7ae0\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u670d\u52a1\u751f\u547d\u5468\u671f\u4ee5\u53ca\u5982\u4f55\u5411 DI \u5bb9\u5668\u6ce8\u518c\u81ea\u5df1\u7684\u670d\u52a1\u3002<\/p>\n<h3>8.6 Summary<\/h3>\n<h3>8.6 \u603b\u7ed3<\/h3>\n<p>DI is baked into the ASP.NET Core framework. You need to ensure that your application adds all the framework\u2019s dependencies for optional features in Program.cs; otherwise, you\u2019ll get exceptions at runtime when the DI container can\u2019t find the required services.<br \/>\nDI \u5df2\u878d\u5165 ASP.NET Core \u6846\u67b6\u4e2d\u3002\u60a8\u9700\u8981\u786e\u4fdd\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e3a Program.cs \u4e2d\u7684\u53ef\u9009\u529f\u80fd\u6dfb\u52a0\u4e86\u6846\u67b6\u7684\u6240\u6709\u4f9d\u8d56\u9879;\u5426\u5219\uff0c\u5f53 DI \u5bb9\u5668\u627e\u4e0d\u5230\u6240\u9700\u7684\u670d\u52a1\u65f6\uff0c\u60a8\u5c06\u5728\u8fd0\u884c\u65f6\u6536\u5230\u5f02\u5e38\u3002<\/p>\n<p>The dependency graph is the set of objects that must be created to create a specific requested root object. The DI container creates all these dependencies for you.<br \/>\n\u4f9d\u8d56\u5173\u7cfb\u56fe\u662f\u521b\u5efa\u7279\u5b9a\u8bf7\u6c42\u7684\u6839\u5bf9\u8c61\u65f6\u5fc5\u987b\u521b\u5efa\u7684\u5bf9\u8c61\u96c6\u3002DI \u5bb9\u5668\u4f1a\u4e3a\u60a8\u521b\u5efa\u6240\u6709\u8fd9\u4e9b\u4f9d\u8d56\u9879\u3002<\/p>\n<p>You should aim to use explicit dependencies instead of implicit dependencies in most cases. ASP.NET Core uses constructor arguments and endpoint handler arguments to declare explicit dependencies.<br \/>\n\u5728\u5927\u591a\u6570\u60c5\u51b5\u4e0b\uff0c\u60a8\u5e94\u8be5\u4f7f\u7528\u663e\u5f0f\u4f9d\u8d56\u9879\u800c\u4e0d\u662f\u9690\u5f0f\u4f9d\u8d56\u9879\u3002ASP.NET Core \u4f7f\u7528\u6784\u9020\u51fd\u6570\u53c2\u6570\u548c\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u53c2\u6570\u6765\u58f0\u660e\u663e\u5f0f\u4f9d\u8d56\u9879\u3002<\/p>\n<p>When discussing DI, the term service is used to describe any class or interface registered with the container.<br \/>\n\u5728\u8ba8\u8bba DI \u65f6\uff0c\u672f\u8bed service \u7528\u4e8e\u63cf\u8ff0\u5411\u5bb9\u5668\u6ce8\u518c\u7684\u4efb\u4f55\u7c7b\u6216\u63a5\u53e3\u3002<\/p>\n<p>You register services with the DI container so that the container knows which implementation to use for each requested service. This registration typically takes the form \u201cFor interface X, use implementation Y.\u201d<br \/>\n\u60a8\u53ef\u4ee5\u5411 DI \u5bb9\u5668\u6ce8\u518c\u670d\u52a1\uff0c\u4ee5\u4fbf\u5bb9\u5668\u77e5\u9053\u8981\u4e3a\u6bcf\u4e2a\u8bf7\u6c42\u7684\u670d\u52a1\u4f7f\u7528\u54ea\u4e2a\u5b9e\u73b0\u3002\u6b64\u6ce8\u518c\u901a\u5e38\u91c7\u7528\u201c\u5bf9\u4e8e\u63a5\u53e3 X\uff0c\u4f7f\u7528\u5b9e\u73b0 Y\u201d\u7684\u5f62\u5f0f\u3002<\/p>\n<p>You must register services with the container by calling Addextension methods on the IServiceCollection exposed as WebApplicationBuilder.Services in Program.cs. If you forget to register a service that\u2019s used by the framework or in your own code, you\u2019ll get an InvalidOperationException at runtime.<br \/>\n\u5fc5\u987b\u901a\u8fc7\u5728 Program.cs \u4e2d\u4f5c\u4e3a WebApplicationBuilder.Services \u516c\u5f00\u7684 IServiceCollection \u4e0a\u8c03\u7528 Add\u6269\u5c55\u65b9\u6cd5\u6765\u5411\u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u3002\u5982\u679c\u60a8\u5fd8\u8bb0\u6ce8\u518c\u6846\u67b6\u4f7f\u7528\u7684\u670d\u52a1\u6216\u5728\u60a8\u81ea\u5df1\u7684\u4ee3\u7801\u4e2d\u6ce8\u518c\u7684\u670d\u52a1\uff0c\u60a8\u5c06\u5728\u8fd0\u884c\u65f6\u6536\u5230 InvalidOperationException\u3002<\/p>\n<p>You can retrieve services from the DI container in your endpoint handlers by adding a parameter of the required type.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u6dfb\u52a0\u6240\u9700\u7c7b\u578b\u7684\u53c2\u6570\uff0c\u4ece\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u7684 DI \u5bb9\u5668\u4e2d\u68c0\u7d22\u670d\u52a1\u3002<\/p>\n<p>You can retrieve services from the DI container in Program.cs via the service locator pattern by calling <code>GetService&lt;T&gt;()<\/code> or <code>GetRequiredService&lt;T&gt;()<\/code> on the IServiceProvider exposed as WebApplication.Services. Service location is generally considered to be an antipattern; generally, you shouldn\u2019t use it inside your handler methods, but it\u2019s fine to use it directly inside Program.cs.<br \/>\n\u60a8\u53ef\u4ee5\u901a\u8fc7\u5bf9\u516c\u5f00\u4e3a WebApplication.Services \u7684 IServiceProvider \u8c03\u7528 <code>GetService&lt;T&gt;\uff08\uff09<\/code> \u6216 <code>GetRequiredService&lt;T&gt;\uff08\uff09<\/code> Program.cs \u4e2d\u7684\u670d\u52a1\u5b9a\u4f4d\u5668\u6a21\u5f0f\u4ece DI \u5bb9\u5668\u4e2d\u68c0\u7d22\u670d\u52a1\u3002\u670d\u52a1\u4f4d\u7f6e\u901a\u5e38\u88ab\u89c6\u4e3a\u53cd\u6a21\u5f0f;\u901a\u5e38\uff0c\u60a8\u4e0d\u5e94\u8be5\u5728 Handler \u65b9\u6cd5\u4e2d\u4f7f\u7528\u5b83\uff0c\u4f46\u53ef\u4ee5\u76f4\u63a5\u5728 Program.cs \u4e2d\u4f7f\u7528\u5b83\u3002<\/p>\n<p><code>GetService&lt;T&gt;()<\/code> returns null if the requested service isn\u2019t registered with the DI container. By contrast, <code>GetRequiredService&lt;T&gt;()<\/code> throws an InvalidOperationException.<br \/>\n<code>GetService&lt;T&gt;\uff08\uff09<\/code> \u5982\u679c\u8bf7\u6c42\u7684\u670d\u52a1\u672a\u6ce8\u518c\u5230 DI \u5bb9\u5668\uff0c\u5219\u8fd4\u56de null\u3002\u76f8\u6bd4\u4e4b\u4e0b\uff0c<code>GetRequiredService&lt;T&gt;\uff08\uff09<\/code> \u4f1a\u5f15\u53d1 InvalidOperationException\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Part 2 Building complete applications \u7b2c 2 \u90e8\u5206\uff1a\u6784\u5efa\u5b8c\u6574\u7684\u5e94\u7528\u7a0b\u5e8f We covered a lot of ground in part 1. You saw how an ASP.NET Core application is composed of middleware, and we focused heavily on minimal API endpoints. You saw how to use them to build JSON APIs, how to extract common code using filters and route [&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-587","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\/587","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=587"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/587\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=587"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=587"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=587"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}