{"id":589,"date":"2025-04-05T11:37:08","date_gmt":"2025-04-05T03:37:08","guid":{"rendered":"https:\/\/www.hyy.net\/?p=589"},"modified":"2025-04-05T11:37:08","modified_gmt":"2025-04-05T03:37:08","slug":"asp-net-core-in-action-9-registering-services-with-dependency-injection","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=589","title":{"rendered":"ASP.NET Core in Action 9 Registering services with dependency injection"},"content":{"rendered":"<p>9 Registering services with dependency injection<br \/>\n9 \u4f7f\u7528\u4f9d\u8d56\u6ce8\u5165\u6ce8\u518c\u670d\u52a1<\/p>\n<h2>This chapter covers<\/h2>\n<h2>\u672c\u7ae0\u6db5\u76d6<\/h2>\n<ul>\n<li>\n<p>Configuring your services to work with dependency injection<br \/>\n\u914d\u7f6e\u670d\u52a1\u4ee5\u4f7f\u7528\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165<\/p>\n<\/li>\n<li>\n<p>Choosing the correct lifetime for your services<br \/>\n\u4e3a\u60a8\u7684\u670d\u52a1\u9009\u62e9\u6b63\u786e\u7684\u751f\u547d\u5468\u671f<\/p>\n<\/li>\n<\/ul>\n<p>In chapter 8 you learned about dependency injection (DI) in general, why it\u2019s useful as a pattern for developing loosely coupled code, and its central place in ASP.NET Core. In this chapter you\u2019ll build on that knowledge to apply DI to your own classes.<\/p>\n<p>\u5728\u7b2c 8 \u7ae0\u4e2d\uff0c\u60a8\u4e86\u89e3\u4e86\u4f9d\u8d56\u9879\u6ce8\u5165 \uff08DI\uff09 \u7684\u4e00\u822c\u77e5\u8bc6\uff0c\u4e3a\u4ec0\u4e48\u5b83\u4f5c\u4e3a\u5f00\u53d1\u677e\u6563\u8026\u5408\u4ee3\u7801\u7684\u6a21\u5f0f\u5f88\u6709\u7528\uff0c\u4ee5\u53ca\u5b83\u5728 ASP.NET Core \u4e2d\u7684\u6838\u5fc3\u4f4d\u7f6e\u3002\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5c06\u57fa\u4e8e\u8fd9\u4e9b\u77e5\u8bc6\u5c06 DI \u5e94\u7528\u4e8e\u60a8\u81ea\u5df1\u7684\u7c7b\u3002<\/p>\n<p>You\u2019ll start by learning 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 manually in your code. We look at the various patterns you can use to register your services and some of the limitations of the built-in DI container.<\/p>\n<p>\u9996\u5148\uff0c\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\u6211\u4eec\u6765\u770b\u770b\u4f60\u53ef\u4ee5\u7528\u6765\u6ce8\u518c\u670d\u52a1\u7684\u5404\u79cd\u6a21\u5f0f\uff0c\u4ee5\u53ca\u5185\u7f6e DI \u5bb9\u5668\u7684\u4e00\u4e9b\u9650\u5236\u3002<\/p>\n<p>Next, you\u2019ll learn how to handle multiple implementations of a service. You\u2019ll learn how to inject multiple versions of a service, how to override a default service registration, and how to register a service conditionally if you don\u2019t know whether it\u2019s already registered.<\/p>\n<p>\u63a5\u4e0b\u6765\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5904\u7406\u670d\u52a1\u7684\u591a\u4e2a\u5b9e\u73b0\u3002\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u6ce8\u5165\u670d\u52a1\u7684\u591a\u4e2a\u7248\u672c\uff0c\u5982\u4f55\u8986\u76d6\u9ed8\u8ba4\u670d\u52a1\u6ce8\u518c\uff0c\u4ee5\u53ca\u5982\u4f55\u5728\u4e0d\u77e5\u9053\u670d\u52a1\u662f\u5426\u5df2\u6ce8\u518c\u65f6\u6709\u6761\u4ef6\u5730\u6ce8\u518c\u670d\u52a1\u3002<\/p>\n<p>In section 9.4 we look at how you can control how long your objects are used\u2014that is, their lifetime. We explore the differences among the three lifetime options and some of the pitfalls to be aware of when you come to write your own applications. Finally, in section 9.5 you\u2019ll learn why lifetimes are important when resolving services outside the context of an HTTP request.<\/p>\n<p>\u5728 9.4 \u4e2d\uff0c\u6211\u4eec\u5c06\u4e86\u89e3\u5982\u4f55\u63a7\u5236\u5bf9\u8c61\u7684\u4f7f\u7528\u65f6\u95f4 \u2014 \u5373\u5b83\u4eec\u7684\u751f\u547d\u5468\u671f\u3002\u6211\u4eec\u63a2\u8ba8\u4e86\u4e09\u79cd\u751f\u547d\u5468\u671f\u9009\u9879\u4e4b\u95f4\u7684\u5dee\u5f02\uff0c\u4ee5\u53ca\u7f16\u5199\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u65f6\u9700\u8981\u6ce8\u610f\u7684\u4e00\u4e9b\u9677\u9631\u3002\u6700\u540e\uff0c\u5728 9.5 \u8282\u4e2d\uff0c\u60a8\u5c06\u4e86\u89e3\u4e3a\u4ec0\u4e48\u5728 HTTP \u8bf7\u6c42\u4e0a\u4e0b\u6587\u4e4b\u5916\u89e3\u6790\u670d\u52a1\u65f6\u751f\u547d\u5468\u671f\u5f88\u91cd\u8981\u3002<\/p>\n<p>We\u2019ll start by revisiting the EmailSender service from chapter 8 to see how you could register the dependency graph in your DI container.<\/p>\n<p>\u9996\u5148\uff0c\u6211\u4eec\u5c06\u91cd\u65b0\u5ba1\u89c6\u7b2c 8 \u7ae0\u4e2d\u7684 EmailSender \u670d\u52a1\uff0c\u4e86\u89e3\u5982\u4f55\u5728 DI \u5bb9\u5668\u4e2d\u6ce8\u518c\u4f9d\u8d56\u5173\u7cfb\u56fe\u3002<\/p>\n<h2>9.1 Registering custom services with the DI container<\/h2>\n<h2>9.1 \u5411 DI \u5bb9\u5668\u6ce8\u518c\u81ea\u5b9a\u4e49\u670d\u52a1<\/h2>\n<p>In this section you\u2019ll learn how to register your own services with the DI container. We\u2019ll explore the difference between a service and an implementation, and learn how to register the EmailSender hierarchy introduced in chapter 8.<\/p>\n<p>\u5728\u672c\u8282\u4e2d\uff0c\u60a8\u5c06\u5b66\u4e60\u5982\u4f55\u5411 DI \u5bb9\u5668\u6ce8\u518c\u81ea\u5df1\u7684\u670d\u52a1\u3002\u6211\u4eec\u5c06\u63a2\u8ba8\u670d\u52a1\u548c\u5b9e\u73b0\u4e4b\u95f4\u7684\u533a\u522b\uff0c\u5e76\u5b66\u4e60\u5982\u4f55\u6ce8\u518c\u7b2c 8 \u7ae0\u4e2d\u4ecb\u7ecd\u7684 EmailSender \u5c42\u6b21\u7ed3\u6784\u3002<\/p>\n<p>In chapter 8 I described a system for sending emails when a new user registers in your application. Initially, the minimal API endpoint handler RegisterUser created an instance of EmailSender manually, using code similar to the following listing (which you saw in chapter 8).<\/p>\n<p>\u5728\u7b2c 8 \u7ae0\u4e2d\uff0c\u6211\u63cf\u8ff0\u4e86\u4e00\u4e2a\u5f53\u65b0\u7528\u6237\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ce8\u518c\u65f6\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u7684\u7cfb\u7edf\u3002\u6700\u521d\uff0c\u6700\u5c0f API \u7aef\u70b9\u5904\u7406\u7a0b\u5e8f RegisterUser \u4f7f\u7528\u7c7b\u4f3c\u4e8e\u4ee5\u4e0b\u6e05\u5355\u7684\u4ee3\u7801\u624b\u52a8\u521b\u5efa\u4e86\u4e00\u4e2a EmailSender \u5b9e\u4f8b\uff08\u60a8\u5728\u7b2c 8 \u7ae0\u4e2d\u770b\u5230\uff09\u3002<\/p>\n<p>Listing 9.1 Creating an EmailSender instance without dependency injection<br \/>\n\u793a\u4f8b 9.1 \u521b\u5efa\u65e0\u4f9d\u8d56\u6ce8\u5165\u7684\u5b9e\u4f8bEmailSender<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/register\/{username}&quot;, RegisterUser); \u2776\n\napp.Run();\n\nstring RegisterUser(string username)\n{\n    IEmailSender emailSender = new EmailSender( \u2777\n        new MessageFactory(), \u2778\n        new NetworkClient( \u2779\n            new EmailServerSettings \u277a\n                ( \u277a\n                    Host: &quot;smtp.server.com&quot;, \u277a\n                    Port: 25 \u277a\n                )) \u277a\n            );\n    emailSender.SendEmail(username); \u277b\n    return $&quot;Email sent to {username}!&quot;;\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 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>\u2778 You need a new MessageFactory.<\/p>\n<p>\u60a8\u9700\u8981\u4e00\u4e2a\u65b0\u7684 MessageFactory\u3002<\/p>\n<p>\u2779 The NetworkClient also has dependencies.<br \/>\nNetworkClient \u4e5f\u6709\u4f9d\u8d56\u9879\u3002<\/p>\n<p>\u277a 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>\u277b Finally, you can send the email.<br \/>\n\u6700\u540e\uff0c\u60a8\u53ef\u4ee5\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3002<\/p>\n<p>We subsequently refactored this code to inject an instance of IEmailSender into the handler instead, as shown in listing 9.2. The IEmailSender interface decouples the endpoint handler from the EmailSender implementation, making it easier to change the implementation of EmailSender (or replace it) without having to rewrite RegisterUser.<\/p>\n<p>\u6211\u4eec\u968f\u540e\u91cd\u6784\u4e86\u8fd9\u6bb5\u4ee3\u7801\uff0c\u4ee5\u6ce8\u5165\u4e00\u4e2aIEmailSender \u6dfb\u52a0\u5230\u5904\u7406\u7a0b\u5e8f\u4e2d\uff0c\u5982\u6e05\u5355\u6240\u793a<br \/>\n9.2. IEmailSender \u63a5\u53e3\u5c06\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e0e EmailSender \u5b9e\u73b0\u89e3\u8026\uff0c\u4ece\u800c\u66f4\u5bb9\u6613\u66f4\u6539 mailSender \u7684\u5b9e\u73b0\uff08\u6216\u66ff\u6362\u5b83\uff09\uff0c\u800c\u65e0\u9700\u91cd\u5199 RegisterUser\u3002<\/p>\n<p>Listing 9.2 Using IEmailSender with dependency injection in an endpoint handler<br \/>\n\u6e05\u5355 9.2 \u4f7f\u7528\u5e26\u6709\u4f9d\u8d56\u6ce8\u5165\u7684 IEmailSender\u5728\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/register\/{username}&quot;, RegisterUser); \u2776\n\napp.Run();\n\nstring RegisterUser(string username, IEmailSender emailSender) \u2777\n{\n    emailSender.SendEmail(username); \u2778\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 IEmailSender is injected into the handler using DI.<br \/>\nIEmailSender \u4f7f\u7528 DI \u6ce8\u5165\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>\u2778 The handler uses the IEmailSender instance.<br \/>\n\u5904\u7406\u7a0b\u5e8f\u4f7f\u7528 IEmailSender \u5b9e\u4f8b\u3002<\/p>\n<p>The final step in making the refactoring work is configuring your services with the DI container. This configuration lets the DI container know what to use when it needs to fulfill the IEmailSender dependency. If you don\u2019t register your services, you\u2019ll get an exception at runtime, like the one in figure 9.1. This exception describes a model-binding problem; the minimal API infrastructure tries to bind the emailSender parameter to the request body because IEmailSender isn\u2019t a known service in the DI container.<\/p>\n<p>\u8fdb\u884c\u91cd\u6784\u7684\u6700\u540e\u4e00\u6b65\u662f\u4f7f\u7528 DI \u5bb9\u5668\u914d\u7f6e\u60a8\u7684\u670d\u52a1\u3002\u6b64\u914d\u7f6e\u4f7f DI \u5bb9\u5668\u77e5\u9053\u5728\u9700\u8981\u6ee1\u8db3 IEmailSender \u4f9d\u8d56\u9879\u65f6\u8981\u4f7f\u7528\u4ec0\u4e48\u3002\u5982\u679c\u60a8\u4e0d\u6ce8\u518c\u60a8\u7684\u670d\u52a1\uff0c\u60a8\u5c06\u5728\u8fd0\u884c\u65f6\u6536\u5230\u5f02\u5e38\uff0c\u5982\u56fe 9.1 \u4e2d\u6240\u793a\u3002\u6b64\u5f02\u5e38\u63cf\u8ff0\u4e86\u6a21\u578b\u7ed1\u5b9a\u95ee\u9898;\u6700\u5c0f API \u57fa\u7840\u7ed3\u6784\u5c1d\u8bd5\u5c06 emailSender \u53c2\u6570\u7ed1\u5b9a\u5230\u8bf7\u6c42\u6b63\u6587\uff0c\u56e0\u4e3a IEmailSender \u4e0d\u662f DI \u5bb9\u5668\u4e2d\u7684\u5df2\u77e5\u670d\u52a1\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0901.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.1 If you don\u2019t register all your required dependencies with the DI container, you\u2019ll get an exception at runtime, telling you which service wasn\u2019t registered.<br \/>\n\u56fe 9.1 \u5982\u679c\u4f60\u6ca1\u6709\u5411 DI \u5bb9\u5668\u6ce8\u518c\u6240\u6709\u9700\u8981\u7684\u4f9d\u8d56\u9879\uff0c\u4f60\u4f1a\u5728\u8fd0\u884c\u65f6\u6536\u5230\u4e00\u4e2a\u5f02\u5e38\uff0c\u544a\u8bc9\u4f60\u54ea\u4e2a\u670d\u52a1\u6ca1\u6709\u6ce8\u518c\u3002<\/p>\n<p>To configure the application completely, you need to register an IEmailSender implementation and all its dependencies with the DI container, as shown in figure 9.2.<\/p>\n<p>\u8981\u5b8c\u5168\u914d\u7f6e\u5e94\u7528\u7a0b\u5e8f\uff0c\u60a8\u9700\u8981\u5411 DI \u5bb9\u5668\u6ce8\u518c\u4e00\u4e2a IEmailSender \u5b9e\u73b0\u53ca\u5176\u6240\u6709\u4f9d\u8d56\u9879\uff0c\u5982\u56fe 9.2 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0902.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.2 Configuring the DI container in your application involves telling it what type to use when a given service is requested, such as \u201cUse EmailSender when IEmailSender is required.\u201d<br \/>\n\u56fe 9.2 \u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u914d\u7f6e DI \u5bb9\u5668\u5305\u62ec\u544a\u8bc9\u5b83\u5728\u8bf7\u6c42\u7ed9\u5b9a\u670d\u52a1\u65f6\u4f7f\u7528\u4ec0\u4e48\u7c7b\u578b\uff0c\u4f8b\u5982\u201c\u5f53\u9700\u8981 IEmailSender \u65f6\u4f7f\u7528 EmailSender\u201d\u3002<\/p>\n<p>Configuring DI consists of making a series of statements about the services in your app, such as the following:<\/p>\n<p>\u914d\u7f6e DI \u5305\u62ec\u5bf9\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u670d\u52a1\u8fdb\u884c\u4e00\u7cfb\u5217\u58f0\u660e\uff0c\u4f8b\u5982\uff1a<\/p>\n<ul>\n<li>\n<p>When a service requires IEmailSender, use an instance of EmailSender.<br \/>\n\u5f53\u670d\u52a1\u9700\u8981 IEmailSender \u65f6\uff0c\u8bf7\u4f7f\u7528 EmailSender \u7684\u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<li>\n<p>When a service requires NetworkClient, use an instance of NetworkClient.<br \/>\n\u5f53\u670d\u52a1\u9700\u8981 NetworkClient \u65f6\uff0c\u8bf7\u4f7f\u7528 NetworkClient \u7684\u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<li>\n<p>When a service requires MessageFactory, use an instance of MessageFactory.<br \/>\n\u5f53\u670d\u52a1\u9700\u8981 MessageFactory \u65f6\uff0c\u8bf7\u4f7f\u7528 MessageFactory \u7684\u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>Note<\/strong> You\u2019ll also need to register the EmailServerSettings object with the DI container. We\u2019ll do that slightly differently in section 9.2.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u60a8\u8fd8\u9700\u8981\u5411 DI \u5bb9\u5668\u6ce8\u518c EmailServerSettings \u5bf9\u8c61\u3002\u6211\u4eec\u5c06\u5728 9.2 \u8282\u4e2d\u7565\u5fae\u4e0d\u540c\u5730\u6267\u884c\u6b64\u4f5c\u3002<\/p>\n<p>These statements are made by calling various Add<em> methods on the IServiceCollection exposed as the WebApplicationBuilder.Services property. Each Add<\/em> method provides three pieces of information to the DI container:<\/p>\n<p>\u8fd9\u4e9b\u8bed\u53e5\u662f\u901a\u8fc7\u5bf9\u4f5c\u4e3a WebApplicationBuilder.Services \u5c5e\u6027\u516c\u5f00\u7684 IServiceCollection \u8c03\u7528\u5404\u79cd Add<em> \u65b9\u6cd5\u6765\u8fdb\u884c\u7684\u3002\u6bcf\u4e2a Add<\/em> \u65b9\u6cd5\u90fd\u5411 DI \u5bb9\u5668\u63d0\u4f9b\u4e09\u6761\u4fe1\u606f\uff1a<\/p>\n<ul>\n<li>\n<p>Service type\u2014TService. This class or interface will be requested as a dependency. It\u2019s often an interface, such as IEmailSender, but sometimes a concrete type, such as NetworkClient or MessageFactory.<br \/>\n\u670d\u52a1\u7c7b\u578b \u2014 TService\u3002\u6b64\u7c7b\u6216\u63a5\u53e3\u5c06\u4f5c\u4e3a\u4f9d\u8d56\u9879\u8bf7\u6c42\u3002\u5b83\u901a\u5e38\u662f\u4e00\u4e2a\u63a5\u53e3\uff0c\u5982 IEmailSender\uff0c\u4f46\u6709\u65f6\u662f\u4e00\u4e2a\u5177\u4f53\u7c7b\u578b\uff0c\u5982 NetworkClient \u6216 MessageFactory\u3002<\/p>\n<\/li>\n<li>\n<p>Implementation type\u2014TService or TImplementation. The container should create this class to fulfill the dependency. It must be a concrete type, such as EmailSender. It may be the same as the service type, as for NetworkClient and MessageFactory.<br \/>\n\u5b9e\u73b0\u7c7b\u578b - TService \u6216 TImplementation\u3002\u5bb9\u5668\u5e94\u521b\u5efa\u6b64\u7c7b\u4ee5\u6ee1\u8db3\u4f9d\u8d56\u9879\u3002\u5b83\u5fc5\u987b\u662f\u5177\u4f53\u7c7b\u578b\uff0c\u4f8b\u5982 EmailSender\u3002\u5b83\u53ef\u80fd\u4e0e NetworkClient \u548c MessageFactory \u7684\u670d\u52a1\u7c7b\u578b\u76f8\u540c\u3002<\/p>\n<\/li>\n<li>\n<p>Lifetime\u2014transient, singleton, or scoped. The lifetime defines how long an instance of the service should be used by the DI container. I discuss lifetimes in detail in section 9.4.<br \/>\nDefinition<br \/>\n\u751f\u5b58\u671f - \u77ac\u6001\u3001\u5355\u4f8b\u6216\u8303\u56f4\u3002\u751f\u5b58\u671f\u5b9a\u4e49 DI \u5bb9\u5668\u5e94\u4f7f\u7528\u670d\u52a1\u5b9e\u4f8b\u7684\u65f6\u95f4\u3002\u6211\u5728 9.4 \u8282\u4e2d\u8be6\u7ec6\u8ba8\u8bba\u4e86\u5bff\u547d\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>DEFINITION<\/strong> A concrete type is a type that can be created, such as a standard class or struct. It contrasts with a type such as an interface or an abstract class, which can\u2019t be created directly.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u5177\u4f53\u7c7b\u578b\u662f\u53ef\u4ee5\u521b\u5efa\u7684\u7c7b\u578b\uff0c\u4f8b\u5982\u6807\u51c6\u7c7b\u6216\u7ed3\u6784\u3002\u5b83\u4e0e interface \u6216\u62bd\u8c61\u7c7b\u7b49\u7c7b\u578b\u5f62\u6210\u5bf9\u6bd4\uff0c\u540e\u8005\u65e0\u6cd5\u76f4\u63a5\u521b\u5efa\u3002<\/p>\n<p>Listing 9.3 shows how you can configure EmailSender and its dependencies in your application by using three methods: <code>AddScoped&lt;TService&gt;<\/code>, <code>AddSingleton&lt;TService&gt;<\/code>, and <code>AddScoped&lt;TService, TImplementation&gt;<\/code>. This code tells the DI container how to create each of the TService instances when they\u2019re required and which lifetime to use.<\/p>\n<p>\u6e05\u5355 9.3 \u5c55\u793a\u4e86\u5982\u4f55\u4f7f\u7528\u4e09\u79cd\u65b9\u6cd5\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u914d\u7f6e EmailSender \u53ca\u5176\u4f9d\u8d56\u9879\uff1a<code>AddScoped&lt;TService&gt;<\/code>\u3001<code>AddSingleton&lt;TService&gt;<\/code> \u548c <code>AddScoped&lt;TService\u3001TImplementation&gt;<\/code>\u3002\u6b64\u4ee3\u7801\u544a\u8bc9 DI \u5bb9\u5668\u5982\u4f55\u5728\u9700\u8981\u65f6\u521b\u5efa\u6bcf\u4e2a TService \u5b9e\u4f8b\u4ee5\u53ca\u8981\u4f7f\u7528\u7684\u751f\u547d\u5468\u671f\u3002<\/p>\n<p>Listing 9.3 Registering services with the DI container<br \/>\n\u6e05\u5355 9.3 \u4f7f\u7528 DI \u5bb9\u5668\u6ce8\u518c\u670d\u52a1<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddScoped&lt;IEmailSender, EmailSender&gt;(); \u2776\nbuilder.Services.AddScoped&lt;NetworkClient&gt;(); \u2777\nbuilder.Services.AddSingleton&lt;MessageFactory&gt;(); \u2778\n\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/register\/{username}&quot;, RegisterUser);\n\napp.Run();\n\nstring RegisterUser(string username, IEmailSender emailSender)\n{\n    emailSender.SendEmail(username);\n    return $&quot;Email sent to {username}!&quot;;\n}<\/code><\/pre>\n<p>\u2776 Whenever you require an IEmailSender, use EmailSender.<br \/>\n\u6bcf\u5f53\u9700\u8981 IEmailSender \u65f6\uff0c\u8bf7\u4f7f\u7528 EmailSender\u3002<\/p>\n<p>\u2777 Whenever you require a NetworkClient, use NetworkClient.<br \/>\n\u6bcf\u5f53\u60a8\u9700\u8981 NetworkClient \u65f6\uff0c\u8bf7\u4f7f\u7528 NetworkClient\u3002<\/p>\n<p>\u2778 Whenever you require a MessageFactory, use MessageFactory.<br \/>\n\u6bcf\u5f53\u4f60\u9700\u8981 MessageFactory \u65f6\uff0c\u8bf7\u4f7f\u7528 MessageFactory\u3002<\/p>\n<p>That\u2019s all there is to DI! It may seem a little bit like magic, but you\u2019re simply giving the container instructions for making all the parts. You give it a recipe for cooking the chili, shred the lettuce, and grate the cheese, so when you ask for a burrito, it can put all the parts together and hand you your meal!<\/p>\n<p>\u8fd9\u5c31\u662f DI \u7684\u5168\u90e8\u5185\u5bb9\uff01\u8fd9\u53ef\u80fd\u770b\u8d77\u6765\u6709\u70b9\u50cf\u9b54\u672f\uff0c\u4f46\u60a8\u53ea\u662f\u5728\u7ed9\u5bb9\u5668\u63d0\u4f9b\u5236\u4f5c\u6240\u6709\u90e8\u4ef6\u7684\u8bf4\u660e\u3002\u4f60\u7ed9\u5b83\u4e00\u4e2a\u70f9\u996a\u8fa3\u6912\u3001\u5207\u788e\u751f\u83dc\u548c\u78e8\u788e\u5976\u916a\u7684\u98df\u8c31\uff0c\u8fd9\u6837\u5f53\u4f60\u8981\u58a8\u897f\u54e5\u5377\u997c\u65f6\uff0c\u5b83\u53ef\u4ee5\u628a\u6240\u6709\u90e8\u5206\u653e\u5728\u4e00\u8d77\uff0c\u7136\u540e\u628a\u4f60\u7684\u996d\u83dc\u9012\u7ed9\u4f60\uff01<\/p>\n<p><strong>Note<\/strong> Under the hood, the built-in ASP.NET Core DI container uses optimized reflection to create dependencies, but different DI containers may use other approaches. The Add<em> APIs are the only way to register dependencies with the built-in container; there\u2019s no support for using external configuration files to configure the container, for example.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5728\u540e\u53f0\uff0c\u5185\u7f6e\u7684 ASP.NET Core DI \u5bb9\u5668\u4f7f\u7528\u4f18\u5316\u7684\u53cd\u5c04\u6765\u521b\u5efa\u4f9d\u8d56\u9879\uff0c\u4f46\u4e0d\u540c\u7684 DI \u5bb9\u5668\u53ef\u80fd\u4f1a\u4f7f\u7528\u5176\u4ed6\u65b9\u6cd5\u3002Add<\/em> API \u662f\u5411\u5185\u7f6e\u5bb9\u5668\u6ce8\u518c\u4f9d\u8d56\u9879\u7684\u552f\u4e00\u65b9\u6cd5;\u4f8b\u5982\uff0c\u4e0d\u652f\u6301\u4f7f\u7528\u5916\u90e8\u914d\u7f6e\u6587\u4ef6\u6765\u914d\u7f6e\u5bb9\u5668\u3002<\/p>\n<p>The service type and implementation type are the same for NetworkClient and MessageFactory, so there\u2019s no need to specify the same type twice in the AddScoped method\u2014hence, the slightly simpler signature.<\/p>\n<p>NetworkClient \u548c MessageFactory \u7684\u670d\u52a1\u7c7b\u578b\u548c\u5b9e\u73b0\u7c7b\u578b\u76f8\u540c\uff0c\u56e0\u6b64\u65e0\u9700\u5728 AddScoped \u65b9\u6cd5\u4e2d\u4e24\u6b21\u6307\u5b9a\u76f8\u540c\u7684\u7c7b\u578b\uff0c\u56e0\u6b64\u7b7e\u540d\u7a0d\u5fae\u7b80\u5355\u4e00\u4e9b\u3002<\/p>\n<p><strong>Note<\/strong> The EmailSender instance is registered only as an IEmailSender, so you can\u2019t resolve it by requesting the specific EmailSender implementation; you must use the IEmailSender interface.<br \/>\n<strong>\u6ce8\u610f<\/strong> EmailSender \u5b9e\u4f8b\u4ec5\u6ce8\u518c\u4e3a IEmailSender\uff0c\u56e0\u6b64\u60a8\u65e0\u6cd5\u901a\u8fc7\u8bf7\u6c42\u7279\u5b9a\u7684 EmailSender \u5b9e\u73b0\u6765\u89e3\u6790\u5b83;\u60a8\u5fc5\u987b\u4f7f\u7528 IEmailSender \u63a5\u53e3\u3002<\/p>\n<p>These generic methods aren\u2019t the only ways to register services with the container. You can also provide objects directly or by using lambdas, as you\u2019ll see in section 9.2.<\/p>\n<p>\u8fd9\u4e9b\u6cdb\u578b\u65b9\u6cd5\u5e76\u4e0d\u662f\u5411\u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u7684\u552f\u4e00\u65b9\u6cd5\u3002\u60a8\u4e5f\u53ef\u4ee5\u76f4\u63a5\u6216\u4f7f\u7528 lambda \u63d0\u4f9b\u5bf9\u8c61\uff0c\u5982\u7b2c 9.2 \u8282\u6240\u793a\u3002<\/p>\n<h2>9.2 Registering services using objects and lambdas<\/h2>\n<h2>9.2 \u4f7f\u7528\u5bf9\u8c61\u548c lambda \u6ce8\u518c\u670d\u52a1<\/h2>\n<p>As I mentioned in section 9.1, I didn\u2019t quite register all the services required by EmailSender. In the previous examples, NetworkClient depends on EmailServerSettings, which you\u2019ll also need to register with the DI container for your project to run without exceptions.<\/p>\n<p>\u6b63\u5982\u6211\u5728 9.1 \u8282\u4e2d\u63d0\u5230\u7684\uff0c\u6211\u6ca1\u6709\u5b8c\u5168\u6ce8\u518c EmailSender \u6240\u9700\u7684\u6240\u6709\u670d\u52a1\u3002\u5728\u524d\u9762\u7684\u793a\u4f8b\u4e2d\uff0cNetworkClient \u4f9d\u8d56\u4e8e EmailServerSettings\uff0c\u60a8\u8fd8\u9700\u8981\u5411 DI \u5bb9\u5668\u6ce8\u518c\u5b83\uff0c\u4ee5\u4fbf\u60a8\u7684\u9879\u76ee\u80fd\u591f\u65e0\u5f02\u5e38\u5730\u8fd0\u884c\u3002<\/p>\n<p>I avoided registering this object in the preceding example because you have to take a slightly different approach. The preceding Add* methods use generics to specify the Type of the class to register, but they don\u2019t give any indication of how to construct an instance of that type. Instead, the container makes several assumptions that you have to adhere to:<\/p>\n<p>\u5728\u524d\u9762\u7684\u793a\u4f8b\u4e2d\uff0c\u6211\u907f\u514d\u6ce8\u518c\u6b64\u5bf9\u8c61\uff0c\u56e0\u4e3a\u60a8\u5fc5\u987b\u91c7\u7528\u7565\u6709\u4e0d\u540c\u7684\u65b9\u6cd5\u3002\u524d\u9762\u7684 Add* \u65b9\u6cd5\u4f7f\u7528\u6cdb\u578b\u6307\u5b9a\u8981\u6ce8\u518c\u7684\u7c7b\u7684 Type\uff0c\u4f46\u5b83\u4eec\u6ca1\u6709\u6307\u793a\u5982\u4f55\u6784\u9020\u8be5\u7c7b\u578b\u7684\u5b9e\u4f8b\u3002\u76f8\u53cd\uff0c\u5bb9\u5668\u4f1a\u505a\u51fa\u51e0\u4e2a\u60a8\u5fc5\u987b\u9075\u5b88\u7684\u5047\u8bbe\uff1a<\/p>\n<ul>\n<li>\n<p>The class must be a concrete type.<br \/>\n\u8be5\u7c7b\u5fc5\u987b\u662f\u5177\u4f53\u7c7b\u578b\u3002<\/p>\n<\/li>\n<li>\n<p>The class must have only a single relevant constructor that the container can use.<br \/>\n\u8be5\u7c7b\u5fc5\u987b\u53ea\u6709\u4e00\u4e2a\u5bb9\u5668\u53ef\u4ee5\u4f7f\u7528\u7684\u76f8\u5173\u6784\u9020\u51fd\u6570\u3002<\/p>\n<\/li>\n<li>\n<p>For a constructor to be valid, all constructor arguments must be registered with the container or must be arguments with a default value.<br \/>\n\u8981\u4f7f\u6784\u9020\u51fd\u6570\u76f8\u5173\uff0c\u6240\u6709\u6784\u9020\u51fd\u6570\u53c2\u6570\u90fd\u5fc5\u987b\u6ce8\u518c\u5230\u5bb9\u5668\u4e2d\uff0c\u6216\u8005\u5fc5\u987b\u662f\u5177\u6709\u9ed8\u8ba4\u503c\u7684\u53c2\u6570\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>Note<\/strong> These limitations apply to the simple built-in DI container. If you choose to use a third-party container in your app, it may have a different set of limitations.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u8fd9\u4e9b\u9650\u5236\u9002\u7528\u4e8e\u7b80\u5355\u7684\u5185\u7f6e DI \u5bb9\u5668\u3002\u5982\u679c\u60a8\u9009\u62e9\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4f7f\u7528\u7b2c\u4e09\u65b9\u5bb9\u5668\uff0c\u5219\u5b83\u53ef\u80fd\u5177\u6709\u4e00\u7ec4\u4e0d\u540c\u7684\u9650\u5236\u3002<\/p>\n<p>The EmailServerSettings record doesn\u2019t meet these requirements, as it requires you to provide a Host and Port in the constructor, which are a string and int, respectively, without default values:<\/p>\n<p>EmailServerSettings \u8bb0\u5f55\u4e0d\u6ee1\u8db3\u8fd9\u4e9b\u8981\u6c42\uff0c\u56e0\u4e3a\u5b83\u8981\u6c42\u60a8\u5728\u6784\u9020\u51fd\u6570\u4e2d\u63d0\u4f9b Host \u548c Port\uff0c\u5b83\u4eec\u5206\u522b\u662f string \u548c int\uff0c\u6ca1\u6709\u9ed8\u8ba4\u503c\uff1a<\/p>\n<pre><code>public record EmailServerSettings(string Host, int Port);<\/code><\/pre>\n<p>You can\u2019t register these primitive types in the container. It would be weird to say \u201cFor every string constructor argument, in any type, use the &quot;smtp.server.com&quot; value.\u201d<\/p>\n<p>\u4e0d\u80fd\u5728\u5bb9\u5668\u4e2d\u6ce8\u518c\u8fd9\u4e9b\u57fa\u5143\u7c7b\u578b\u3002\u5982\u679c\u8bf4\u201c\u5bf9\u4e8e\u4efb\u4f55\u7c7b\u578b\u7684\u6bcf\u4e2a\u5b57\u7b26\u4e32\u6784\u9020\u51fd\u6570\u53c2\u6570\uff0c\u8bf7\u4f7f\u7528 \u201dsmtp.server.com\u201c \u503c\uff0c\u90a3\u5c31\u5f88\u5947\u602a\u4e86\u3002<\/p>\n<p>Instead, you can create an instance of the EmailServerSettings object yourself and provide that to the container, as shown in the following listing. The container uses the preconstructed object whenever an instance of the EmailServerSettings object is required.<\/p>\n<p>\u76f8\u53cd\uff0c\u60a8\u53ef\u4ee5\u81ea\u5df1\u521b\u5efa EmailServerSettings \u5bf9\u8c61\u7684\u5b9e\u4f8b\uff0c\u5e76\u5c06\u5176\u63d0\u4f9b\u7ed9\u5bb9\u5668\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002\u6bcf\u5f53\u9700\u8981 EmailServerSettings \u5bf9\u8c61\u7684\u5b9e\u4f8b\u65f6\uff0c\u5bb9\u5668\u90fd\u4f1a\u4f7f\u7528\u9884\u6784\u9020\u7684\u5bf9\u8c61\u3002<\/p>\n<p>Listing 9.4 Providing an object instance when registering services<br \/>\n\u793a\u4f8b 9.4 \u5728\u6ce8\u518c\u670d\u52a1\u65f6\u63d0\u4f9b\u5bf9\u8c61\u5b9e\u4f8b<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddScoped&lt;IEmailSender, EmailSender&gt;();\nbuilder.Services.AddScoped&lt;NetworkClient&gt;();\nbuilder.Services.AddSingleton&lt;MessageFactory&gt;();\nbuilder.Services.AddSingleton(\n        new EmailServerSettings \u2776\n        ( \u2776\n            Host: &quot;smtp.server.com&quot;, \u2776\n            Port: 25 \u2776\n        )); \u2776\n\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/register\/{username}&quot;, RegisterUser);\n\napp.Run();<\/code><\/pre>\n<p>\u2776 This instance of EmailServerSettings will be used whenever an instance is<br \/>\nrequired.<br \/>\n\u6bcf\u5f53\u9700\u8981\u5b9e\u4f8b\u65f6\uff0c\u90fd\u4f1a\u4f7f\u7528\u8fd9\u4e2a EmailServerSettings \u5b9e\u4f8b\u3002<\/p>\n<p>This code works fine if you want to have only a single instance of EmailServerSettings in your application; the same object will be shared everywhere. But what if you want to create a new object each time one is requested?<\/p>\n<p>\u5982\u679c\u60a8\u53ea\u60f3\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u53ea\u6709\u4e00\u4e2a EmailServerSettings \u5b9e\u4f8b\uff0c\u5219\u6b64\u4ee3\u7801\u53ef\u4ee5\u6b63\u5e38\u5de5\u4f5c;\u540c\u4e00\u5bf9\u8c61\u5c06\u5728\u4efb\u4f55\u5730\u65b9\u5171\u4eab\u3002\u4f46\u662f\uff0c\u5982\u679c\u60a8\u60f3\u5728\u6bcf\u6b21\u8bf7\u6c42\u65f6\u90fd\u521b\u5efa\u4e00\u4e2a\u65b0\u5bf9\u8c61\uff0c\u8be5\u600e\u4e48\u529e\uff1f<\/p>\n<p><strong>Note<\/strong> When the same object is used whenever it\u2019s requested, it\u2019s known as a singleton. If you create an object and pass it to the container, it\u2019s always registered as a singleton. You can also register any class using the <code>AddSingleton&lt;T&gt;()<\/code> method, and the container will use only one instance throughout your application. I discuss singletons and other lifetimes in detail in section 9.4. The lifetime is how long the DI container should use a given object to fulfill a service\u2019s dependencies.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5f53\u8bf7\u6c42\u65f6\u4f7f\u7528\u76f8\u540c\u7684\u5bf9\u8c61\u65f6\uff0c\u5b83\u79f0\u4e3a\u5355\u4e00\u5b9e\u4f8b\u3002\u5982\u679c\u60a8\u521b\u5efa\u4e00\u4e2a\u5bf9\u8c61\u5e76\u5c06\u5176\u4f20\u9012\u7ed9\u5bb9\u5668\uff0c\u5219\u5b83\u59cb\u7ec8\u6ce8\u518c\u4e3a\u5355\u4e00\u5b9e\u4f8b\u3002\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 <code>AddSingleton&lt;T&gt;\uff08\uff09<\/code> \u65b9\u6cd5\u6ce8\u518c\u4efb\u4f55\u7c7b\uff0c\u5e76\u4e14\u5bb9\u5668\u5c06\u5728\u6574\u4e2a\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4ec5\u4f7f\u7528\u4e00\u4e2a\u5b9e\u4f8b\u3002\u6211\u5728 9.4 \u8282\u4e2d\u8be6\u7ec6\u8ba8\u8bba\u4e86 singletons \u548c\u5176\u4ed6\u751f\u547d\u5468\u671f\u3002\u751f\u547d\u5468\u671f\u662f DI \u5bb9\u5668\u5e94\u8be5\u4f7f\u7528\u7ed9\u5b9a\u5bf9\u8c61\u6765\u5b9e\u73b0\u670d\u52a1\u7684\u4f9d\u8d56\u9879\u7684\u65f6\u95f4\u3002<\/p>\n<p>Instead of providing a single instance that the container will always use, you can provide a function that the container invokes when it needs an instance of the type, as shown in figure 9.3.<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0903.png\" alt=\"alt text\" \/><br \/>\n\u4f60\u53ef\u4ee5\u63d0\u4f9b\u4e00\u4e2a\u51fd\u6570\uff0c\u5f53\u5bb9\u5668\u9700\u8981\u8be5\u7c7b\u578b\u7684\u5b9e\u4f8b\u65f6\uff0c\u4f60\u53ef\u4ee5\u63d0\u4f9b\u4e00\u4e2a\u51fd\u6570\uff0c\u800c\u4e0d\u662f\u63d0\u4f9b\u5bb9\u5668\u5c06\u59cb\u7ec8\u4f7f\u7528\u7684\u5355\u4e2a\u5b9e\u4f8b\uff0c\u5982\u56fe 9.3 \u6240\u793a\u3002<\/p>\n<p>Figure 9.3 You can register a function with the DI container that will be invoked whenever a new instance of a service is required.<br \/>\n\u56fe 9.3 \u60a8\u53ef\u4ee5\u5728 DI \u5bb9\u5668\u4e2d\u6ce8\u518c\u4e00\u4e2a\u51fd\u6570\uff0c\u6bcf\u5f53\u9700\u8981\u670d\u52a1\u7684\u65b0\u5b9e\u4f8b\u65f6\uff0c\u8be5\u51fd\u6570\u5c06\u88ab\u8c03\u7528\u3002<\/p>\n<p><strong>Note<\/strong> Figure 9.3 is an example of the factory pattern, in which you define how a type is created. Note that the factory functions must be synchronous; you can\u2019t create types asynchronously by (for example) using async.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u56fe 9.3 \u662f\u5de5\u5382\u6a21\u5f0f\u7684\u4e00\u4e2a\u793a\u4f8b\uff0c\u60a8\u53ef\u4ee5\u5728\u5176\u4e2d\u5b9a\u4e49\u5982\u4f55\u521b\u5efa\u7c7b\u578b\u3002\u8bf7\u6ce8\u610f\uff0c\u5de5\u5382\u51fd\u6570\u5fc5\u987b\u662f\u540c\u6b65\u7684;\u60a8\u4e0d\u80fd \uff08\u4f8b\u5982\uff09 \u4f7f\u7528 async \u5f02\u6b65\u521b\u5efa\u7c7b\u578b\u3002<\/p>\n<p>The easiest way to register a service using the factory pattern is with a lambda function (an anonymous delegate), in which the container creates a new EmailServerSettings object whenever it\u2019s needed, as shown in the following listing.<\/p>\n<p>\u4f7f\u7528\u5de5\u5382\u6a21\u5f0f\u6ce8\u518c\u670d\u52a1\u7684\u6700\u7b80\u5355\u65b9\u6cd5\u662f\u4f7f\u7528 lambda \u51fd\u6570\uff08\u533f\u540d\u59d4\u6258\uff09\uff0c\u5176\u4e2d\u5bb9\u5668\u5728\u9700\u8981\u65f6\u521b\u5efa\u65b0\u7684 EmailServerSettings \u5bf9\u8c61\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<p>Listing 9.5 Using a lambda factory function to register a dependency<br \/>\n\u6e05\u5355 9.5 \u4f7f\u7528 lambda \u5de5\u5382\u51fd\u6570\u6ce8\u518c\u4f9d\u8d56\u9879<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddScoped&lt;IEmailSender, EmailSender&gt;();\nbuilder.Services.AddScoped&lt;NetworkClient&gt;();\nbuilder.Services.AddSingleton&lt;MessageFactory&gt;();\n\nbuilder.Services.AddScoped( \u2776\n    provider =&gt; \u2777\n        new EmailServerSettings \u2778\n        ( \u2778\n            Host: &quot;smtp.server.com&quot;, \u2778\n            Port: 25 \u2778\n        )); \u2778\n\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/register\/{username}&quot;, RegisterUser);\n\napp.Run();\n<\/code><\/pre>\n<p>\u2776 Because you\u2019re providing a function to create the object, you aren\u2019t restricted to a singleton.<br \/>\n\u56e0\u4e3a\u4f60\u63d0\u4f9b\u4e86\u4e00\u4e2a\u51fd\u6570\u6765\u521b\u5efa\u5bf9\u8c61\uff0c\u6240\u4ee5\u4f60\u4e0d\u9650\u4e8e\u5355\u4e00\u5b9e\u4f8b\u3002<\/p>\n<p>\u2777 The lambda is provided an instance of IServiceProvider.<br \/>\nlambda \u63d0\u4f9b\u4e86 IServiceProvider \u7684\u5b9e\u4f8b\u3002<\/p>\n<p>\u2778 The constructor is called every time an EmailServerSettings object is required instead of only once.<br \/>\n\u6bcf\u6b21\u9700\u8981 EmailServerSettings \u5bf9\u8c61\u65f6\u90fd\u4f1a\u8c03\u7528\u6784\u9020\u51fd\u6570\uff0c\u800c\u4e0d\u4ec5\u4ec5\u662f\u4e00\u6b21\u3002<\/p>\n<p>In this example, I changed the lifetime of the created EmailServerSettings object to scoped instead of singleton and provided a factory lambda function that returns a new EmailServerSettings object. Every time the container requires a new EmailServerSettings, it executes the function and uses the new object it returns.<br \/>\n\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u6211\u5c06\u521b\u5efa\u7684 EmailServerSettings \u5bf9\u8c61\u7684\u751f\u547d\u5468\u671f\u66f4\u6539\u4e3a scoped \u800c\u4e0d\u662f singleton\uff0c\u5e76\u63d0\u4f9b\u4e86\u4e00\u4e2a\u8fd4\u56de\u65b0 EmailServerSettings \u5bf9\u8c61\u7684\u5de5\u5382 lambda \u51fd\u6570\u3002\u6bcf\u6b21\u5bb9\u5668\u9700\u8981\u65b0\u7684 EmailServerSettings \u65f6\uff0c\u5b83\u90fd\u4f1a\u6267\u884c\u8be5\u51fd\u6570\u5e76\u4f7f\u7528\u5b83\u8fd4\u56de\u7684\u65b0\u5bf9\u8c61\u3002<\/p>\n<p>When you use a lambda to register your services, you\u2019re given an IServiceProvider instance at runtime, called provider in listing 9.5. This instance is the public API of the DI container itself, which exposes the <code>GetService&lt;T&gt;()<\/code> and <code>GetRequiredService&lt;T&gt;()<\/code> extension methods you saw in chapter 8. If you need to obtain dependencies to create an instance of your service, you can reach into the container at runtime in this way, but you should avoid doing so if possible.<br \/>\n\u5f53\u60a8\u4f7f\u7528 lambda \u6ce8\u518c\u670d\u52a1\u65f6\uff0c\u5728\u8fd0\u884c\u65f6\u4f1a\u4e3a\u60a8\u63d0\u4f9b\u4e00\u4e2a IServiceProvider \u5b9e\u4f8b\uff0c\u5728\u5217\u8868 9.5 \u4e2d\u79f0\u4e3a provider\u3002\u6b64\u5b9e\u4f8b\u662f DI \u5bb9\u5668\u672c\u8eab\u7684\u516c\u5171 API\uff0c\u5b83\u516c\u5f00\u4e86 <code>GetService&lt;T&gt;\uff08\uff09<\/code> \u548c <code>GetRequiredService&lt;T&gt;\uff08\uff09<\/code> \u6269\u5c55\u65b9\u6cd5\u5728\u7b2c 8 \u7ae0\u4e2d\u3002\u5982\u679c\u60a8\u9700\u8981\u83b7\u53d6\u4f9d\u8d56\u9879\u6765\u521b\u5efa\u670d\u52a1\u5b9e\u4f8b\uff0c\u5219\u53ef\u4ee5\u5728\u8fd0\u884c\u65f6\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u8bbf\u95ee\u5bb9\u5668\uff0c\u4f46\u5e94\u5c3d\u53ef\u80fd\u907f\u514d\u8fd9\u6837\u505a\u3002<\/p>\n<p><strong>Tip<\/strong> Avoid calling <code>GetService&lt;T&gt;()<\/code> and <code>GetRequiredService&lt;T&gt;()<\/code> in your factory functions if possible. Instead, favor constructor injection; it\u2019s more performant and simpler to reason about.<br \/>\n<strong>\u63d0\u793a<\/strong> \u5982\u679c\u53ef\u80fd\uff0c\u8bf7\u907f\u514d\u5728\u5de5\u5382\u51fd\u6570\u4e2d\u8c03\u7528 <code>GetService&lt;T&gt;\uff08\uff09<\/code> \u548c <code>GetRequiredService&lt;T&gt;\uff08\uff09<\/code>\u3002 \u76f8\u53cd\uff0c\u652f\u6301\u6784\u9020\u51fd\u6570\u6ce8\u5165;\u5b83\u7684\u6027\u80fd\u66f4\u9ad8\uff0c\u63a8\u7406\u4e5f\u66f4\u7b80\u5355\u3002<\/p>\n<blockquote>\n<p>Open generics and dependency injection<br \/>\n\u5f00\u653e\u6cdb\u578b\u548c\u4f9d\u8d56\u9879\u6ce8\u5165<br \/>\nAs already mentioned, you couldn\u2019t use the generic registration methods with EmailServerSettings because it uses primitive dependencies (in this case, string and int) in its constructor. Neither can you use the generic registration methods to register open generics.<br \/>\n\u5982\u524d\u6240\u8ff0\uff0c\u60a8\u4e0d\u80fd\u5c06\u6cdb\u578b\u6ce8\u518c\u65b9\u6cd5\u4e0e EmailServerSettings \u4e00\u8d77\u4f7f\u7528\uff0c\u56e0\u4e3a\u5b83\u5728\u5176\u6784\u9020\u51fd\u6570\u4e2d\u4f7f\u7528\u57fa\u5143\u4f9d\u8d56\u9879\uff08\u5728\u672c\u4f8b\u4e2d\u4e3a string \u548c int\uff09\u3002\u4e5f\u4e0d\u80fd\u4f7f\u7528\u6cdb\u578b\u6ce8\u518c\u65b9\u6cd5\u6ce8\u518c\u5f00\u653e\u6cdb\u578b\u3002<br \/>\nOpen generics are types that contain a generic type parameter, such as <code>Repository &lt;T&gt;<\/code>. You normally use this sort of type to define a base behavior that you can use with multiple generic types. In the <code>Repository&lt;T&gt;<\/code> example, you might inject <code>IRepository&lt;Customer&gt;<\/code> into your services, which should inject an instance of <code>DbRepository&lt;Customer&gt;<\/code>, for example.<br \/>\n\u5f00\u653e\u6cdb\u578b\u662f\u5305\u542b\u6cdb\u578b\u7c7b\u578b\u53c2\u6570\u7684\u7c7b\u578b\uff0c\u4f8b\u5982 <code>Repository &lt;T&gt;<\/code>\u3002\u901a\u5e38\u4f7f\u7528\u8fd9\u79cd\u7c7b\u578b\u6765\u5b9a\u4e49\u53ef\u4e0e\u591a\u4e2a\u6cdb\u578b\u7c7b\u578b\u4e00\u8d77\u4f7f\u7528\u7684\u57fa\u672c\u884c\u4e3a\u3002\u5728 <code>Repository&lt;T&gt;<\/code> \u793a\u4f8b\u4e2d\uff0c\u60a8\u53ef\u4ee5\u5c06 <code>IRepository &lt;Customer&gt;<\/code> \u6ce8\u5165\u5230\u60a8\u7684\u670d\u52a1\u4e2d\uff0c\u8fd9\u5e94\u8be5\u4f1a\u6ce8\u5165 DbRepository \u7684\u5b9e\u4f8b\u4f8b\u5982\uff0c<Customer>\u3002<br \/>\nTo register these types, you must use a different overload of the Add<em> methods, as in this example:<br \/>\n\u8981\u6ce8\u518c\u8fd9\u4e9b\u7c7b\u578b\uff0c\u5fc5\u987b\u4f7f\u7528 Add<\/em> \u7684\u4e0d\u540c\u91cd\u8f7d\u65b9\u6cd5\uff0c\u5982\u4ee5\u4e0b\u793a\u4f8b\u6240\u793a\uff1a<br \/>\nbuilder.Services.AddScoped(typeof(<code>IRespository&lt;&gt;<\/code>), typeof(<code>DbRepository&lt;&gt;<\/code>));<br \/>\nThis code ensures that whenever a service constructor requires <code>IRespository&lt;T&gt;<\/code>, the container injects an instance of <code>DbRepository&lt;T&gt;<\/code>.<br \/>\n\u6b64\u4ee3\u7801\u53ef\u786e\u4fdd\u6bcf\u5f53\u670d\u52a1\u6784\u9020\u51fd\u6570\u9700\u8981<code>IRespository&lt;T&gt;<\/code> \u4e2d\uff0c\u5bb9\u5668\u4f1a\u6ce8\u5165 <code>DbRepository&lt;T&gt;<\/code> \u7684\u5b9e\u4f8b\u3002<\/p>\n<\/blockquote>\n<p>At this point, all your dependencies are registered. But your Program.cs is starting to look a little messy, isn\u2019t it? The choice is entirely down to personal preference, but I like to group my services into logical collections and create extension methods for them, as in listing 9.6. This approach creates an equivalent to the framework\u2019s AddRazorPages() extension method\u2014a nice, simple registration API. As you add more features to your app, I think you\u2019ll appreciate it too.<br \/>\n\u6b64\u65f6\uff0c\u60a8\u7684\u6240\u6709\u4f9d\u8d56\u9879\u90fd\u5df2\u6ce8\u518c\u3002\u4f46\u662f\u4f60\u7684Program.cs\u5f00\u59cb\u770b\u8d77\u6765\u6709\u70b9\u51cc\u4e71\u4e86\uff0c\u4e0d\u662f\u5417\uff1f\u9009\u62e9\u5b8c\u5168\u53d6\u51b3\u4e8e\u4e2a\u4eba\u559c\u597d\uff0c\u4f46\u6211\u559c\u6b22\u5c06\u6211\u7684\u670d\u52a1\u5206\u7ec4\u5230\u903b\u8f91\u96c6\u5408\u4e2d\uff0c\u5e76\u4e3a\u5b83\u4eec\u521b\u5efa\u6269\u5c55\u65b9\u6cd5\uff0c\u5982\u6e05\u5355 9.6 \u6240\u793a\u3002\u6b64\u65b9\u6cd5\u521b\u5efa\u4e0e\u6846\u67b6\u7684 AddRazorPages\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\u7b49\u6548\u7684 \u2014 \u4e00\u4e2a\u6f02\u4eae\u3001\u7b80\u5355\u7684\u6ce8\u518c API\u3002\u968f\u7740\u60a8\u5411\u5e94\u7528\u7a0b\u5e8f\u6dfb\u52a0\u66f4\u591a\u529f\u80fd\uff0c\u6211\u60f3\u60a8\u4e5f\u4f1a\u559c\u6b22\u5b83\u3002<\/p>\n<p>Listing 9.6 Creating an extension method to tidy up adding multiple services<br \/>\n\u6e05\u5355 9.6 \u521b\u5efa\u4e00\u4e2a\u6269\u5c55\u65b9\u6cd5\u6765\u6574\u7406\u6dfb\u52a0\u591a\u4e2a\u670d\u52a1<\/p>\n<pre><code>public static class EmailSenderServiceCollectionExtensions\n{\n    public static IServiceCollection AddEmailSender(\n        this IServiceCollection services) \u2776\n    {\n        services.AddScoped&lt;IEmailSender, EmailSender&gt;(); \u2777\n        services.AddSingleton&lt;NetworkClient&gt;(); \u2777\n        services.AddScoped&lt;MessageFactory&gt;(); \u2777\n        services.AddSingleton( \u2777\n            new EmailServerSettings \u2777\n            ( \u2777\n                host: &quot;smtp.server.com&quot;, \u2777\n                port: 25 \u2777\n            )); \u2777\n        return services; \u2778\n    }\n}<\/code><\/pre>\n<p>\u2776 Creates an extension method on IServiceCollection by using the \u201cthis\u201d keyword<br \/>\n\u4f7f\u7528\u201cthis\u201d\u5173\u952e\u5b57\u5728 IServiceCollection \u4e0a\u521b\u5efa\u6269\u5c55\u65b9\u6cd5<\/p>\n<p>\u2777 Cuts and pastes your registration code from Program.cs<br \/>\n\u4ece Program.cs \u526a\u5207\u5e76\u7c98\u8d34\u60a8\u7684\u6ce8\u518c\u7801<\/p>\n<p>\u2778 By convention, returns the IServiceCollection to allow method chaining<br \/>\n\u6309\u7167\u7ea6\u5b9a\uff0c\u8fd4\u56de IServiceCollection \u4ee5\u5141\u8bb8\u65b9\u6cd5\u94fe\u63a5<\/p>\n<p>With the preceding extension method created, the following listing shows that your startup code is much easier to grok!<\/p>\n<p>\u521b\u5efa\u4e0a\u8ff0\u6269\u5c55\u65b9\u6cd5\u540e\uff0c\u4ee5\u4e0b\u6e05\u5355\u663e\u793a\u60a8\u7684\u542f\u52a8\u4ee3\u7801\u66f4\u5bb9\u6613\u7406\u89e3\uff01<\/p>\n<p>Listing 9.7 Using an extension method to register your services<br \/>\n\u6e05\u5355 9.7 \u4f7f\u7528\u6269\u5c55\u65b9\u6cd5\u6ce8\u518c\u60a8\u7684\u670d\u52a1<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddEmailSender(); \u2776\n\nWebApplication app = builder.Build();\n\napp.MapGet(&quot;\/register\/{username}&quot;, RegisterUser);\n\napp.Run();<\/code><\/pre>\n<p>\u2776 The extension method registers all the services associated with the<br \/>\nEmailSender.<br \/>\n\u6269\u5c55\u65b9\u6cd5\u6ce8\u518c\u4e0e EmailSender \u5173\u8054\u7684\u6240\u6709\u670d\u52a1\u3002<\/p>\n<p>So far, you\u2019ve seen how to register the simple DI cases in which you have a single implementation of a service. In some scenarios, you may have multiple implementations of an interface. In section 9.3 you\u2019ll see how to register these with the container to match your requirements.<\/p>\n<p>\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u60a8\u5df2\u7ecf\u4e86\u89e3\u4e86\u5982\u4f55\u6ce8\u518c\u7b80\u5355\u7684 DI \u6848\u4f8b\uff0c\u5176\u4e2d\u60a8\u6709\u4e00\u4e2a\u670d\u52a1\u7684\u5355\u4e2a\u5b9e\u73b0\u3002\u5728\u67d0\u4e9b\u60c5\u51b5\u4e0b\uff0c\u60a8\u53ef\u80fd\u6709\u591a\u4e2a\u63a5\u53e3\u5b9e\u73b0\u3002\u5728 Section 9.3 \u4e2d\uff0c\u60a8\u5c06\u770b\u5230\u5982\u4f55\u5c06\u8fd9\u4e9b\u6ce8\u518c\u5230\u5bb9\u5668\u4e2d\u4ee5\u6ee1\u8db3\u60a8\u7684\u9700\u6c42\u3002<\/p>\n<h2>9.3 Registering a service in the container multiple times<\/h2>\n<h2>9.3 \u5728\u5bb9\u5668\u4e2d\u591a\u6b21\u6ce8\u518c\u670d\u52a1<\/h2>\n<p>One advantage of coding to interfaces is that you can create multiple implementations of a service. Suppose that you want to create a more generalized version of IEmailSender so that you can send messages via Short Message Service (SMS) or Facebook, as well as by email. You create the interface for it as follows,<br \/>\n\u5bf9\u63a5\u53e3\u8fdb\u884c\u7f16\u7801\u7684\u4e00\u4e2a\u4f18\u70b9\u662f\uff0c\u60a8\u53ef\u4ee5\u521b\u5efa\u670d\u52a1\u7684\u591a\u4e2a\u5b9e\u73b0\u3002\u5047\u8bbe\u60a8\u8981\u521b\u5efa\u66f4\u901a\u7528\u7684 IEmailSender \u7248\u672c\uff0c\u4ee5\u4fbf\u53ef\u4ee5\u901a\u8fc7\u77ed\u6d88\u606f\u670d\u52a1 \uff08SMS\uff09 \u6216 Facebook \u4ee5\u53ca\u7535\u5b50\u90ae\u4ef6\u53d1\u9001\u6d88\u606f\u3002\u60a8\u53ef\u4ee5\u6309\u5982\u4e0b\u65b9\u5f0f\u4e3a\u5176\u521b\u5efa\u63a5\u53e3\uff1a<\/p>\n<pre><code>public interface IMessageSender\n{\n    public void SendMessage(string message);\n}<\/code><\/pre>\n<p>as well as several implementations: EmailSender, SmsSender, and FacebookSender. But how do you register these implementations in the container? And how can you inject these implementations into your RegisterUser handler? The answers vary slightly, depending on whether you want to use all the implementations in your consumer or only one.<\/p>\n<p>\u4ee5\u53ca\u591a\u79cd\u5b9e\u73b0\uff1aEmailSender\u3001SmsSender \u548c FacebookSender\u3002\u4f46\u662f\u5982\u4f55\u5728\u5bb9\u5668\u4e2d\u6ce8\u518c\u8fd9\u4e9b\u5b9e\u73b0\u5462\uff1f\u5982\u4f55\u5c06\u8fd9\u4e9b\u5b9e\u73b0\u6ce8\u5165\u5230 RegisterUser \u5904\u7406\u7a0b\u5e8f\u4e2d\u5462\uff1f\u7b54\u6848\u7565\u6709\u4e0d\u540c\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u60a8\u662f\u8981\u4f7f\u7528 Consumer \u4e2d\u7684\u6240\u6709 implementations\uff0c\u8fd8\u662f\u53ea\u4f7f\u7528 one\u3002<\/p>\n<h3>9.3.1 Injecting multiple implementations of an interface<\/h3>\n<h3>9.3.1 \u6ce8\u5165\u63a5\u53e3\u7684\u591a\u4e2a\u5b9e\u73b0<\/h3>\n<p>Suppose that you want to send a message using each of the IMessageSender implementations whenever a new user registers so that they get an email, an SMS text, and a Facebook message, as shown in figure 9.4.<\/p>\n<p>\u5047\u8bbe\u60a8\u5e0c\u671b\u5728\u65b0\u7528\u6237\u6ce8\u518c\u65f6\u4f7f\u7528\u6bcf\u4e2a IMessageSender \u5b9e\u73b0\u53d1\u9001\u6d88\u606f\uff0c\u4ee5\u4fbf\u4ed6\u4eec\u6536\u5230\u7535\u5b50\u90ae\u4ef6\u3001SMS \u6587\u672c\u548c Facebook \u6d88\u606f\uff0c\u5982\u56fe 9.4 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0904.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.4 When a user registers with your application, they call the RegisterUser handler. This handler sends them an email, an SMS text, and a Facebook message using the IMessageSender classes.<br \/>\n\u56fe 9.4 \u5f53\u7528\u6237\u6ce8\u518c\u5230\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u65f6\uff0c\u4ed6\u4eec\u4f1a\u8c03\u7528 RegisterUser \u5904\u7406\u7a0b\u5e8f\u3002\u6b64\u5904\u7406\u7a0b\u5e8f\u4f7f\u7528 IMessageSender \u7c7b\u5411\u4ed6\u4eec\u53d1\u9001\u7535\u5b50\u90ae\u4ef6\u3001SMS \u6587\u672c\u548c Facebook \u6d88\u606f\u3002<\/p>\n<p>The easiest way to achieve this goal is to register all the service implementations in your DI container and have it inject one of each type into the RegisterUser endpoint handler. Then RegisterUser can use a simple foreach loop to call SendMessage() on each implementation, as shown in figure 9.5.<\/p>\n<p>\u5b9e\u73b0\u6b64\u76ee\u6807\u7684\u6700\u7b80\u5355\u65b9\u6cd5\u662f\u5728 DI \u5bb9\u5668\u4e2d\u6ce8\u518c\u6240\u6709\u670d\u52a1\u5b9e\u73b0\uff0c\u5e76\u8ba9\u5b83\u5c06\u6bcf\u79cd\u7c7b\u578b\u4e2d\u7684\u4e00\u4e2a\u6ce8\u5165\u5230 RegisterUser \u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u3002\u7136\u540e RegisterUser \u53ef\u4ee5\u4f7f\u7528\u4e00\u4e2a\u7b80\u5355\u7684 foreach \u5faa\u73af\u5728\u6bcf\u4e2a\u5b9e\u73b0\u4e0a\u8c03\u7528 SendMessage\uff08\uff09\uff0c\u5982\u56fe 9.5 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0905.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.5 You can register multiple implementations of a service with the DI container, such as IEmailSender in this example. You can retrieve an instance of each of these implementations by requiring IEnumerable<IMessageSender> in the RegisterUser handler.<br \/>\n\u56fe 9.5 \u4f60\u53ef\u4ee5\u5411 DI \u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u7684\u591a\u4e2a\u5b9e\u73b0\uff0c\u4f8b\u5982\u672c\u4f8b\u4e2d\u7684 IEmailSender\u3002\u60a8\u53ef\u4ee5\u901a\u8fc7\u5728 RegisterUser \u5904\u7406\u7a0b\u5e8f\u4e2d\u8981\u6c42 IEnumerable<IMessageSender> \u6765\u68c0\u7d22\u8fd9\u4e9b\u5b9e\u73b0\u4e2d\u7684\u6bcf\u4e00\u4e2a\u7684\u5b9e\u4f8b\u3002<\/p>\n<p>You register multiple implementations of the same service with a DI container in exactly the same way as for single implementations, using the Add* extension methods as in this example:<\/p>\n<p>\u4f7f\u7528 Add<em> \u6269\u5c55\u65b9\u6cd5\uff0c\u4f7f\u7528 Add<\/em> \u6269\u5c55\u65b9\u6cd5\uff0c\u4ee5\u4e0e\u5355\u4e2a\u5b9e\u73b0\u5b8c\u5168\u76f8\u540c\u7684\u65b9\u5f0f\u5411 DI \u5bb9\u5668\u6ce8\u518c\u540c\u4e00\u670d\u52a1\u7684\u591a\u4e2a\u5b9e\u73b0\uff0c\u5982\u4e0b\u4f8b\u6240\u793a\uff1a<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nbuilder.Services.AddScoped&lt;IMessageSender, EmailSender&gt;();\nbuilder.Services.AddScoped&lt;IMessageSender, SmsSender&gt;();\nbuilder.Services.AddScoped&lt;IMessageSender, FacebookSender&gt;();<\/code><\/pre>\n<p>Then you can inject <code>IEnumerable&lt;IMessageSender&gt;<\/code> into RegisterUser, as shown in listing 9.8. The container injects an array of IMessageSender containing one of each of the implementations you have registered, in the same order as you registered them. Then you can use a standard foreach loop and call SendMessage on each implementation.<\/p>\n<p>\u7136\u540e\u4f60\u53ef\u4ee5\u5c06 <code>IEnumerable&lt;IMessageSender&gt;<\/code> \u6ce8\u5165\u5230 RegisterUser \u4e2d\uff0c\u5982\u6e05\u5355 9.8 \u6240\u793a\u3002\u8be5\u5bb9\u5668\u6ce8\u5165\u4e00\u4e2a IMessageSender \u6570\u7ec4\uff0c\u5176\u4e2d\u5305\u542b\u60a8\u5df2\u6ce8\u518c\u7684\u6bcf\u4e2a\u5b9e\u73b0\u4e4b\u4e00\uff0c\u5176\u987a\u5e8f\u4e0e\u6ce8\u518c\u5b83\u4eec\u7684\u987a\u5e8f\u76f8\u540c\u3002\u7136\u540e\uff0c\u60a8\u53ef\u4ee5\u4f7f\u7528\u6807\u51c6 foreach \u5faa\u73af\u5e76\u5728\u6bcf\u4e2a\u5b9e\u73b0\u4e0a\u8c03\u7528 SendMessage\u3002<\/p>\n<p>Listing 9.8 Injecting multiple implementations of a service into an endpoint<br \/>\n\u6e05\u5355 9.8 \u5c06\u670d\u52a1\u7684\u591a\u4e2a\u5b9e\u73b0\u6ce8\u5165\u5230\u7aef\u70b9\u4e2d<\/p>\n<pre><code>string RegisterUser(\n    string username,\n    IEnumerable&lt;IMessageSender&gt; senders) \u2776\n    {\n    foreach(var sender in senders) \u2777\n    { \u2777\n        Sender.SendMessage($\u201dHello {username}!\u201d); \u2777\n    } \u2777\n    return $&quot;Welcome message sent to {username}&quot;;\n}<\/code><\/pre>\n<p>\u2776 Requests an IEnumerable injects an array of IMessageSender<br \/>\n\u8bf7\u6c42 IEnumerable \u6ce8\u5165 IMessageSender \u6570\u7ec4<\/p>\n<p>\u2777 Each IMessageSender in the IEnumerable is a different implementation.<br \/>\nIEnumerable \u4e2d\u7684\u6bcf\u4e2a IMessageSender \u90fd\u662f\u4e0d\u540c\u7684\u5b9e\u73b0\u3002<\/p>\n<p><strong>Warning<\/strong> You must use <code>IEnumerable&lt;T&gt;<\/code> as the handler parameter type to inject all the registered types of a service, T. Even though this parameter will be injected as a T[] array, you can\u2019t use T[] or <code>ICollection&lt;T&gt;<\/code> as your constructor argument. Doing so will cause an InvalidOperationException, similar to that in figure 9.1.<br \/>\n<strong>\u8b66\u544a<\/strong> \u60a8\u5fc5\u987b\u4f7f\u7528 <code>IEnumerable&lt;T&gt;<\/code> \u4f5c\u4e3a\u5904\u7406\u7a0b\u5e8f\u53c2\u6570\u7c7b\u578b\uff0c\u4ee5\u6ce8\u5165\u670d\u52a1\u7684\u6240\u6709\u5df2\u6ce8\u518c\u7c7b\u578b\u3002\u5373\u4f7f\u6b64\u53c2\u6570\u5c06\u4f5c\u4e3a T[]\u6570\u7ec4\uff0c\u5219\u4e0d\u80fd\u4f7f\u7528 T[] \u6216 <code>ICollection&lt;T&gt;<\/code> \u4f5c\u4e3a\u6784\u9020\u51fd\u6570\u53c2\u6570\u3002\u8fd9\u6837\u505a\u4f1a\u5bfc\u81f4InvalidOperationException\uff0c\u7c7b\u4f3c\u4e8e\u56fe 9.1 \u4e2d\u7684\u5185\u5bb9\u3002<\/p>\n<p>It\u2019s simple enough to inject all the registered implementations of a service, but what if you need only one? How does the container know which one to use?<\/p>\n<p>\u6ce8\u5165\u670d\u52a1\u7684\u6240\u6709\u5df2\u6ce8\u518c\u5b9e\u73b0\u975e\u5e38\u7b80\u5355\uff0c\u4f46\u5982\u679c\u4f60\u53ea\u9700\u8981\u4e00\u4e2a\u5462\uff1f\u5bb9\u5668\u5982\u4f55\u77e5\u9053\u8981\u4f7f\u7528\u54ea\u4e00\u4e2a\uff1f<\/p>\n<h3>9.3.2 Injecting a single implementation when multiple services are registered<\/h3>\n<h3>9.3.2 \u5728\u6ce8\u518c\u591a\u4e2a\u670d\u52a1\u65f6\u6ce8\u5165\u5355\u4e2a\u5b9e\u73b0<\/h3>\n<p>Suppose that you\u2019ve already registered all the IMessageSender implementations. What happens if you have a service that requires only one of them? Consider this example:<br \/>\n\u5047\u8bbe\u60a8\u5df2\u7ecf\u6ce8\u518c\u4e86\u6240\u6709 IMessageSender \u5b9e\u73b0\u3002\u5982\u679c\u60a8\u7684\u670d\u52a1\u53ea\u9700\u8981\u5176\u4e2d\u4e00\u4e2a\uff0c\u4f1a\u53d1\u751f\u4ec0\u4e48\u60c5\u51b5\uff1f\u8bf7\u8003\u8651\u4ee5\u4e0b\u793a\u4f8b\uff1a<\/p>\n<pre><code>public class SingleMessageSender\n{\n    private readonly IMessageSender _messageSender;\n    public SingleMessageSender(IMessageSender messageSender)\n    {\n        _messageSender = messageSender;\n    }\n}<\/code><\/pre>\n<p>Of the three implementations available, the container needs to pick a single IMessageSender to inject into this service. It does this by using the last registered implementation: FacebookSender from the previous example.<\/p>\n<p>\u5728\u4e09\u79cd\u53ef\u7528\u7684\u5b9e\u73b0\u4e2d\uff0c\u5bb9\u5668\u9700\u8981\u9009\u53d6\u4e00\u4e2a IMessageSender \u4ee5\u6ce8\u5165\u5230\u6b64\u670d\u52a1\u4e2d\u3002\u5b83\u901a\u8fc7\u4f7f\u7528\u4e0a\u4e00\u4e2a\u793a\u4f8b\u4e2d\u7684 FacebookSender \u6765\u5b9e\u73b0\u6b64\u76ee\u7684\u3002<\/p>\n<p><strong>Note<\/strong> The DI container will use the last registered implementation of a service when resolving a single instance of the service.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5728\u89e3\u6790\u670d\u52a1\u7684\u5355\u4e2a\u5b9e\u4f8b\u65f6\uff0cDI \u5bb9\u5668\u5c06\u4f7f\u7528\u4e0a\u6b21\u6ce8\u518c\u7684\u670d\u52a1\u5b9e\u73b0\u3002<\/p>\n<p>This feature can be particularly useful for replacing built-in DI registrations with your own services. If you have a custom implementation of a service that you know is registered within a library\u2019s Add* extension method, you can override that registration by registering your own implementation afterward. The DI container will use your implementation whenever a single instance of the service is requested.<\/p>\n<p>\u6b64\u529f\u80fd\u5bf9\u4e8e\u5c06\u5185\u7f6e DI \u6ce8\u518c\u66ff\u6362\u4e3a\u60a8\u81ea\u5df1\u7684\u670d\u52a1\u7279\u522b\u6709\u7528\u3002\u5982\u679c\u60a8\u77e5\u9053\u5728\u5e93\u7684 Add* \u6269\u5c55\u65b9\u6cd5\u4e2d\u6ce8\u518c\u4e86\u670d\u52a1\u7684\u81ea\u5b9a\u4e49\u5b9e\u65bd\uff0c\u5219\u53ef\u4ee5\u901a\u8fc7\u5728\u4e4b\u540e\u6ce8\u518c\u81ea\u5df1\u7684\u5b9e\u65bd\u6765\u8986\u76d6\u8be5\u6ce8\u518c\u3002\u6bcf\u5f53\u8bf7\u6c42\u670d\u52a1\u7684\u5355\u4e2a\u5b9e\u4f8b\u65f6\uff0cDI \u5bb9\u5668\u90fd\u4f1a\u4f7f\u7528\u60a8\u7684\u5b9e\u73b0\u3002<\/p>\n<p>The main disadvantage of this approach is that you still end up with multiple implementations registered; you can inject an <code>IEnumerable&lt;T&gt;<\/code> as before. Sometimes you want to register a service conditionally so that you always have only a single registered implementation.<\/p>\n<p>\u8fd9\u79cd\u65b9\u6cd5\u7684\u4e3b\u8981\u7f3a\u70b9\u662f\u4f60\u6700\u7ec8\u4ecd\u7136\u6ce8\u518c\u4e86\u591a\u4e2a implementation;\u60a8\u53ef\u4ee5\u50cf\u4ee5\u524d\u4e00\u6837\u6ce8\u5165 <code>IEnumerable&lt;T&gt;<\/code>\u3002\u6709\u65f6\uff0c\u60a8\u5e0c\u671b\u6709\u6761\u4ef6\u5730\u6ce8\u518c\u4e00\u4e2a\u670d\u52a1\uff0c\u4ee5\u4fbf\u59cb\u7ec8\u53ea\u6709\u4e00\u4e2a\u5df2\u6ce8\u518c\u7684\u5b9e\u73b0\u3002<\/p>\n<h3>9.3.3 Conditionally registering services using TryAdd<\/h3>\n<h3>9.3.3 \u4f7f\u7528 TryAdd \u6709\u6761\u4ef6\u5730\u6ce8\u518c\u670d\u52a1<\/h3>\n<p>Sometimes you want to add an implementation of a service only if one hasn\u2019t already been added. This approach is particularly useful for library authors; they can create a default implementation of an interface and register it only if the user hasn\u2019t already registered their own implementation.<\/p>\n<p>\u6709\u65f6\uff0c\u4ec5\u5f53\u5c1a\u672a\u6dfb\u52a0\u670d\u52a1\u65f6\uff0c\u60a8\u624d\u5e0c\u671b\u6dfb\u52a0\u670d\u52a1\u7684\u5b9e\u73b0\u3002\u6b64\u65b9\u6cd5\u5bf9\u5e93\u4f5c\u8005\u7279\u522b\u6709\u7528;\u4ed6\u4eec\u53ef\u4ee5\u521b\u5efa interface \u7684\u9ed8\u8ba4 implementation \uff0c\u5e76\u4e14\u53ea\u6709\u5728\u7528\u6237\u5c1a\u672a\u6ce8\u518c\u81ea\u5df1\u7684 implementation \u65f6\u624d\u80fd\u6ce8\u518c\u5b83\u3002<\/p>\n<p>You can find several extension methods for conditional registration in the Microsoft.Extensions.DependencyInjection.Extensions namespace, such as TryAddScoped. This method checks whether a service has been registered with the container before calling AddScoped on the implementation. Listing 9.9 shows how you can add SmsSender conditionally if there are no existing IMessageSender implementations. As you initially register EmailSender, the container ignores the SmsSender registration, so it isn\u2019t available in your app.<\/p>\n<p>\u60a8\u53ef\u4ee5\u5728 Microsoft.Extensions.DependencyInjection.Exte nsions \u547d\u540d\u7a7a\u95f4\u4e2d\u627e\u5230\u591a\u79cd\u7528\u4e8e\u6761\u4ef6\u6ce8\u518c\u7684\u6269\u5c55\u65b9\u6cd5\uff0c\u4f8b\u5982 TryAddScoped\u3002\u6b64\u65b9\u6cd5\u5728\u5bf9\u5b9e\u73b0\u8c03\u7528 AddScoped \u4e4b\u524d\uff0c\u68c0\u67e5\u662f\u5426\u5df2\u5411\u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u3002\u6e05\u5355 9.9 \u5c55\u793a\u4e86\u5982\u4f55\u6709\u6761\u4ef6\u5730\u6dfb\u52a0 SmsSender\u5982\u679c\u6ca1\u6709\u73b0\u6709\u7684 IMessageSender \u5b9e\u73b0\u3002\u5f53\u60a8\u6700\u521d\u6ce8\u518c EmailSender \u65f6\uff0c\u5bb9\u5668\u4f1a\u5ffd\u7565 SmsSender \u6ce8\u518c\uff0c\u56e0\u6b64\u5b83\u5728\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4e0d\u53ef\u7528\u3002<\/p>\n<p>Listing 9.9 Conditionally adding a service using TryAddScoped<br \/>\n\u6e05\u5355 9.9 \u4f7f\u7528 TryAdd \u6709\u6761\u4ef6\u5730\u6ce8\u518c\u670d\u52a1<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddScoped&lt;IMessageSender, EmailSender&gt;(); \u2776\nbuilder.Services.TryAddScoped&lt;IMessageSender, SmsSender&gt;(); \u2777\n<\/code><\/pre>\n<p>\u2776 EmailSender is registered with the container.<br \/>\nEmailSender \u5df2\u6ce8\u518c\u5230\u5bb9\u5668\u4e2d\u3002<\/p>\n<p>\u2777 There\u2019s already an IMessageSender implementation, so SmsSender isn\u2019t<br \/>\nregistered.<br \/>\n\u5df2\u7ecf\u6709\u4e00\u4e2a IMessageSender \u5b9e\u73b0\uff0c\u56e0\u6b64 SmsSender \u672a\u6ce8\u518c\u3002<\/p>\n<p>Code like this doesn\u2019t often make a lot of sense at the application level, but it can be useful if you\u2019re building libraries for use in multiple apps. The ASP.NET Core framework, for example, uses TryAdd* in many places, which lets you easily register alternative implementations of internal components in your own application if you want.<\/p>\n<p>\u50cf\u8fd9\u6837\u7684\u4ee3\u7801\u5728\u5e94\u7528\u7a0b\u5e8f\u7ea7\u522b\u901a\u5e38\u6ca1\u6709\u591a\u5927\u610f\u4e49\uff0c\u4f46\u5982\u679c\u60a8\u6b63\u5728\u6784\u5efa\u7528\u4e8e\u591a\u4e2a\u5e94\u7528\u7a0b\u5e8f\u7684\u5e93\uff0c\u5b83\u53ef\u80fd\u5f88\u6709\u7528\u3002\u4f8b\u5982\uff0cASP.NET Core \u6846\u67b6\u5728\u8bb8\u591a\u5730\u65b9\u90fd\u4f7f\u7528 TryAdd*\uff0c\u5b83\u5141\u8bb8\u60a8\u6839\u636e\u9700\u8981\u8f7b\u677e\u5730\u5728\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ce8\u518c\u5185\u90e8\u7ec4\u4ef6\u7684\u66ff\u4ee3\u5b9e\u65bd\u3002<\/p>\n<p>You can also replace a previously registered implementation by using the Replace() extension method. Unfortunately, the API for this method isn\u2019t as friendly as the TryAdd methods. To replace a previously registered IMessageSender with SmsSender, you\u2019d use<\/p>\n<p>\u60a8\u8fd8\u53ef\u4ee5\u4f7f\u7528 Replace\uff08\uff09 \u6269\u5c55\u65b9\u6cd5\u66ff\u6362\u4ee5\u524d\u6ce8\u518c\u7684\u5b9e\u73b0\u3002\u9057\u61be\u7684\u662f\uff0c\u6b64\u65b9\u6cd5\u7684 API \u4e0d\u5982 TryAdd \u65b9\u6cd5\u53cb\u597d\u3002\u8981\u5c06\u4ee5\u524d\u6ce8\u518c\u7684 IMessageSender \u66ff\u6362\u4e3a SmsSender\uff0c\u8bf7\u4f7f\u7528<\/p>\n<pre><code>builder.Services.Replace(new ServiceDescriptor(\n    typeof(IMessageSender), typeof(SmsSender), ServiceLifetime.Scoped\n));<\/code><\/pre>\n<p><strong>Tip<\/strong> When using Replace, you must provide the same lifetime that was used to register the service that\u2019s being replaced.<br \/>\n<strong>\u63d0\u793a<\/strong> \u4f7f\u7528 Replace \u65f6\uff0c\u60a8\u5fc5\u987b\u63d0\u4f9b\u7528\u4e8e\u6ce8\u518c\u8981\u66ff\u6362\u7684\u670d\u52a1\u7684\u76f8\u540c\u751f\u547d\u5468\u671f\u3002<\/p>\n<p>We\u2019ve pretty much covered registering dependencies but touched only vaguely on one important aspect: lifetimes. Understanding lifetimes is crucial in working with DI containers, so it\u2019s important to pay close attention to them when registering your services with the container.<\/p>\n<p>\u6211\u4eec\u51e0\u4e4e\u5df2\u7ecf\u4ecb\u7ecd\u4e86\u6ce8\u518c\u4f9d\u8d56\u9879\uff0c\u4f46\u53ea\u6a21\u7cca\u5730\u89e6\u53ca\u4e86\u4e00\u4e2a\u91cd\u8981\u7684\u65b9\u9762\uff1a\u751f\u547d\u5468\u671f\u3002\u4e86\u89e3\u751f\u547d\u5468\u671f\u5bf9\u4e8e\u4f7f\u7528 DI \u5bb9\u5668\u81f3\u5173\u91cd\u8981\uff0c\u56e0\u6b64\u5728\u5411\u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u65f6\uff0c\u8bf7\u52a1\u5fc5\u5bc6\u5207\u5173\u6ce8\u5b83\u4eec\u3002<\/p>\n<h3>9.4 Understanding lifetimes: When are services created?<\/h3>\n<h3>9.4 \u4e86\u89e3\u751f\u547d\u5468\u671f\uff1a\u4f55\u65f6\u521b\u5efa\u670d\u52a1\uff1f<\/h3>\n<p>Whenever the DI container is asked for a particular registered service, such as an instance of IMessageSender, it can do either of two things to fulfill the request:<\/p>\n<p>\u6bcf\u5f53\u5411 DI \u5bb9\u5668\u8bf7\u6c42\u7279\u5b9a\u7684\u5df2\u6ce8\u518c\u670d\u52a1\uff08\u5982 IMessageSender \u7684\u5b9e\u4f8b\uff09\u65f6\uff0c\u5b83\u90fd\u53ef\u4ee5\u6267\u884c\u4ee5\u4e0b\u4e24\u9879\u4f5c\u4e4b\u4e00\u6765\u6ee1\u8db3\u8bf7\u6c42\uff1a<\/p>\n<ul>\n<li>\n<p>Create and return a new instance of the service<br \/>\n\u521b\u5efa\u5e76\u8fd4\u56de\u670d\u52a1\u7684\u65b0\u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<li>\n<p>Return an existing instance of the service<br \/>\n\u8fd4\u56de\u670d\u52a1\u7684\u73b0\u6709\u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<\/ul>\n<p>The lifetime of a service controls the behavior of the DI container with respect to these two options. You define the lifetime of a service during DI service registration. The lifetime dictates when a DI container reuses an existing instance of the service to fulfill service dependencies and when it creates a new one.<\/p>\n<p>\u670d\u52a1\u7684\u751f\u5b58\u671f\u63a7\u5236 DI \u5bb9\u5668\u76f8\u5bf9\u4e8e\u8fd9\u4e24\u4e2a\u9009\u9879\u7684\u884c\u4e3a\u3002\u60a8\u53ef\u4ee5\u5728 DI \u670d\u52a1\u6ce8\u518c\u671f\u95f4\u5b9a\u4e49\u670d\u52a1\u7684\u751f\u547d\u5468\u671f\u3002\u751f\u547d\u5468\u671f\u89c4\u5b9a\u4e86 DI \u5bb9\u5668\u4f55\u65f6\u91cd\u7528\u670d\u52a1\u7684\u73b0\u6709\u5b9e\u4f8b\u6765\u5b9e\u73b0\u670d\u52a1\u4f9d\u8d56\u9879\uff0c\u4ee5\u53ca\u4f55\u65f6\u521b\u5efa\u65b0\u5b9e\u4f8b\u3002<\/p>\n<p><strong>Definition<\/strong> The lifetime of a service is how long an instance of a service should live in a container before the container creates a new instance.<br \/>\n<strong>\u5b9a\u4e49<\/strong> \u670d\u52a1\u7684\u751f\u547d\u5468\u671f\u662f\u6307\u5728\u5bb9\u5668\u521b\u5efa\u65b0\u5b9e\u4f8b\u4e4b\u524d\uff0c\u670d\u52a1\u5b9e\u4f8b\u5e94\u5728\u5bb9\u5668\u4e2d\u5b58\u5728\u7684\u65f6\u95f4\u3002<\/p>\n<p>It\u2019s important to get your head around the implications for the different lifetimes used in ASP.NET Core, so this section looks at each lifetime option and when you should use it. In particular, you\u2019ll see how the lifetime affects how often the DI container creates new objects. In section 9.4.4 I\u2019ll show you an antipattern of lifetimes to watch out for, in which a short-lifetime dependency is captured by a long-lifetime dependency. This antipattern can cause some hard-to-debug issues, so it\u2019s important to bear in mind when configuring your app.<\/p>\n<p>\u4e86\u89e3 ASP.NET Core \u4e2d\u4f7f\u7528\u7684\u4e0d\u540c\u751f\u547d\u5468\u671f\u7684\u5f71\u54cd\u975e\u5e38\u91cd\u8981\uff0c\u56e0\u6b64\u672c\u8282\u5c06\u4ecb\u7ecd\u6bcf\u4e2a\u751f\u547d\u5468\u671f\u9009\u9879\u4ee5\u53ca\u4f55\u65f6\u5e94\u8be5\u4f7f\u7528\u5b83\u3002\u7279\u522b\u662f\uff0c\u60a8\u5c06\u770b\u5230\u751f\u547d\u5468\u671f\u5982\u4f55\u5f71\u54cd DI \u5bb9\u5668\u521b\u5efa\u65b0\u5bf9\u8c61\u7684\u9891\u7387\u3002\u5728 9.4.4 \u8282\u4e2d\uff0c\u6211\u5c06\u5411\u4f60\u5c55\u793a\u4e00\u4e2a\u9700\u8981\u6ce8\u610f\u7684\u751f\u547d\u5468\u671f\u7684\u53cd\u6a21\u5f0f\uff0c\u5176\u4e2d\u77ed\u751f\u547d\u5468\u671f\u7684\u4f9d\u8d56\u6027\u88ab\u957f\u751f\u547d\u5468\u671f\u7684\u4f9d\u8d56\u6027\u6355\u83b7\u3002\u8fd9\u79cd\u53cd\u6a21\u5f0f\u53ef\u80fd\u4f1a\u5bfc\u81f4\u4e00\u4e9b\u96be\u4ee5\u8c03\u8bd5\u7684\u95ee\u9898\uff0c\u56e0\u6b64\u5728\u914d\u7f6e\u5e94\u7528\u7a0b\u5e8f\u65f6\u8bf7\u52a1\u5fc5\u8bb0\u4f4f\u3002<\/p>\n<p>In ASP.NET Core, you can specify one of three lifetimes when registering a service with the built-in container:<br \/>\n\u5728 ASP.NET Core \u4e2d\uff0c\u60a8\u53ef\u4ee5\u5728\u4f7f\u7528\u5185\u7f6e\u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u65f6\u6307\u5b9a\u4e09\u4e2a\u751f\u547d\u5468\u671f\u4e4b\u4e00\uff1a<\/p>\n<ul>\n<li>\n<p>Transient\u2014Every time a service is requested, a new instance is created. Potentially, you can have different instances of the same class within the same dependency graph.<br \/>\nTransient \uff08\u77ac\u6001\uff09 \u2013 \u6bcf\u6b21\u8bf7\u6c42\u670d\u52a1\u65f6\uff0c\u90fd\u4f1a\u521b\u5efa\u4e00\u4e2a\u65b0\u5b9e\u4f8b\u3002\u60a8\u53ef\u80fd\u4f1a\u5728\u540c\u4e00\u4f9d\u8d56\u9879\u5173\u7cfb\u56fe\u4e2d\u62e5\u6709\u540c\u4e00\u7c7b\u7684\u4e0d\u540c\u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<li>\n<p>Scoped\u2014Within a scope, all requests for a service give you the same object. For different scopes, you get different objects. In ASP.NET Core, each web request gets its own scope.<br \/>\n\u8303\u56f4 - \u5728\u4e00\u4e2a\u8303\u56f4\u5185\uff0c\u670d\u52a1\u7684\u6240\u6709\u8bf7\u6c42\u90fd\u4f1a\u4e3a\u60a8\u63d0\u4f9b\u76f8\u540c\u7684\u5bf9\u8c61\u3002\u5bf9\u4e8e\u4e0d\u540c\u7684\u8303\u56f4\uff0c\u4f60\u4f1a\u5f97\u5230\u4e0d\u540c\u7684\u5bf9\u8c61\u3002\u5728 ASP.NET Core \u4e2d\uff0c\u6bcf\u4e2a Web \u8bf7\u6c42\u90fd\u6709\u81ea\u5df1\u7684\u8303\u56f4\u3002<\/p>\n<\/li>\n<li>\n<p>Singleton\u2014You always get the same instance of the service, regardless of scope.<br \/>\n\u5355\u4e00\u5b9e\u4f8b - \u65e0\u8bba\u8303\u56f4\u5982\u4f55\uff0c\u60a8\u59cb\u7ec8\u4f1a\u83b7\u5f97\u76f8\u540c\u7684\u670d\u52a1\u5b9e\u4f8b\u3002<\/p>\n<\/li>\n<\/ul>\n<p><strong>Note<\/strong> These concepts align well with most other DI containers, but the terminology may differ. If you\u2019re familiar with a third-party DI container, be sure you understand how the lifetime concepts align with the built-in ASP.NET Core DI container.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u8fd9\u4e9b\u6982\u5ff5\u4e0e\u5927\u591a\u6570\u5176\u4ed6 DI \u5bb9\u5668\u975e\u5e38\u4e00\u81f4\uff0c\u4f46\u672f\u8bed\u53ef\u80fd\u6709\u6240\u4e0d\u540c\u3002\u5982\u679c\u60a8\u719f\u6089\u4f7f\u7528\u7b2c\u4e09\u65b9 DI \u5bb9\u5668\u65f6\uff0c\u8bf7\u786e\u4fdd\u60a8\u4e86\u89e3\u751f\u547d\u5468\u671f\u6982\u5ff5\u5982\u4f55\u4e0e\u5185\u7f6e\u7684 ASP.NET Core DI \u5bb9\u5668\u4fdd\u6301\u4e00\u81f4\u3002<\/p>\n<p>To illustrate the behavior of each lifetime, I use a simple example in this section. Suppose that you have DataContext, which has a connection to a database, as shown in listing 9.10. It has a single property, RowCount, which represents the number of rows in the Users table of a database. For the purposes of this example, we emulate calling the database by setting the number of rows randomly when the DataContext object is created, so you always get the same value every time you call RowCount on a given DataContext instance. Different instances of DataContext return different RowCount values.<\/p>\n<p>\u4e3a\u4e86\u8bf4\u660e\u6bcf\u4e2a\u751f\u547d\u5468\u671f\u7684\u884c\u4e3a\uff0c\u6211\u5728\u672c\u8282\u4e2d\u4f7f\u7528\u4e86\u4e00\u4e2a\u7b80\u5355\u7684\u793a\u4f8b\u3002\u5047\u8bbe\u4f60\u6709 DataContext\uff0c\u5b83\u4e0e\u4e00\u4e2a\u6570\u636e\u5e93\u6709\u8fde\u63a5\uff0c\u5982\u6e05\u5355 9.10 \u6240\u793a\u3002\u5b83\u6709\u4e00\u4e2a\u5c5e\u6027 RowCount\uff0c\u8be5\u5c5e\u6027\u8868\u793a\u6570\u636e\u5e93\u7684 Users \u8868\u4e2d\u7684\u884c\u6570\u3002\u5bf9\u4e8e\u6b64\u793a\u4f8b\uff0c\u6211\u4eec\u901a\u8fc7\u5728\u521b\u5efa DataContext \u5bf9\u8c61\u65f6\u968f\u673a\u8bbe\u7f6e\u884c\u6570\u6765\u6a21\u62df\u8c03\u7528\u6570\u636e\u5e93\uff0c\u56e0\u6b64\u6bcf\u6b21\u5728\u7ed9\u5b9a DataContext \u5b9e\u4f8b\u4e0a\u8c03\u7528 RowCount \u65f6\uff0c\u60a8\u59cb\u7ec8\u4f1a\u83b7\u5f97\u76f8\u540c\u7684\u503c\u3002DataContext \u7684\u4e0d\u540c\u5b9e\u4f8b\u8fd4\u56de\u4e0d\u540c\u7684 RowCount \u503c\u3002<\/p>\n<p>Listing 9.10 DataContext generating a random RowCount on creation<br \/>\n\u6e05\u5355 9.10 \u5728\u521b\u5efaDataContext\u65f6\u751f\u6210\u4e00\u4e2a random RowCount<\/p>\n<pre><code>class DataContext\n{\n    public int RowCount { get; } \u2776\n        = Random.Shared.Next(1, 1_000_000_000); \u2777\n}<\/code><\/pre>\n<p>\u2776 The property is read-only, so it always returns the same value.<br \/>\n\u8be5\u5c5e\u6027\u662f\u53ea\u8bfb\u7684\uff0c\u56e0\u6b64\u5b83\u59cb\u7ec8\u8fd4\u56de\u76f8\u540c\u7684\u503c\u3002<\/p>\n<p>\u2777 Generates a random number between 1 and 1,000,000,000<br \/>\n\u751f\u6210\u4e00\u4e2a\u4ecb\u4e8e 1 \u548c 1,000,000,000 \u4e4b\u95f4\u7684\u968f\u673a\u6570<\/p>\n<p>You also have a Repository class that has a dependency on the DataContext, as shown in the next listing. It also exposes a RowCount property, but this property delegates the call to its instance of DataContext. Whatever value DataContext was created with, the Repository displays the same value.<\/p>\n<p>\u60a8\u8fd8\u6709\u4e00\u4e2a\u4f9d\u8d56\u4e8e DataContext \u7684 Repository \u7c7b\uff0c\u5982\u4e0b\u4e00\u4e2a\u6e05\u5355\u6240\u793a\u3002\u5b83\u8fd8\u516c\u5f00\u4e86\u4e00\u4e2a RowCount \u5c5e\u6027\uff0c\u4f46\u6b64\u5c5e\u6027\u5c06\u8c03\u7528\u59d4\u6258\u7ed9\u5176 DataContext \u5b9e\u4f8b\u3002\u65e0\u8bba\u4ef7\u503c\u5982\u4f55DataContext \u65f6\uff0cRepository \u663e\u793a\u76f8\u540c\u7684\u503c\u3002<\/p>\n<p>Listing 9.11 Repository service that depends on an instance of DataContext<br \/>\n\u6e05\u5355 9.11 \u4f9d\u8d56\u4e8eDataContext \u7684\u5b9e\u4f8b<\/p>\n<pre><code>public class Repository\n{\n    private readonly DataContext _dataContext; \u2776\n    public Repository(DataContext dataContext) \u2776\n    { \u2776\n        _dataContext = dataContext; \u2776\n    } \u2776\n    public int RowCount =&gt; _dataContext.RowCount; \u2777\n}<\/code><\/pre>\n<p>\u2776 An instance of DataContext is provided using DI.<br \/>\nDataContext \u7684\u5b9e\u4f8b\u662f\u4f7f\u7528 DI \u63d0\u4f9b\u7684\u3002<br \/>\n\u2777 RowCount returns the same value as the current instance of DataContext.<br \/>\nRowCount \u8fd4\u56de\u4e0e DataContext \u7684\u5f53\u524d\u5b9e\u4f8b\u76f8\u540c\u7684\u503c\u3002<\/p>\n<p>Finally, you have your endpoint handler, RowCounts, which takes a dependency on both Repository and on DataContext directly. When the minimal API infrastructure creates the arguments needed to call RowCounts, the DI container injects an instance of DataContext and an instance of Repository. To create Repository, it must create a second instance of DataContext. Over the course of two requests, four instances of DataContext will be required, as shown in figure 9.6.<\/p>\n<p>\u6700\u540e\uff0c\u60a8\u6709\u7ec8\u7aef\u8282\u70b9\u5904\u7406\u7a0b\u5e8f RowCounts\uff0c\u5b83\u76f4\u63a5\u4f9d\u8d56\u4e8e Repository \u548c DataContext\u3002\u5f53\u6700\u5c0f\u7684 API \u57fa\u7840\u8bbe\u65bd\u521b\u5efa\u8c03\u7528 RowCounts \u6240\u9700\u7684\u53c2\u6570\u65f6\uff0cDI \u5bb9\u5668\u4f1a\u6ce8\u5165\u4e00\u4e2a DataContext \u5b9e\u4f8b\u548c\u4e00\u4e2a Repository \u5b9e\u4f8b\u3002\u8981\u521b\u5efa Repository\uff0c\u5b83\u5fc5\u987b\u521b\u5efa DataContext \u7684\u7b2c\u4e8c\u4e2a\u5b9e\u4f8b\u3002\u5728\u4e24\u4e2a\u8bf7\u6c42\u7684\u8fc7\u7a0b\u4e2d\uff0c\u5c06\u9700\u8981 4 \u4e2a DataContext \u5b9e\u4f8b\uff0c\u5982\u56fe 9.6 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0906.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.6 The DI container uses two instances of DataContext for each request. Depending on the lifetime with which the DataContext type is registered, the container might create one, two, or four instances of DataContext.<br \/>\n\u56fe 9.6 DI \u5bb9\u5668\u4e3a\u6bcf\u4e2a\u8bf7\u6c42\u4f7f\u7528\u4e24\u4e2a DataContext \u5b9e\u4f8b\u3002\u6839\u636e DataContext \u7c7b\u578b\u6ce8\u518c\u7684\u751f\u547d\u5468\u671f\uff0c\u5bb9\u5668\u53ef\u80fd\u4f1a\u521b\u5efa\u4e00\u4e2a\u3001\u4e24\u4e2a\u6216\u56db\u4e2a DataContext \u5b9e\u4f8b\u3002<\/p>\n<p>The RowCounts handler retrieves the value of RowCount returned from both Repository and DataContext and then returns them as a string, similar to the code in listing 9.12. The sample code associated with this book also records and displays the values from previous requests so you can easily track how the values change with each request.<\/p>\n<p>RowCounts \u5904\u7406\u7a0b\u5e8f\u68c0\u7d22\u4ece Repository \u548c DataContext \u8fd4\u56de\u7684 RowCount \u7684\u503c\uff0c\u7136\u540e\u5c06\u5b83\u4eec\u4f5c\u4e3a\u5b57\u7b26\u4e32\u8fd4\u56de\uff0c\u7c7b\u4f3c\u4e8e\u6e05\u5355\u4e2d\u7684\u4ee3\u78019.12. \u4e0e\u672c\u4e66\u5173\u8054\u7684\u793a\u4f8b\u4ee3\u7801\u8fd8\u8bb0\u5f55\u5e76\u663e\u793a\u5148\u524d\u8bf7\u6c42\u7684\u503c\uff0c\u56e0\u6b64\u60a8\u53ef\u4ee5\u8f7b\u677e\u8ddf\u8e2a\u6bcf\u4e2a\u8bf7\u6c42\u7684\u503c\u5982\u4f55\u53d8\u5316\u3002<\/p>\n<p>Listing 9.12 The RowCounts handler depends on DataContext and Repository<br \/>\n\u6e05\u5355 9.12 RowCounts \u5904\u7406\u7a0b\u5e8f\u4f9d\u8d56\u4e8eDataContext \u548c\u5b58\u50a8\u5e93<\/p>\n<pre><code>static string RowCounts( \u2776\n    DataContext db, \u2776\n    Repository repository) \u2776\n{\n    int dbCount = db.RowCount; \u2777\n    int repositoryCount = repository.RowCount; \u2777\n\n    return: $&quot;DataContext: {dbCount}, Repository: {repositoryCount}&quot;; \u2778\n}<\/code><\/pre>\n<p>\u2776 DataContext and Repository are created using DI.<br \/>\nDataContext \u548c Repository \u662f\u4f7f\u7528 DI \u521b\u5efa\u7684\u3002<\/p>\n<p>\u2777 When invoked, the page handler retrieves and records RowCount from both<br \/>\ndependencies.<br \/>\n\u8c03\u7528\u65f6\uff0c\u9875\u9762\u5904\u7406\u7a0b\u5e8f\u4f1a\u4ece\u4e24\u4e2a\u4f9d\u8d56\u9879\u4e2d\u68c0\u7d22\u5e76\u8bb0\u5f55 RowCount\u3002<\/p>\n<p>\u2778 The counts are returned in the response.<br \/>\n\u54cd\u5e94\u4e2d\u8fd4\u56de\u8ba1\u6570\u3002<\/p>\n<p>The purpose of this example is to explore the relationships among the four DataContext instances, depending on the lifetimes you use to register the services with the container. I\u2019m generating a random number in DataContext as a way of uniquely identifying a DataContext instance, but you can think of this example as being a point-in-time snapshot of, say, the number of users logged on to your site or the amount of stock in a warehouse.<\/p>\n<p>\u6b64\u793a\u4f8b\u7684\u76ee\u7684\u662f\u63a2\u7d22 4 \u4e2a DataContext \u5b9e\u4f8b\u4e4b\u95f4\u7684\u5173\u7cfb\uff0c\u5177\u4f53\u53d6\u51b3\u4e8e\u60a8\u7528\u4e8e\u5411\u5bb9\u5668\u6ce8\u518c\u670d\u52a1\u7684\u751f\u547d\u5468\u671f\u3002\u6211\u5728 DataContext \u4e2d\u751f\u6210\u4e00\u4e2a\u968f\u673a\u6570\uff0c\u4f5c\u4e3a\u552f\u4e00\u6807\u8bc6 DataContext \u5b9e\u4f8b\u7684\u4e00\u79cd\u65b9\u5f0f\uff0c\u4f46\u60a8\u53ef\u4ee5\u5c06\u6b64\u793a\u4f8b\u89c6\u4e3a\u767b\u5f55\u5230\u60a8\u7ad9\u70b9\u7684\u7528\u6237\u6570\u91cf\u6216\u4ed3\u5e93\u4e2d\u5e93\u5b58\u91cf\u7684\u65f6\u95f4\u70b9\u5feb\u7167\u3002<\/p>\n<p>I\u2019ll start with the shortest-lived lifetime (transient), move on to the common scoped lifetime, and then take a look at singletons. Finally, I\u2019ll show an important trap you should be on the lookout for when registering services in your own apps.<\/p>\n<p>\u6211\u5c06\u4ece\u6700\u77ed\u751f\u5b58\u671f \uff08transient\uff09 \u5f00\u59cb\uff0c\u7136\u540e\u8f6c\u5230\u5e38\u89c1\u7684\u4f5c\u7528\u57df\u751f\u5b58\u671f\uff0c\u7136\u540e\u770b\u4e00\u4e0b\u5355\u4f8b\u3002\u6700\u540e\uff0c\u6211\u5c06\u5c55\u793a\u4e00\u4e2a\u91cd\u8981\u7684\u9677\u9631\u5728\u60a8\u81ea\u5df1\u7684\u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ce8\u518c\u670d\u52a1\u65f6\u8981\u6ce8\u610f\u3002<\/p>\n<h3>9.4.1 Transient: Everyone is unique<\/h3>\n<h3>9.4.1 \u77ac\u6001\uff1a\u6bcf\u4e2a\u4eba\u90fd\u662f\u72ec\u4e00\u65e0\u4e8c\u7684<\/h3>\n<p>In the ASP.NET Core DI container, transient services are always created new whenever they\u2019re needed to fulfill a dependency. You can register your services using the AddTransient extension methods:<\/p>\n<p>\u5728 ASP.NET Core DI \u5bb9\u5668\u4e2d\uff0c\u6bcf\u5f53\u9700\u8981\u4e34\u65f6\u670d\u52a1\u6765\u5b9e\u73b0\u4f9d\u8d56\u9879\u65f6\uff0c\u5b83\u4eec\u603b\u662f\u4f1a\u521b\u5efa\u65b0\u7684\u3002\u60a8\u53ef\u4ee5\u4f7f\u7528 AddTransient \u6269\u5c55\u65b9\u6cd5\u6ce8\u518c\u60a8\u7684\u670d\u52a1\uff1a<\/p>\n<pre><code>builder.Services.AddTransient&lt;DataContext&gt;();\nbuilder.Services.AddTransient&lt;Repository&gt;();<\/code><\/pre>\n<p>When you register services this way, every time a dependency is required, the container creates a new one. This behavior of the container for transient services applies both between requests and within requests; the DataContext injected into the Repository will be a different instance from the one injected into the RowCounts handler.<\/p>\n<p>\u5f53\u60a8\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u6ce8\u518c\u670d\u52a1\u65f6\uff0c\u6bcf\u6b21\u9700\u8981\u4f9d\u8d56\u9879\u65f6\uff0c\u5bb9\u5668\u90fd\u4f1a\u521b\u5efa\u4e00\u4e2a\u65b0\u4f9d\u8d56\u9879\u3002\u4e34\u65f6\u670d\u52a1\u5bb9\u5668\u7684\u8fd9\u79cd\u884c\u4e3a\u9002\u7528\u4e8e\u8bf7\u6c42\u4e4b\u95f4\u548c\u8bf7\u6c42\u5185;\u6ce8\u5165 Repository \u7684 DataContext \u5c06\u4e0e\u6ce8\u5165 RowCounts \u5904\u7406\u7a0b\u5e8f\u7684\u5b9e\u4f8b\u4e0d\u540c\u3002<\/p>\n<p><strong>Note<\/strong> Transient dependencies can result in different instances of the same type within a single dependency graph.<br \/>\n<strong>\u6ce8\u610f<\/strong>\uff1a\u77ac\u6001\u4f9d\u8d56\u5173\u7cfb\u53ef\u80fd\u4f1a\u5bfc\u81f4\u5355\u4e2a\u4f9d\u8d56\u5173\u7cfb\u56fe\u4e2d\u51fa\u73b0\u76f8\u540c\u7c7b\u578b\u7684\u4e0d\u540c\u5b9e\u4f8b\u3002<\/p>\n<p>Figure 9.7 shows the results you get from calling the API repeatedly when you use the transient lifetime for both services. You can see that every value is different, both within a request and between requests. Note that figure 9.7 was generated using the source code for this chapter, which is based on the listings in this chapter, but also displays the results from previous requests to make the behavior easier to observe.<\/p>\n<p>\u56fe 9.7 \u663e\u793a\u4e86\u5728\u5bf9\u8fd9\u4e24\u4e2a\u670d\u52a1\u4f7f\u7528\u77ac\u6001\u751f\u547d\u5468\u671f\u65f6\u91cd\u590d\u8c03\u7528 API \u6240\u83b7\u5f97\u7684\u7ed3\u679c\u3002\u60a8\u53ef\u4ee5\u770b\u5230\uff0c\u6bcf\u4e2a\u503c\u90fd\u4e0d\u540c\uff0c\u65e0\u8bba\u662f\u5728\u8bf7\u6c42\u4e2d\u8fd8\u662f\u5728\u8bf7\u6c42\u4e4b\u95f4\u3002\u8bf7\u6ce8\u610f\uff0c\u56fe 9.7 \u662f\u4f7f\u7528\u672c\u7ae0\u7684\u6e90\u4ee3\u7801\u751f\u6210\u7684\uff0c\u8be5\u6e90\u4ee3\u7801\u57fa\u4e8e\u672c\u7ae0\u4e2d\u7684\u6e05\u5355\uff0c\u4f46\u4e5f\u663e\u793a\u4e86\u6765\u81ea\u5148\u524d\u8bf7\u6c42\u7684\u7ed3\u679c\uff0c\u4ee5\u4f7f\u884c\u4e3a\u66f4\u6613\u4e8e\u89c2\u5bdf\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0907.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.7 When registered using the transient lifetime, all DataContext objects are different, as you see by the fact that all the values are different within and between requests.<br \/>\n\u56fe 9.7 \u5f53\u4f7f\u7528\u77ac\u6001\u751f\u547d\u5468\u671f\u6ce8\u518c\u65f6\uff0c\u6240\u6709 DataContext \u5bf9\u8c61\u90fd\u662f\u4e0d\u540c\u7684\uff0c\u6b63\u5982\u60a8\u6240\u770b\u5230\u7684\uff0c\u8bf7\u6c42\u5185\u90e8\u548c\u8bf7\u6c42\u4e4b\u95f4\u7684\u6240\u6709\u503c\u90fd\u4e0d\u540c\u3002<\/p>\n<p>Transient lifetimes can result in the creation of a lot of objects, so they make the most sense for lightweight services with little or no state. Using the transient lifetime is equivalent to calling new every time you need a new object, so bear that in mind when using it. You probably won\u2019t use the transient lifetime often; the majority of your services will probably be scoped instead.<\/p>\n<p>\u77ac\u6001\u751f\u547d\u5468\u671f\u53ef\u80fd\u4f1a\u5bfc\u81f4\u521b\u5efa\u5927\u91cf\u5bf9\u8c61\uff0c\u56e0\u6b64\u5b83\u4eec\u5bf9\u4e8e\u72b6\u6001\u5f88\u5c11\u6216\u6ca1\u6709\u72b6\u6001\u7684\u8f7b\u91cf\u7ea7\u670d\u52a1\u6700\u6709\u610f\u4e49\u3002\u4f7f\u7528 transient \u751f\u547d\u5468\u671f\u76f8\u5f53\u4e8e\u6bcf\u6b21\u9700\u8981\u65b0\u5bf9\u8c61\u65f6\u8c03\u7528 new\uff0c\u56e0\u6b64\u5728\u4f7f\u7528\u5b83\u65f6\u8bf7\u8bb0\u4f4f\u8fd9\u4e00\u70b9\u3002\u60a8\u53ef\u80fd\u4e0d\u4f1a\u4f7f\u7528\u77ac\u6001\u751f\u5b58\u671f\u901a\u5e38\u662f;\u60a8\u7684\u5927\u591a\u6570\u670d\u52a1\u53ef\u80fd\u4f1a\u6539\u4e3a\u9650\u5b9a\u8303\u56f4\u3002<\/p>\n<h3>9.4.2 Scoped: Let\u2019s stick together<\/h3>\n<h3>9.4.2 \u8303\u56f4\uff1a\u8ba9\u6211\u4eec\u56e2\u7ed3\u4e00\u81f4<\/h3>\n<p>The scoped lifetime states that a single instance of an object will be used within a given scope, but a different instance will be used between different scopes. In ASP.NET Core, a scope maps to a request, so within a single request, the container will use the same object to fulfill all dependencies.<\/p>\n<p>\u4f5c\u7528\u57df\u751f\u547d\u5468\u671f\u8868\u793a\u5c06\u5728\u7ed9\u5b9a\u8303\u56f4\u5185\u4f7f\u7528\u5bf9\u8c61\u7684\u5355\u4e2a\u5b9e\u4f8b\uff0c\u4f46\u5c06\u5728\u4e0d\u540c\u7684\u4f5c\u7528\u57df\u4e4b\u95f4\u4f7f\u7528\u4e0d\u540c\u7684\u5b9e\u4f8b\u3002\u5728 ASP.NET Core \u4e2d\uff0c\u8303\u56f4\u6620\u5c04\u5230\u8bf7\u6c42\uff0c\u56e0\u6b64\u5728\u5355\u4e2a\u8bf7\u6c42\u4e2d\uff0c\u5bb9\u5668\u5c06\u4f7f\u7528\u76f8\u540c\u7684\u5bf9\u8c61\u6765\u6ee1\u8db3\u6240\u6709\u4f9d\u8d56\u9879\u3002<\/p>\n<p>In the row-count example, within a single request (a single scope) the same DataContext is used throughout the dependency graph. The DataContext injected into the Repository is the same instance as the one injected into the RowCounts handler.<\/p>\n<p>\u5728\u884c\u8ba1\u6570\u793a\u4f8b\u4e2d\uff0c\u5728\u5355\u4e2a\u8bf7\u6c42\uff08\u5355\u4e2a\u8303\u56f4\uff09\u4e2d\uff0c\u5728\u6574\u4e2a\u4f9d\u8d56\u5173\u7cfb\u56fe\u4e2d\u4f7f\u7528\u76f8\u540c\u7684 DataContext\u3002\u6ce8\u5165 Repository \u7684 DataContext \u4e0e\u6ce8\u5165 RowCounts \u5904\u7406\u7a0b\u5e8f\u7684\u5b9e\u4f8b\u76f8\u540c\u3002<\/p>\n<p>In the next request, you\u2019re in a different scope, so the container creates a new instance of DataContext, as shown in figure 9.8. A different instance means a different RowCount for each request, as you can see. As before, figure 9.8 also shows the counts for previous requests.<\/p>\n<p>\u5728\u4e0b\u4e00\u4e2a\u8bf7\u6c42\u4e2d\uff0c\u60a8\u5904\u4e8e\u4e0d\u540c\u7684\u8303\u56f4\u5185\uff0c\u56e0\u6b64\u5bb9\u5668\u4f1a\u521b\u5efa\u4e00\u4e2a\u65b0\u7684 DataContext \u5b9e\u4f8b\uff0c\u5982\u56fe 9.8 \u6240\u793a\u3002\u5982\u60a8\u6240\u89c1\uff0c\u4e0d\u540c\u7684\u5b9e\u4f8b\u610f\u5473\u7740\u6bcf\u4e2a\u8bf7\u6c42\u7684 RowCount \u4e0d\u540c\u3002\u548c\u4ee5\u524d\u4e00\u6837\uff0c\u56fe 9.8 \u4e5f\u663e\u793a\u4e86\u5148\u524d\u8bf7\u6c42\u7684\u8ba1\u6570\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0908.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.8 Scoped dependencies use the same instance of DataContext within a single request but a new instance for a separate request. Consequently, the RowCounts are identical within a request.<br \/>\n\u56fe 9.8 \u4f5c\u7528\u57df\u4f9d\u8d56\u9879\u5728\u5355\u4e2a\u8bf7\u6c42\u4e2d\u4f7f\u7528\u76f8\u540c\u7684 DataContext \u5b9e\u4f8b\uff0c\u4f46\u5bf9\u5355\u72ec\u7684\u8bf7\u6c42\u4f7f\u7528\u65b0\u5b9e\u4f8b\u3002\u56e0\u6b64\uff0c\u8bf7\u6c42\u4e2d\u7684 RowCount\u662f\u76f8\u540c\u7684\u3002<\/p>\n<p>You can register dependencies as scoped by using the AddScoped extension methods. In this example, I registered DataContext as scoped and left Repository as transient, but you\u2019d get the same results in this case if both were scoped:<\/p>\n<p>\u60a8\u53ef\u4ee5\u4f7f\u7528 AddScoped \u6269\u5c55\u65b9\u6cd5\u5c06\u4f9d\u8d56\u9879\u6ce8\u518c\u4e3a scoped\u3002\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u6211\u5c06 DataContext \u6ce8\u518c\u4e3a\u8303\u56f4\uff0c\u5e76\u5c06 Repository \u4fdd\u7559\u4e3a transient\uff0c\u4f46\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u5982\u679c\u4e24\u8005\u90fd\u662f\u8303\u56f4\uff0c\u60a8\u5c06\u5f97\u5230\u76f8\u540c\u7684\u7ed3\u679c\uff1a<\/p>\n<pre><code>builder.Services.AddScoped&lt;DataContext&gt;();<\/code><\/pre>\n<p>Due to the nature of web requests, you\u2019ll often find services registered as scoped dependencies in ASP.NET Core. Database contexts and authentication services are common examples of services that should be scoped to a request\u2014anything that you want to share across your services within a single request but that needs to change between requests.<\/p>\n<p>\u7531\u4e8e Web \u8bf7\u6c42\u7684\u6027\u8d28\uff0c\u60a8\u7ecf\u5e38\u4f1a\u53d1\u73b0\u5728 ASP.NET Core \u4e2d\u6ce8\u518c\u4e3a\u8303\u56f4\u4f9d\u8d56\u9879\u7684\u670d\u52a1\u3002\u6570\u636e\u5e93\u4e0a\u4e0b\u6587\u548c\u8eab\u4efd\u9a8c\u8bc1\u670d\u52a1\u662f\u5e94\u5c06\u8303\u56f4\u9650\u5b9a\u4e3a\u8bf7\u6c42\u7684\u670d\u52a1\u7684\u5e38\u89c1\u793a\u4f8b\uff0c\u8bf7\u6c42\u662f\u60a8\u5e0c\u671b\u5728\u5355\u4e2a\u8bf7\u6c42\u4e2d\u8de8\u670d\u52a1\u5171\u4eab\u4f46\u9700\u8981\u5728\u8bf7\u6c42\u4e4b\u95f4\u66f4\u6539\u7684\u4efb\u4f55\u5185\u5bb9\u3002<\/p>\n<p><strong>NOTE<\/strong> If your scoped or transient services implement IDisposable, the DI container automatically disposes them<br \/>\nwhen the scope ends.<br \/>\n<strong>\u6ce8\u610f<\/strong> \u5982\u679c\u60a8\u7684\u8303\u56f4\u6216\u4e34\u65f6\u670d\u52a1\u5b9e\u73b0 IDisposable\uff0c\u5219 DI \u5bb9\u5668\u4f1a\u5728\u8303\u56f4\u7ed3\u675f\u65f6\u81ea\u52a8\u91ca\u653e\u5b83\u4eec\u3002<\/p>\n<p>Generally speaking, you\u2019ll find a lot of services registered using the scoped lifetime\u2014especially anything that uses a database, anything that\u2019s dependent on details of the HTTP request, or anything that uses a scoped service. But some services don\u2019t need to change between requests, such as a service that calculates the area of a circle or returns the current time in different time zones. For these services, a singleton lifetime might be more appropriate.<\/p>\n<p>\u4e00\u822c\u6765\u8bf4\uff0c\u60a8\u4f1a\u53d1\u73b0\u8bb8\u591a\u4f7f\u7528\u4f5c\u7528\u57df\u751f\u547d\u5468\u671f\u6ce8\u518c\u7684\u670d\u52a1\uff0c\u5c24\u5176\u662f\u4efb\u4f55\u4f7f\u7528\u6570\u636e\u5e93\u7684\u670d\u52a1\u3001\u4efb\u4f55\u4f9d\u8d56\u4e8e HTTP \u8bf7\u6c42\u8be6\u7ec6\u4fe1\u606f\u7684\u670d\u52a1\uff0c\u6216\u8005\u4efb\u4f55\u4f7f\u7528\u4f5c\u7528\u57df\u670d\u52a1\u7684\u670d\u52a1\u3002\u4f46\u6709\u4e9b\u670d\u52a1\u4e0d\u9700\u8981\u5728\u8bf7\u6c42\u4e4b\u95f4\u66f4\u6539\uff0c\u4f8b\u5982\u8ba1\u7b97\u5706\u7684\u9762\u79ef\u6216\u8fd4\u56de\u4e0d\u540c\u65f6\u533a\u7684\u5f53\u524d\u65f6\u95f4\u7684\u670d\u52a1\u3002\u5bf9\u4e8e\u8fd9\u4e9b\u670d\u52a1\uff0c\u5355\u4e00\u5b9e\u4f8b\u751f\u5b58\u671f\u53ef\u80fd\u66f4\u5408\u9002\u3002<\/p>\n<h3>9.4.3 Singleton: There can be only one<\/h3>\n<h3>9.4.3 Singleton\uff1a\u53ea\u80fd\u6709\u4e00\u4e2a<\/h3>\n<p>The singleton is a pattern that came before DI; the DI container provides a robust and easy-to-use implementation of it. The singleton is conceptually simple: an instance of the service is created when it\u2019s first needed (or during registration, as in section 9.2), and that\u2019s it. You\u2019ll always get the same instance injected into your services.<\/p>\n<p>singleton \u662f DI \u4e4b\u524d\u7684\u6a21\u5f0f;DI \u5bb9\u5668\u63d0\u4f9b\u4e86\u5f3a\u5927\u4e14\u6613\u4e8e\u4f7f\u7528\u7684\u5b9e\u73b0\u3002singleton \u5728\u6982\u5ff5\u4e0a\u5f88\u7b80\u5355\uff1a\u5728\u7b2c\u4e00\u6b21\u9700\u8981\u65f6\uff08\u6216\u5728\u6ce8\u518c\u671f\u95f4\uff0c\u5982 9.2 \u8282\uff09\u521b\u5efa\u670d\u52a1\u7684\u5b9e\u4f8b\uff0c\u4ec5\u6b64\u800c\u5df2\u3002\u60a8\u5c06\u59cb\u7ec8\u5c06\u76f8\u540c\u7684\u5b9e\u4f8b\u6ce8\u5165\u5230\u60a8\u7684\u670d\u52a1\u4e2d\u3002<\/p>\n<p>The singleton pattern is particularly useful for objects that are expensive to create, contain data that must be shared across requests, or don\u2019t hold state. The latter two points are important: any service registered as a singleton should be thread-safe.<\/p>\n<p>\u5bf9\u4e8e\u521b\u5efa\u6210\u672c\u9ad8\u6602\u3001\u5305\u542b\u5fc5\u987b\u5728\u8bf7\u6c42\u4e4b\u95f4\u5171\u4eab\u7684\u6570\u636e\u6216\u4e0d\u4fdd\u5b58\u72b6\u6001\u7684\u5bf9\u8c61\uff0c\u5355\u72ec\u6a21\u5f0f\u7279\u522b\u6709\u7528\u3002\u540e\u4e24\u70b9\u5f88\u91cd\u8981\uff1a\u4efb\u4f55\u6ce8\u518c\u4e3a\u5355\u4e00\u5b9e\u4f8b\u7684\u670d\u52a1\u90fd\u5e94\u8be5\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\u3002<\/p>\n<p><strong>Warning<\/strong> Singleton services must be thread-safe in a web application, as they\u2019ll typically be used by multiple threads during concurrent requests.<br \/>\n<strong>\u8b66\u544a<\/strong> \u5355\u4f8b\u670d\u52a1\u5728 Web \u5e94\u7528\u7a0b\u5e8f\u4e2d\u5fc5\u987b\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\uff0c\u56e0\u4e3a\u5b83\u4eec\u901a\u5e38\u5728\u5e76\u53d1\u8bf7\u6c42\u671f\u95f4\u7531\u591a\u4e2a\u7ebf\u7a0b\u4f7f\u7528\u3002<\/p>\n<p>Let\u2019s consider what using singletons means for the row-count example. We can update the registration of DataContext to be a singleton:<\/p>\n<p>\u8ba9\u6211\u4eec\u8003\u8651\u4e00\u4e0b\u4f7f\u7528\u5355\u4f8b\u5bf9\u884c\u8ba1\u6570\u793a\u4f8b\u610f\u5473\u7740\u4ec0\u4e48\u3002\u6211\u4eec\u53ef\u4ee5\u5c06 DataContext \u7684\u6ce8\u518c\u66f4\u65b0\u4e3a\u5355\u4f8b\uff1a<\/p>\n<pre><code>builder.Services.AddSingleton&lt;DataContext&gt;();<\/code><\/pre>\n<p>Then we can call the RowCounts handler and observe the results in figure 9.9. We can see that every instance has returned the same value, indicating that the same instance of DataContext is used in every request, both when injected directly into the endpoint handler and when referenced transitively by Repository.<\/p>\n<p>\u7136\u540e\u6211\u4eec\u53ef\u4ee5\u8c03\u7528 RowCounts \u5904\u7406\u7a0b\u5e8f\u5e76\u89c2\u5bdf\u56fe 9.9 \u4e2d\u7684\u7ed3\u679c\u3002\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u6bcf\u4e2a\u5b9e\u4f8b\u90fd\u8fd4\u56de\u4e86\u76f8\u540c\u7684\u503c\uff0c\u8fd9\u8868\u660e\u6bcf\u4e2a\u8bf7\u6c42\u90fd\u4f7f\u7528\u4e86\u76f8\u540c\u7684 DataContext \u5b9e\u4f8b\uff0c\u65e0\u8bba\u662f\u76f4\u63a5\u6ce8\u5165\u5230\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f\u4e2d\u65f6\uff0c\u8fd8\u662f\u88ab Repository \u4f20\u9012\u5f15\u7528\u65f6\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0909.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.9 Any service registered as a singleton always returns the same instance. Consequently, all the calls to the RowCounts handler return the same value, both within a request and between requests.<br \/>\n\u56fe 9.9 \u4efb\u4f55\u6ce8\u518c\u4e3a\u5355\u4f8b\u7684\u670d\u52a1\u603b\u662f\u8fd4\u56de\u76f8\u540c\u7684\u5b9e\u4f8b\u3002\u56e0\u6b64\uff0c\u5bf9 RowCounts \u5904\u7406\u7a0b\u5e8f\u7684\u6240\u6709\u8c03\u7528\u5728\u8bf7\u6c42\u5185\u548c\u8bf7\u6c42\u4e4b\u95f4\u90fd\u8fd4\u56de\u76f8\u540c\u7684\u503c\u3002<\/p>\n<p>Singletons are convenient for objects that need to be shared or that are immutable and expensive to create. A caching service should be a singleton, as all requests need to share the service. It must be thread-safe, though. Similarly, you might register a settings object loaded from a remote server as a singleton if you load the settings once at startup and reuse them through the lifetime of your app.<\/p>\n<p>\u5355\u4f8b\u5bf9\u4e8e\u9700\u8981\u5171\u4eab\u6216\u4e0d\u53ef\u53d8\u4e14\u521b\u5efa\u6210\u672c\u9ad8\u6602\u7684\u5bf9\u8c61\u6765\u8bf4\u5f88\u65b9\u4fbf\u3002\u7f13\u5b58\u670d\u52a1\u5e94\u8be5\u662f\u5355\u4e00\u5b9e\u4f8b\uff0c\u56e0\u4e3a\u6240\u6709\u8bf7\u6c42\u90fd\u9700\u8981\u5171\u4eab\u8be5\u670d\u52a1\u3002\u4e0d\u8fc7\uff0c\u5b83\u5fc5\u987b\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\u3002\u540c\u6837\uff0c\u5982\u679c\u60a8\u5728\u542f\u52a8\u65f6\u52a0\u8f7d\u4e00\u6b21\u8bbe\u7f6e\uff0c\u5e76\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u751f\u547d\u5468\u671f\u4e2d\u91cd\u590d\u4f7f\u7528\u5b83\u4eec\uff0c\u5219\u53ef\u4ee5\u5c06\u4ece\u8fdc\u7a0b\u670d\u52a1\u5668\u52a0\u8f7d\u7684\u8bbe\u7f6e\u5bf9\u8c61\u6ce8\u518c\u4e3a\u5355\u4e00\u5b9e\u4f8b\u3002<\/p>\n<p>On the face of it, choosing a lifetime for a service may not seem to be too tricky. But an important gotcha can come back to bite you in subtle ways, as you\u2019ll see in section 9.4.4.<\/p>\n<p>\u4ece\u8868\u9762\u4e0a\u770b\uff0c\u4e3a\u670d\u52a1\u9009\u62e9\u751f\u547d\u5468\u671f\u4f3c\u4e4e\u5e76\u4e0d\u592a\u68d8\u624b\u3002\u4f46\u662f\u4e00\u4e2a\u91cd\u8981\u7684\u95ee\u9898\u53ef\u80fd\u4f1a\u4ee5\u5fae\u5999\u7684\u65b9\u5f0f\u56de\u6765\u54ac\u4f60\uff0c\u6b63\u5982\u60a8\u5c06\u5728 9.4.4 \u8282\u4e2d\u770b\u5230\u7684\u90a3\u6837\u3002<\/p>\n<h3>9.4.4 Keeping an eye out for captive dependencies<\/h3>\n<h3>9.4.4 \u5bc6\u5207\u5173\u6ce8\u6355\u83b7\u4f9d\u8d56\u9879<\/h3>\n<p>Suppose that you\u2019re configuring the lifetime for the DataContext and Repository examples. You think about the suggestions I\u2019ve provided and decide on the following lifetimes:<\/p>\n<p>\u5047\u8bbe\u60a8\u6b63\u5728\u4e3a DataContext \u548c Repository \u793a\u4f8b\u914d\u7f6e\u751f\u547d\u5468\u671f\u3002\u60a8\u8003\u8651\u6211\u63d0\u4f9b\u7684\u5efa\u8bae\u5e76\u51b3\u5b9a\u4ee5\u4e0b\u751f\u547d\u5468\u671f\uff1a<\/p>\n<ul>\n<li>\n<p>DataContext\u2014Scoped, as it should be shared for a single request<br \/>\nDataContext \u2014 \u8303\u56f4\u9650\u5b9a\uff0c\u56e0\u4e3a\u5b83\u5e94\u8be5\u4e3a\u5355\u4e2a\u8bf7\u6c42\u5171\u4eab<\/p>\n<\/li>\n<li>\n<p>Repository\u2014Singleton, as it has no state of its own and is thread-safe, so why not?<br \/>\n\u5b58\u50a8\u5e93 \u2014 \u5355\u4f8b\uff0c\u56e0\u4e3a\u5b83\u6ca1\u6709\u81ea\u5df1\u7684\u72b6\u6001\u5e76\u4e14\u662f\u7ebf\u7a0b\u5b89\u5168\u7684\uff0c\u90a3\u4e48\u4e3a\u4ec0\u4e48\u4e0d\u5462\uff1f<\/p>\n<\/li>\n<\/ul>\n<p>Warning This lifetime configuration is to explore a bug. Don\u2019t use it in your code; if you do, you\u2019ll experience a similar problem!<br \/>\n\u8b66\u544a \u6b64\u751f\u547d\u5468\u671f\u914d\u7f6e\u7528\u4e8e\u63a2\u7d22 bug\u3002\u4e0d\u8981\u5728\u4ee3\u7801\u4e2d\u4f7f\u7528\u5b83;\u5982\u679c\u4f60\u8fd9\u6837\u505a\uff0c\u4f60\u4f1a\u9047\u5230\u7c7b\u4f3c\u7684\u95ee\u9898\uff01<\/p>\n<p>Unfortunately, you\u2019ve created a captive dependency because you\u2019re injecting a scoped object, DataContext, into a singleton, Repository. As it\u2019s a singleton, the same Repository instance is used throughout the lifetime of the app, so the DataContext that was injected into it will also hang around, even though a new one should be used with every request. Figure 9.10 shows this scenario, in which a new instance of DataContext is created for each scope but the instance inside Repository hangs around for the lifetime of the app.<\/p>\n<p>\u9057\u61be\u7684\u662f\uff0c\u60a8\u521b\u5efa\u4e86\u6355\u83b7\u4f9d\u8d56\u9879\uff0c\u56e0\u4e3a\u60a8\u6b63\u5728\u5c06\u8303\u56f4\u5bf9\u8c61 DataContext \u6ce8\u5165\u5230\u5355\u4e00\u5b9e\u4f8b Repository \u4e2d\u3002\u7531\u4e8e\u5b83\u662f\u4e00\u4e2a\u5355\u4f8b\uff0c\u56e0\u6b64\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u6574\u4e2a\u751f\u547d\u5468\u671f\u4e2d\u4f7f\u7528\u76f8\u540c\u7684 Repository \u5b9e\u4f8b\uff0c\u56e0\u6b64\u6ce8\u5165\u5176\u4e2d\u7684 DataContext \u4e5f\u5c06\u6302\u8d77\uff0c\u5373\u4f7f\u6bcf\u4e2a\u8bf7\u6c42\u90fd\u5e94\u8be5\u4f7f\u7528\u4e00\u4e2a\u65b0\u7684 DataContext \u4e5f\u662f\u5982\u6b64\u3002\u56fe 9.10 \u663e\u793a\u4e86\u8fd9\u79cd\u60c5\u51b5\uff0c\u5176\u4e2d\u4e3a\u6bcf\u4e2a\u8303\u56f4\u521b\u5efa\u4e86\u4e00\u4e2a\u65b0\u7684 DataContext \u5b9e\u4f8b\uff0c\u4f46Repository \u4e2d\u7684\u5b9e\u4f8b\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u751f\u547d\u5468\u671f\u5185\u6302\u8d77\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0910.png\" alt=\"alt text\" \/><\/p>\n<p>Listing 9.10 DataContext is registered as a scoped dependency, but Repository is a singleton. Even though you expect a new DataContext for every request, Repository captures the injected DataContext and causes it to be reused for the lifetime of the app.<\/p>\n<p>\u56fe 9.10 DataContext \u6ce8\u518c\u4e3a\u8303\u56f4\u4f9d\u8d56\u9879\uff0c\u4f46 Repository \u662f\u5355\u4f8b\u3002\u5373\u4f7f\u60a8\u5e0c\u671b\u6bcf\u4e2a\u8bf7\u6c42\u90fd\u6709\u4e00\u4e2a\u65b0\u7684 DataContext\uff0cRepository \u4e5f\u4f1a\u6355\u83b7 \u6ce8\u5165\u7684 DataContext\uff0c\u5e76\u4f7f\u5176\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u751f\u547d\u5468\u671f\u5185\u91cd\u590d\u4f7f\u7528\u3002<\/p>\n<p>Captive dependencies can cause subtle bugs that are hard to root out, so you should always keep an eye out for them. These captive dependencies are relatively easy to introduce, so always think carefully when registering a singleton service.<\/p>\n<p>\u6355\u83b7\u4f9d\u8d56\u9879\u53ef\u80fd\u4f1a\u5bfc\u81f4\u96be\u4ee5\u6839\u9664\u7684\u7ec6\u5fae\u9519\u8bef\uff0c\u56e0\u6b64\u60a8\u5e94\u8be5\u59cb\u7ec8\u7559\u610f\u5b83\u4eec\u3002\u8fd9\u4e9b\u6355\u83b7\u4f9d\u8d56\u9879\u76f8\u5bf9\u5bb9\u6613\u5f15\u5165\uff0c\u56e0\u6b64\u5728\u6ce8\u518c singleton \u670d\u52a1\u65f6\u8bf7\u59cb\u7ec8\u4ed4\u7ec6\u8003\u8651\u3002<\/p>\n<p><strong><em>Warning<\/em><\/strong> A service should use only dependencies that have a lifetime longer than or equal to the service\u2019s lifetime. A service registered as a singleton can safely use only singleton dependencies. A service registered as scoped can safely use scoped or singleton dependencies. A transient service can use dependencies with any lifetime.<br \/>\n<strong><em>\u8b66\u544a\uff1a<\/em><\/strong> \u670d\u52a1\u5e94\u4ec5\u4f7f\u7528\u751f\u547d\u5468\u671f\u957f\u4e8e\u6216\u7b49\u4e8e\u670d\u52a1\u751f\u547d\u5468\u671f\u7684\u4f9d\u8d56\u9879\u3002\u6ce8\u518c\u4e3a\u5355\u4e00\u5b9e\u4f8b\u7684\u670d\u52a1\u53ef\u4ee5\u5b89\u5168\u5730\u4ec5\u4f7f\u7528\u5355\u4e00\u5b9e\u4f8b\u4f9d\u8d56\u9879\u3002\u6ce8\u518c\u4e3a scoped \u7684\u670d\u52a1\u53ef\u4ee5\u5b89\u5168\u5730\u4f7f\u7528 scoped \u6216\u5355\u4e00\u5b9e\u4f8b\u4f9d\u8d56\u9879\u3002\u4e34\u65f6\u670d\u52a1\u53ef\u4ee5\u4f7f\u7528\u4efb\u4f55\u751f\u547d\u5468\u671f\u7684\u4f9d\u8d56\u9879\u3002<\/p>\n<p>At this point, I should mention one glimmer of hope in this cautionary tale: ASP.NET Core automatically checks for these kinds of captive dependencies and throws an exception on application startup if it detects them, or on first use of a captive dependency, as shown in figure 9.11.<\/p>\n<p>\u5728\u8fd9\u4e00\u70b9\u4e0a\uff0c\u6211\u5e94\u8be5\u5728\u8fd9\u4e2a\u8b66\u793a\u6545\u4e8b\u4e2d\u63d0\u5230\u4e00\u4e1d\u5e0c\u671b\uff1aASP.NET Core \u4f1a\u81ea\u52a8\u68c0\u67e5\u8fd9\u4e9b\u7c7b\u578b\u7684\u6355\u83b7\u4f9d\u8d56\u9879\uff0c\u5e76\u5728\u5e94\u7528\u7a0b\u5e8f\u542f\u52a8\u65f6\u6216\u9996\u6b21\u4f7f\u7528\u6355\u83b7\u4f9d\u8d56\u9879\u65f6\u5f15\u53d1\u5f02\u5e38\uff0c\u5982\u56fe 9.11 \u6240\u793a\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0911.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.11 When ValidateScopes is enabled, the DI container throws an exception when it creates a service with a captive dependency. By default, this check is enabled only for development environments.<br \/>\n\u56fe 9.11 \u542f\u7528 ValidateScopes \u540e\uff0cDI \u5bb9\u5668\u5728\u521b\u5efa\u5177\u6709\u6355\u83b7\u4f9d\u8d56\u9879\u7684\u670d\u52a1\u65f6\u4f1a\u5f15\u53d1\u5f02\u5e38\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u4ec5\u5bf9\u5f00\u53d1\u73af\u5883\u542f\u7528\u6b64\u68c0\u67e5\u3002<\/p>\n<p>This scope validation check has a performance effect, so by default it\u2019s enabled only when your app is running in a development environment, but it should help you catch most problems of this kind. You can enable or disable this check regardless of environment by configuring the ValidateScopes option on your WebApplicationBuilder in Program.cs by using the Host property, as shown in the following listing.<\/p>\n<p>\u6b64\u8303\u56f4\u9a8c\u8bc1\u68c0\u67e5\u4f1a\u4ea7\u751f\u6027\u80fd\u6210\u672c\uff0c\u56e0\u6b64\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u4ec5\u5f53\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u5728\u5f00\u53d1\u73af\u5883\u4e2d\u8fd0\u884c\u65f6\uff0c\u5b83\u624d\u4f1a\u542f\u7528\uff0c\u4f46\u5b83\u5e94\u8be5\u53ef\u4ee5\u5e2e\u52a9\u60a8\u6355\u83b7\u5927\u591a\u6570\u6b64\u7c7b\u95ee\u9898\u3002\u65e0\u8bba\u73af\u5883\u5982\u4f55\uff0c\u60a8\u90fd\u53ef\u4ee5\u901a\u8fc7\u4f7f\u7528 Host \u5c5e\u6027\u5728 Program.cs WebApplicationBuilder \u4e0a\u914d\u7f6e ValidateScopes \u9009\u9879\u6765\u542f\u7528\u6216\u7981\u7528\u6b64\u68c0\u67e5\uff0c\u5982\u4e0b\u9762\u7684\u6e05\u5355\u6240\u793a\u3002<\/p>\n<h3>Listing 9.13 Setting the ValidateScopes property to always validate scopes<\/h3>\n<h3>Listing 9.13 \u8bbe\u7f6eValidateScopes\u5c5e\u6027\u4ee5\u59cb\u7ec8\u9a8c\u8bc1\u8303\u56f4<\/h3>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args); \u2776\n\nbuilder.Host.UseDefaultServiceProvider(o =&gt; \u2777\n{\n    o.ValidateScopes = true; \u2778\n    o.ValidateOnBuild = true; \u2779\n});\n<\/code><\/pre>\n<p>\u2776 The default builder sets ValidateScopes to validate only in development<br \/>\nenvironments.<br \/>\n\u9ed8\u8ba4\u751f\u6210\u5668\u5c06 ValidateScopes \u8bbe\u7f6e\u4e3a\u4ec5\u5728\u5f00\u53d1\u73af\u5883\u4e2d\u9a8c\u8bc1\u3002<\/p>\n<p>\u2777 You can override the validation check with the UseDefaultServiceProvider<br \/>\nextension.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 UseDefaultServiceProvider \u6269\u5c55\u8986\u76d6\u9a8c\u8bc1\u68c0\u67e5\u3002<\/p>\n<p>\u2778 Setting this to true will validate scopes in all environments, which has<br \/>\nperformance implications.<br \/>\n\u5c06\u6b64\u9879\u8bbe\u7f6e\u4e3a true \u5c06\u9a8c\u8bc1\u6240\u6709\u73af\u5883\u4e2d\u7684\u4f5c\u7528\u57df\uff0c\u8fd9\u4f1a\u5f71\u54cd\u6027\u80fd\u3002<\/p>\n<p>\u2779 ValidateOnBuild checks that every registered service has all its dependencies registered.<br \/>\nValidateOnBuild \u68c0\u67e5\u6bcf\u4e2a\u5df2\u6ce8\u518c\u7684\u670d\u52a1\u662f\u5426\u90fd\u5df2\u6ce8\u518c\u5176\u6240\u6709\u4f9d\u8d56\u9879\u3002<\/p>\n<p>Listing 9.13 shows another setting you can enable, ValidateOnBuild, which goes one step further. When the setting is enabled, the DI container checks on application startup that it has dependencies registered for every service it needs to build. If it doesn\u2019t, it throws an exception and shuts down the app, as shown in figure 9.12, letting you know about the misconfiguration. This setting also has a performance effect, so it\u2019s enabled only in development environments by default, but it\u2019s useful for pointing out any missed service registrations.<\/p>\n<p>\u6e05\u5355 9.13 \u663e\u793a\u4e86\u53e6\u4e00\u4e2a\u4f60\u53ef\u4ee5\u542f\u7528\u7684\u8bbe\u7f6e\uff0cValidateOnBuild\uff0c\u5b83\u66f4\u8fdb\u4e00\u6b65\u3002\u542f\u7528\u8be5\u8bbe\u7f6e\u540e\uff0cDI \u5bb9\u5668\u4f1a\u5728\u5e94\u7528\u7a0b\u5e8f\u542f\u52a8\u65f6\u68c0\u67e5\u5b83\u662f\u5426\u4e3a\u9700\u8981\u6784\u5efa\u7684\u6bcf\u4e2a\u670d\u52a1\u6ce8\u518c\u4e86\u4f9d\u8d56\u9879\u3002\u5982\u679c\u6ca1\u6709\uff0c\u5b83\u4f1a\u629b\u51fa\u4e00\u4e2a\u5f02\u5e38\u5e76\u5173\u95ed\u5e94\u7528\u7a0b\u5e8f\uff0c\u5982\u56fe 9.12 \u6240\u793a\uff0c\u8ba9\u60a8\u77e5\u9053\u914d\u7f6e\u9519\u8bef\u3002\u6b64\u8bbe\u7f6e\u4e5f\u6709\u6027\u80fd\u6210\u672c\uff0c\u56e0\u6b64\u9ed8\u8ba4\u60c5\u51b5\u4e0b\u4ec5\u5728\u5f00\u53d1\u73af\u5883\u4e2d\u542f\u7528\uff0c\u4f46\u5b83\u5bf9\u4e8e\u6307\u51fa\u4efb\u4f55\u9519\u8fc7\u7684\u670d\u52a1\u6ce8\u518c\u975e\u5e38\u6709\u7528\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0912.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.12 When ValidateOnBuild is enabled, the DI container checks on app startup that it can create all the registered services. If it finds a service it can\u2019t create, it throws an exception. By default, this check is enabled only for development environments.<br \/>\n\u56fe 9.12 \u542f\u7528 ValidateOnBuild \u540e\uff0cDI \u5bb9\u5668\u4f1a\u5728\u5e94\u7528\u7a0b\u5e8f\u542f\u52a8\u65f6\u68c0\u67e5\u5b83\u662f\u5426\u53ef\u4ee5\u521b\u5efa\u6240\u6709\u5df2\u6ce8\u518c\u7684\u670d\u52a1\u3002\u5982\u679c\u5b83\u627e\u5230\u4e00\u4e2a\u670d\u52a1\uff0c\u5b83\u5c31\u627e\u4e0d\u5230create \u65f6\uff0c\u5b83\u4f1a\u5f15\u53d1\u5f02\u5e38\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0c\u4ec5\u5bf9\u5f00\u53d1\u73af\u5883\u542f\u7528\u6b64\u68c0\u67e5\u3002<\/p>\n<p><strong>Warning<\/strong> Unfortunately, the container can\u2019t catch everything. For a list of caveats and exceptions, see this post from my blog: <a href=\"http:\/\/mng.bz\/QmwG\">http:\/\/mng.bz\/QmwG<\/a>.<br \/>\n<strong>\u8b66\u544a<\/strong> \u9057\u61be\u7684\u662f\uff0c\u5bb9\u5668\u65e0\u6cd5\u6355\u83b7\u6240\u6709\u5185\u5bb9\u3002\u6709\u5173\u6ce8\u610f\u4e8b\u9879\u548c\u4f8b\u5916\u60c5\u51b5\u7684\u5217\u8868\uff0c\u8bf7\u53c2\u9605\u6211\u535a\u5ba2\u4e2d\u7684\u8fd9\u7bc7\u6587\u7ae0\uff1a<a href=\"http:\/\/mng.bz\/QmwG\">http:\/\/mng.bz\/QmwG<\/a>\u3002<\/p>\n<p>We\u2019ve almost covered everything about dependency injection now, and there\u2019s only one more thing to consider: how to resolve scoped services on app startup in Program.cs.<\/p>\n<p>\u6211\u4eec\u73b0\u5728\u51e0\u4e4e\u5df2\u7ecf\u6db5\u76d6\u4e86\u6709\u5173\u4f9d\u8d56\u9879\u6ce8\u5165\u7684\u6240\u6709\u5185\u5bb9\uff0c\u73b0\u5728\u53ea\u9700\u8981\u8003\u8651\u4e00\u4ef6\u4e8b\uff1a\u5982\u4f55\u5728 Program.cs \u4e2d\u89e3\u6790\u5e94\u7528\u7a0b\u5e8f\u542f\u52a8\u65f6\u7684\u4f5c\u7528\u57df\u670d\u52a1\u3002<\/p>\n<h2>9.5 Resolving scoped services outside a request<\/h2>\n<h2>9.5 \u5728\u8bf7\u6c42\u4e4b\u5916\u89e3\u6790\u5206\u533a\u670d\u52a1<\/h2>\n<p>In chapter 8, I said that there are two main ways to resolve services from the DI container for minimal API applications:<\/p>\n<p>\u5728\u7b2c 8 \u7ae0\u4e2d\uff0c\u6211\u8bf4\u8fc7\uff0c\u5bf9\u4e8e\u6700\u5c0f\u7684 API \u5e94\u7528\u7a0b\u5e8f\uff0c\u6709\u4e24\u79cd\u4e3b\u8981\u65b9\u6cd5\u53ef\u4ee5\u4ece DI \u5bb9\u5668\u4e2d\u89e3\u6790\u670d\u52a1\uff1a<\/p>\n<ul>\n<li>\n<p>Injecting services into an endpoint handler<br \/>\n\u5c06\u670d\u52a1\u6ce8\u5165\u7aef\u70b9\u5904\u7406\u7a0b\u5e8f<\/p>\n<\/li>\n<li>\n<p>Accessing the DI container directly in Program.cs<br \/>\n\u76f4\u63a5\u5728 Program.cs \u4e2d\u8bbf\u95ee DI \u5bb9\u5668<\/p>\n<\/li>\n<\/ul>\n<p>You\u2019ve seen the first of those approaches several times now in this chapter. In chapter 8 you saw that you can access services in Program.cs by calling <code>GetRequiredService&lt;T&gt;()<\/code> on WebApplication.Services:<\/p>\n<p>\u5728\u672c\u7ae0\u4e2d\uff0c\u60a8\u5df2\u7ecf\u591a\u6b21\u770b\u5230\u8fd9\u4e9b\u65b9\u6cd5\u4e2d\u7684\u7b2c\u4e00\u79cd\u3002\u5728\u7b2c 8 \u7ae0\u4e2d\uff0c\u60a8\u770b\u5230\u60a8\u53ef\u4ee5\u901a\u8fc7\u5bf9 WebApplication.Services \u8c03\u7528 <code>GetRequiredService&lt;T&gt;\uff08\uff09<\/code> \u6765\u8bbf\u95ee Program.cs \u4e2d\u7684\u670d\u52a1\uff1a<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nWebApplication app = builder.Build();\nvar settings = app.Services.GetRequiredService&lt;EmailServerSettings&gt;();<\/code><\/pre>\n<p>It\u2019s important, however, that you resolve only singleton services this way. The IServiceProvider exposed as WebApplication.Services is the root DI container for your app. Services resolved this way live for the lifetime of your app, which is fine for singleton services but typically isn\u2019t the behavior you want for scoped or transient services.<\/p>\n<p>\u4f46\u662f\uff0c\u8bf7\u52a1\u5fc5\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u4ec5\u89e3\u6790\u5355\u4e00\u5b9e\u4f8b\u670d\u52a1\u3002\u4f5c\u4e3a WebApplication.Services \u516c\u5f00\u7684 IServiceProvider \u662f\u5e94\u7528\u7684\u6839 DI \u5bb9\u5668\u3002\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u89e3\u6790\u7684\u670d\u52a1\u5728\u5e94\u7528\u7a0b\u5e8f\u7684\u751f\u547d\u5468\u671f\u5185\u6709\u6548\uff0c\u8fd9\u5bf9\u4e8e\u5355\u4e00\u5b9e\u4f8b\u670d\u52a1\u6765\u8bf4\u5f88\u597d\uff0c\u4f46\u901a\u5e38\u4e0d\u662f\u60a8\u60f3\u8981\u7684\u4f5c\u7528\u57df\u6216\u77ac\u6001\u670d\u52a1\u7684\u884c\u4e3a\u3002<\/p>\n<p><strong>Warning<\/strong> Don\u2019t resolve scoped or transient services directly from WebApplication.Services. This approach can lead to leaking of memory, as the objects are kept alive till the app exits and aren\u2019t garbage-collected.<br \/>\n<strong>\u8b66\u544a<\/strong> \u4e0d\u8981\u76f4\u63a5\u4ece WebApplication.Services \u89e3\u6790\u8303\u56f4\u6216\u6682\u65f6\u6027\u670d\u52a1\u3002\u8fd9\u79cd\u65b9\u6cd5\u53ef\u80fd\u4f1a\u5bfc\u81f4\u5185\u5b58\u6cc4\u6f0f\uff0c\u56e0\u4e3a\u5bf9\u8c61\u5728\u5e94\u7528\u7a0b\u5e8f\u9000\u51fa\u4e4b\u524d\u4fdd\u6301\u6d3b\u52a8\u72b6\u6001\uff0c\u5e76\u4e14\u4e0d\u4f1a\u8fdb\u884c\u5783\u573e\u56de\u6536\u3002<\/p>\n<p>Instead, you should only resolve scoped and transient services from an active scope. A new scope is created automatically for every HTTP request, but when you\u2019re resolving services from the DI container directly in Program.cs (or anywhere else that\u2019s outside the context of an HTTP request), you need to create (and dispose of) a scope manually.<\/p>\n<p>\u76f8\u53cd\uff0c\u60a8\u5e94\u8be5\u53ea\u4ece\u6d3b\u52a8\u8303\u56f4\u89e3\u6790\u8303\u56f4\u670d\u52a1\u548c\u6682\u65f6\u6027\u670d\u52a1\u3002\u7cfb\u7edf\u4f1a\u81ea\u52a8\u4e3a\u6bcf\u4e2a HTTP \u8bf7\u6c42\u521b\u5efa\u4e00\u4e2a\u65b0\u8303\u56f4\uff0c\u4f46\u662f\u5f53\u60a8\u5728 Program.cs \u4e2d\uff08\u6216 HTTP \u8bf7\u6c42\u4e0a\u4e0b\u6587\u4e4b\u5916\u7684\u4efb\u4f55\u5176\u4ed6\u4f4d\u7f6e\uff09\u4e2d\u76f4\u63a5\u4ece DI \u5bb9\u5668\u4e2d\u89e3\u6790\u670d\u52a1\u65f6\uff0c\u60a8\u9700\u8981\u624b\u52a8\u521b\u5efa\uff08\u548c\u91ca\u653e\uff09\u8303\u56f4\u3002<\/p>\n<p>You can create a new scope by calling CreateScope() or CreateAsyncScope() on IServiceProvider, which returns a disposable IServiceScope object, as shown in figure 9.13. IServiceScope also exposes an IServiceProvider property, but any services resolved from this provider are disposed of automatically when you dispose the IServiceScope, ensuring that all the resources held by the scoped and transient services are released correctly.<\/p>\n<p>\u60a8\u53ef\u4ee5\u901a\u8fc7\u5bf9 IServiceProvider \u8c03\u7528 CreateScope\uff08\uff09 \u6216 CreateAsyncScope\uff08\uff09 \u6765\u521b\u5efa\u65b0\u8303\u56f4\uff0c\u8fd9\u5c06\u8fd4\u56de\u4e00\u4e2a\u53ef\u91ca\u653e\u7684 IServiceScope \u5bf9\u8c61\uff0c\u5982\u56fe 9.13 \u6240\u793a\u3002IServiceScope \u8fd8\u516c\u5f00 IServiceProvider \u5c5e\u6027\uff0c\u4f46\u5728\u91ca\u653e IServiceScope \u65f6\uff0c\u5c06\u81ea\u52a8\u91ca\u653e\u4ece\u6b64\u63d0\u4f9b\u7a0b\u5e8f\u89e3\u6790\u7684\u4efb\u4f55\u670d\u52a1\uff0c\u4ece\u800c\u786e\u4fdd\u6b63\u786e\u91ca\u653e\u4f5c\u7528\u57df\u670d\u52a1\u548c\u6682\u65f6\u6027\u670d\u52a1\u6240\u6301\u6709\u7684\u6240\u6709\u8d44\u6e90\u3002<\/p>\n<p><img decoding=\"async\" src=\"\/images\/aspnetcoreinaction\/0913.png\" alt=\"alt text\" \/><\/p>\n<p>Figure 9.13 To resolve scoped or transient services manually, you must create an IServiceScope object by calling CreateScope() on WebApplication.Services. Any scoped or transient services resolved from the DI container exposed as IServiceScope.ServiceProvider are disposed of automatically when you dispose of the IServiceScope object.<br \/>\n\u56fe 9.13 \u8981\u624b\u52a8\u89e3\u6790\u4f5c\u7528\u57df\u6216\u4e34\u65f6\u670d\u52a1\uff0c\u5fc5\u987b\u901a\u8fc7\u5728 WebApplication.Services \u4e0a\u8c03\u7528 CreateScope\uff08\uff09 \u6765\u521b\u5efa IServiceScope \u5bf9\u8c61\u3002\u5728\u91ca\u653e IServiceScope \u5bf9\u8c61\u65f6\uff0c\u5c06\u81ea\u52a8\u91ca\u653e\u4ece\u516c\u5f00\u4e3a IServiceScope.ServiceProvider \u7684 DI \u5bb9\u5668\u89e3\u6790\u7684\u4efb\u4f55\u4f5c\u7528\u57df\u6216\u6682\u65f6\u6027\u670d\u52a1\u3002<\/p>\n<p>The following listing shows how you can resolve a scoped service in Program.cs using the pattern in figure 9.13. This pattern ensures that the scoped DataContext object is disposed of correctly before the call to app.Run().<\/p>\n<p>\u4e0b\u9762\u7684\u6e05\u5355\u663e\u793a\u4e86\u5982\u4f55\u4f7f\u7528\u56fe 9.13 \u4e2d\u7684\u6a21\u5f0f\u5728 Program.cs \u4e2d\u89e3\u6790\u8303\u56f4\u670d\u52a1\u3002\u8fd9<br \/>\npattern \u786e\u4fdd\u5728\u8c03\u7528 app \u4e4b\u524d\u6b63\u786e\u5904\u7406\u4f5c\u7528\u57df\u5185\u7684 DataContext \u5bf9\u8c61\u3002<\/p>\n<p>Listing 9.14 Resolving a scoped service using IServiceScope in Program.cs<br \/>\n\u6e05\u5355 9.14 \u4f7f\u7528Program.cs \u4e2d\u7684 IServiceScope<\/p>\n<pre><code>WebApplicationBuilder builder = WebApplication.CreateBuilder(args);\n\nbuilder.Services.AddScoped&lt;DataContext&gt;(); \u2776\n\nWebApplication app = builder.Build();\n\nawait using (var scope = app.Services.CreateAsyncScope()) \u2777\n{\n    var dbContext = \u2778\n        scope.ServiceProvider.GetRequiredService&lt;DataContext&gt;(); \u2778\n    Console.WriteLine($&quot;Retrieved scope: {dbContext.RowCount}&quot;);\n} \u2779\napp.Run();<\/code><\/pre>\n<p>\u2776 DataContext is registered as scoped, so it shouldn\u2019t be resolved directly from app.Services.<br \/>\nDataContext \u5df2\u6ce8\u518c\u4e3a scoped\uff0c\u56e0\u6b64\u4e0d\u5e94\u76f4\u63a5\u4ece app \u89e3\u6790app.Services\u3002<\/p>\n<p>\u2777 Creates an IServiceScope<br \/>\n\u521b\u5efa IServiceScope<\/p>\n<p>\u2778 Resolves the scoped service from the scoped container<br \/>\n\u4ece\u8303\u56f4\u5bb9\u5668\u89e3\u6790\u8303\u56f4\u670d\u52a1<\/p>\n<p>\u2779 When the IServiceScope is disposed, all resolved services are also disposed.<br \/>\n\u91ca\u653e IServiceScope \u65f6\uff0c\u4e5f\u4f1a\u91ca\u653e\u6240\u6709\u5df2\u89e3\u6790\u7684\u670d\u52a1\u3002<\/p>\n<p>This example uses the async form CreateAsyncScope() instead of CreateScope(), which you generally should favor whenever possible. CreateAsyncScope was introduced in .NET 6 to fix an edge case related to IAsyncDisposable (introduced in .NET Core 3.0). You can read more about this scenario on my blog at <a href=\"http:\/\/mng.bz\/zXGB\">http:\/\/mng.bz\/zXGB<\/a>.<\/p>\n<p>\u6b64\u793a\u4f8b\u4f7f\u7528\u5f02\u6b65\u5f62\u5f0f CreateAsyncScope\uff08\uff09 \u800c\u4e0d\u662f CreateScope\uff08\uff09\uff0c\u60a8\u901a\u5e38\u5e94\u5c3d\u53ef\u80fd\u4f7f\u7528\u540e\u8005\u3002CreateAsyncScope \u662f\u5728 .NET 6 \u4e2d\u5f15\u5165\u7684\uff0c\u7528\u4e8e\u4fee\u590d\u4e0e IAsyncDisposable \u76f8\u5173\u7684\u8fb9\u7f18\u60c5\u51b5\uff08\u5728 .NET Core 3.0 \u4e2d\u5f15\u5165\uff09\u3002\u60a8\u53ef\u4ee5\u5728\u6211\u7684\u535a\u5ba2 <a href=\"http:\/\/mng.bz\/zXGB\">http:\/\/mng.bz\/zXGB<\/a> \u4e0a\u9605\u8bfb\u6709\u5173\u6b64\u65b9\u6848\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n<p>With that, you\u2019ve reached the end of this introduction to DI in ASP.NET Core. Now you know how to register your own services with the DI container, and ideally, you have a good understanding of the three service lifetimes used in .NET. DI appears everywhere in .NET, so it\u2019s important to try to get your head around it.<\/p>\n<p>\u81f3\u6b64\uff0c\u60a8\u5df2\u5b8c\u6210 ASP.NET Core \u4e2d\u7684 DI \u7b80\u4ecb\u7684\u7ed3\u5c3e\u3002\u73b0\u5728\u60a8\u77e5\u9053\u5982\u4f55\u6ce8\u518c\u81ea\u5df1\u7684\u670d\u52a1\uff0c\u7406\u60f3\u60c5\u51b5\u4e0b\uff0c\u60a8\u5bf9 .NET \u4e2d\u4f7f\u7528\u7684\u4e09\u4e2a\u670d\u52a1\u751f\u5b58\u671f\u6709\u5f88\u597d\u7684\u4e86\u89e3\u3002DI \u5728 .NET \u4e2d\u65e0\u5904\u4e0d\u5728\uff0c\u56e0\u6b64\u8bf7\u52a1\u5fc5\u5c1d\u8bd5\u4e86\u89e3\u5b83\u3002<\/p>\n<p>In chapter 10 we\u2019ll look at the ASP.NET Core configuration model. You\u2019ll see how to load settings from a file at runtime, store sensitive settings safely, and make your application behave differently depending on which machine it\u2019s running on. We\u2019ll even use a bit of DI; it gets everywhere in ASP.NET Core!<\/p>\n<p>\u5728\u7b2c 10 \u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd ASP.NET Core \u914d\u7f6e\u6a21\u578b\u3002\u60a8\u5c06\u4e86\u89e3\u5982\u4f55\u5728\u8fd0\u884c\u65f6\u4ece\u6587\u4ef6\u52a0\u8f7d\u8bbe\u7f6e\uff0c\u5b89\u5168\u5730\u5b58\u50a8\u654f\u611f\u8bbe\u7f6e\uff0c\u4ee5\u53ca\u4f7f\u60a8\u7684\u5e94\u7528\u7a0b\u5e8f\u6839\u636e\u8fd0\u884c\u5b83\u7684\u673a\u5668\u8fd0\u884c\u4e0d\u540c\u7684\u884c\u4e3a\u3002\u6211\u4eec\u751a\u81f3\u4f1a\u4f7f\u7528\u4e00\u70b9 DI;\u5b83\u5728 ASP.NET Core \u4e2d\u65e0\u5904\u4e0d\u5728\uff01<\/p>\n<h2>9.6 Summary<\/h2>\n<h2>9.6 \u603b\u7ed3<\/h2>\n<p>When registering your services, you describe three things: the service type, the implementation type, and the lifetime. The service type defines which class or interface will be requested as a dependency. The implementation type is the class the container should create to fulfill the dependency. The lifetime is how long an instance of the service should be used for.<br \/>\n\u5728\u6ce8\u518c\u670d\u52a1\u65f6\uff0c\u60a8\u9700\u8981\u63cf\u8ff0\u4e09\u9879\u5185\u5bb9\uff1a\u670d\u52a1\u7c7b\u578b\u3001\u5b9e\u73b0\u7c7b\u578b\u548c\u751f\u547d\u5468\u671f\u3002\u670d\u52a1\u7c7b\u578b\u5b9a\u4e49\u5c06\u8bf7\u6c42\u54ea\u4e2a\u7c7b\u6216\u63a5\u53e3\u4f5c\u4e3a\u4f9d\u8d56\u9879\u3002implementation type \u662f\u5bb9\u5668\u4e3a\u5b9e\u73b0\u4f9d\u8d56\u9879\u800c\u5e94\u521b\u5efa\u7684\u7c7b\u3002\u751f\u5b58\u671f\u662f\u670d\u52a1\u5b9e\u4f8b\u7684\u4f7f\u7528\u65f6\u95f4\u3002<\/p>\n<p>You can register a service by using generic methods if the class is concrete and all its constructor arguments are registered with the container or have default values.<br \/>\n\u5982\u679c\u7c7b\u662f\u5177\u4f53\u7684\uff0c\u5e76\u4e14\u5176\u6240\u6709\u6784\u9020\u51fd\u6570\u53c2\u6570\u90fd\u5df2\u6ce8\u518c\u5230\u5bb9\u5668\u6216\u5177\u6709\u9ed8\u8ba4\u503c\uff0c\u5219\u53ef\u4ee5\u4f7f\u7528\u6cdb\u578b\u65b9\u6cd5\u6ce8\u518c\u670d\u52a1\u3002<\/p>\n<p>You can provide an instance of a service during registration, which will register that instance as a singleton. This approach can be useful when you already have an instance of the service available.<br \/>\n\u60a8\u53ef\u4ee5\u5728\u6ce8\u518c\u671f\u95f4\u63d0\u4f9b\u670d\u52a1\u7684\u5b9e\u4f8b\uff0c\u8be5\u5b9e\u4f8b\u4f1a\u5c06\u8be5\u5b9e\u4f8b\u6ce8\u518c\u4e3a\u5355\u4e00\u5b9e\u4f8b\u3002\u5f53\u60a8\u5df2\u6709\u53ef\u7528\u7684\u670d\u52a1\u5b9e\u4f8b\u65f6\uff0c\u6b64\u65b9\u6cd5\u53ef\u80fd\u5f88\u6709\u7528\u3002<\/p>\n<p>You can provide a lambda factory function that describes how to create an instance of a service with any lifetime you choose. You can take this approach when your services depend on other services that are accessible only when your application is running.<br \/>\n\u60a8\u53ef\u4ee5\u63d0\u4f9b\u4e00\u4e2a lambda \u5de5\u5382\u51fd\u6570\uff0c\u7528\u4e8e\u63cf\u8ff0\u5982\u4f55\u521b\u5efa\u5177\u6709\u60a8\u9009\u62e9\u7684\u4efb\u4f55\u751f\u547d\u5468\u671f\u7684\u670d\u52a1\u5b9e\u4f8b\u3002\u5f53\u60a8\u7684\u670d\u52a1\u4f9d\u8d56\u4e8e\u4ec5\u5728\u5e94\u7528\u7a0b\u5e8f\u8fd0\u884c\u65f6\u624d\u80fd\u8bbf\u95ee\u7684\u5176\u4ed6\u670d\u52a1\u65f6\uff0c\u60a8\u53ef\u4ee5\u91c7\u7528\u6b64\u65b9\u6cd5\u3002<\/p>\n<p>Avoid calling GetService() or GetRequiredService() in your factory functions if possible. Instead, favor constructor injection; it\u2019s more performant and simpler to reason about.<br \/>\n\u5982\u679c\u53ef\u80fd\uff0c\u8bf7\u907f\u514d\u5728\u5de5\u5382\u51fd\u6570\u4e2d\u8c03\u7528 GetService\uff08\uff09 \u6216 GetRequiredService\uff08\uff09\u3002 \u76f8\u53cd\uff0c\u652f\u6301\u6784\u9020\u51fd\u6570\u6ce8\u5165;\u5b83\u7684\u6027\u80fd\u66f4\u9ad8\uff0c\u63a8\u7406\u4e5f\u66f4\u7b80\u5355\u3002<\/p>\n<p>You can register multiple implementations for a service. Then you can inject <code>IEnumerable&lt;T&gt;<\/code> to get access to all the implementations at runtime.<br \/>\n\u60a8\u53ef\u4ee5\u4e3a\u4e00\u4e2a\u670d\u52a1\u6ce8\u518c\u591a\u4e2a\u5b9e\u73b0\u3002\u7136\u540e\uff0c\u60a8\u53ef\u4ee5\u6ce8\u5165 IEnumerable<T> \u4ee5\u5728\u8fd0\u884c\u65f6\u8bbf\u95ee\u6240\u6709\u5b9e\u73b0\u3002<\/p>\n<p>If you inject a single instance of a multiple-registered service, the container injects the last implementation registered.<br \/>\n\u5982\u679c\u60a8\u6ce8\u5165\u591a\u4e2a\u6ce8\u518c\u670d\u52a1\u7684\u5355\u4e2a\u5b9e\u4f8b\uff0c\u5219\u5bb9\u5668\u5c06\u6ce8\u5165\u6700\u540e\u4e00\u4e2a\u6ce8\u518c\u7684\u5b9e\u73b0\u3002<\/p>\n<p>You can use the TryAddextension methods to ensure that an implementation is registered only if no other implementation of the service has been registered. This approach can be useful for library authors to add default services while still allowing consumers to override the registered services.<br \/>\n\u60a8\u53ef\u4ee5\u4f7f\u7528 TryAdd\u6269\u5c55\u65b9\u6cd5\u786e\u4fdd\u4ec5\u5728\u672a\u6ce8\u518c\u670d\u52a1\u7684\u5176\u4ed6\u5b9e\u65bd\u65f6\u6ce8\u518c\u5b9e\u65bd\u3002\u8fd9\u79cd\u65b9\u6cd5\u5bf9\u4e8e\u5e93\u4f5c\u8005\u6765\u8bf4\u975e\u5e38\u6709\u7528\uff0c\u53ef\u4ee5\u6dfb\u52a0\u9ed8\u8ba4\u670d\u52a1\uff0c\u540c\u65f6\u4ecd\u5141\u8bb8\u4f7f\u7528\u8005\u8986\u76d6\u5df2\u6ce8\u518c\u7684\u670d\u52a1\u3002<\/p>\n<p>You define the lifetime of a service during DI service registration to dictate when a DI container will reuse an existing instance of the service to fulfill service dependencies and when it will create a new one.<br \/>\n\u5728 DI \u670d\u52a1\u6ce8\u518c\u671f\u95f4\u5b9a\u4e49\u670d\u52a1\u7684\u751f\u5b58\u671f\uff0c\u4ee5\u6307\u793a DI \u5bb9\u5668\u4f55\u65f6\u91cd\u7528\u670d\u52a1\u7684\u73b0\u6709\u5b9e\u4f8b\u6765\u6ee1\u8db3\u670d\u52a1\u4f9d\u8d56\u9879\uff0c\u4ee5\u53ca\u4f55\u65f6\u521b\u5efa\u65b0\u5b9e\u4f8b\u3002<\/p>\n<p>A transient lifetime means that every time a service is requested, a new instance is created.<br \/>\n\u77ac\u6001\u751f\u5b58\u671f\u610f\u5473\u7740\u6bcf\u6b21\u8bf7\u6c42\u670d\u52a1\u65f6\uff0c\u90fd\u4f1a\u521b\u5efa\u4e00\u4e2a\u65b0\u5b9e\u4f8b\u3002<\/p>\n<p>A scoped lifetime means that within a scope, all requests for a service will give you the same object. For different scopes, you\u2019ll get different objects. In ASP.NET Core, each web request gets its own scope.<br \/>\n\u4f5c\u7528\u57df\u751f\u547d\u5468\u671f\u610f\u5473\u7740\u5728\u4e00\u4e2a\u8303\u56f4\u5185\uff0c\u5bf9\u670d\u52a1\u7684\u6240\u6709\u8bf7\u6c42\u90fd\u5c06\u4e3a\u4f60\u63d0\u4f9b\u76f8\u540c\u7684\u5bf9\u8c61\u3002\u5bf9\u4e8e\u4e0d\u540c\u7684\u8303\u56f4\uff0c\u4f60\u5c06\u83b7\u5f97\u4e0d\u540c\u7684\u5bf9\u8c61\u3002\u5728 ASP.NET Core \u4e2d\uff0c\u6bcf\u4e2a Web \u8bf7\u6c42\u90fd\u6709\u81ea\u5df1\u7684\u8303\u56f4\u3002<\/p>\n<p>You\u2019ll always get the same instance of a singleton service, regardless of scope.<br \/>\n\u65e0\u8bba\u8303\u56f4\u5982\u4f55\uff0c\u60a8\u90fd\u5c06\u59cb\u7ec8\u83b7\u5f97\u5355\u4e00\u5b9e\u4f8b\u670d\u52a1\u7684\u76f8\u540c\u5b9e\u4f8b\u3002<\/p>\n<p>A service should use only dependencies with a lifetime longer than or equal to the lifetime of the service. By default, ASP.NET Core performs scope validation to check for errors like this one and throws an exception when it finds them, but this feature is enabled only in development environments, as it has a performance cost.<br \/>\n\u670d\u52a1\u5e94\u4ec5\u4f7f\u7528\u751f\u547d\u5468\u671f\u957f\u4e8e\u6216\u7b49\u4e8e\u670d\u52a1\u751f\u547d\u5468\u671f\u7684\u4f9d\u8d56\u9879\u3002\u9ed8\u8ba4\u60c5\u51b5\u4e0b\uff0cASP.NET Core \u6267\u884c\u8303\u56f4\u9a8c\u8bc1\u4ee5\u68c0\u67e5\u6b64\u7c7b\u9519\u8bef\uff0c\u5e76\u5728\u627e\u5230\u9519\u8bef\u65f6\u5f15\u53d1\u5f02\u5e38\uff0c\u4f46\u6b64\u529f\u80fd\u4ec5\u5728\u5f00\u53d1\u73af\u5883\u4e2d\u542f\u7528\uff0c\u56e0\u4e3a\u5b83\u4f1a\u964d\u4f4e\u6027\u80fd\u3002<\/p>\n<p>To access scoped services in Program.cs, you must first create an IServiceScope object by calling CreateScope() or CreateAsyncScope() on WebApplication.Services. You can resolve services from the IServiceScope.ServiceProvider property. When you dispose IServiceScope, any scoped or transient services resolved from the scope are also disposed.<br \/>\n\u82e5\u8981\u8bbf\u95ee Program.cs \u4e2d\u7684\u5206\u533a\u670d\u52a1\uff0c\u5fc5\u987b\u9996\u5148\u901a\u8fc7\u5728 WebApplication \u4e0a\u8c03\u7528 CreateScope\uff08\uff09 \u6216 CreateAsyncScope\uff08\uff09 \u6765\u521b\u5efa IServiceScope \u5bf9\u8c61\u3002\u670d\u52a1\u3002\u53ef\u4ee5\u4ece IServiceScope.ServiceProvider \u5c5e\u6027\u89e3\u6790\u670d\u52a1\u3002\u91ca\u653e IServiceScope \u65f6\uff0c\u8fd8\u4f1a\u91ca\u653e\u4ece\u8be5\u8303\u56f4\u89e3\u6790\u7684\u4efb\u4f55\u4f5c\u7528\u57df\u6216\u6682\u65f6\u6027\u670d\u52a1\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>9 Registering services with dependency injection 9 \u4f7f\u7528\u4f9d\u8d56\u6ce8\u5165\u6ce8\u518c\u670d\u52a1 This chapter covers \u672c\u7ae0\u6db5\u76d6 Configuring your services to work with dependency injection \u914d\u7f6e\u670d\u52a1\u4ee5\u4f7f\u7528\u4f9d\u8d56\u5173\u7cfb\u6ce8\u5165 Choosing the correct lifetime for your services \u4e3a\u60a8\u7684\u670d\u52a1\u9009\u62e9\u6b63\u786e\u7684\u751f\u547d\u5468\u671f In chapter 8 you learned about dependency injection (DI) in general, why it\u2019s useful as a pattern for developing loosely coupled code, and its central place [&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-589","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\/589","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=589"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/589\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=589"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=589"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=589"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}