{"id":1126,"date":"2025-05-27T14:46:55","date_gmt":"2025-05-27T06:46:55","guid":{"rendered":"https:\/\/www.hyy.net\/?p=1126"},"modified":"2025-05-27T14:46:55","modified_gmt":"2025-05-27T06:46:55","slug":"ultimate-asp-net-core-web-api-14-asynchronous-code","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=1126","title":{"rendered":"Ultimate ASP.NET Core Web API 14 ASYNCHRONOUS CODE"},"content":{"rendered":"<p>14 ASYNCHRONOUS CODE<br \/>\n14 \u5f02\u6b65\u4ee3\u7801<\/p>\n<p>In this chapter, we are going to convert synchronous code to asynchronous inside ASP.NET Core. First, we are going to learn a bit about asynchronous programming and why should we write async code. Then we are going to use our code from the previous chapters and rewrite it in an async manner.\u200c<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u5728 ASP.NET Core \u4e2d\u5c06\u540c\u6b65\u4ee3\u7801\u8f6c\u6362\u4e3a\u5f02\u6b65\u4ee3\u7801\u3002\u9996\u5148\uff0c\u6211\u4eec\u5c06\u5b66\u4e60\u4e00\u4e9b\u5173\u4e8e\u5f02\u6b65\u7f16\u7a0b\u7684\u77e5\u8bc6\uff0c\u4ee5\u53ca\u4e3a\u4ec0\u4e48\u6211\u4eec\u5e94\u8be5\u7f16\u5199\u5f02\u6b65\u4ee3\u7801\u3002\u7136\u540e\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u524d\u51e0\u7ae0\u4e2d\u7684\u4ee3\u7801\uff0c\u5e76\u4ee5\u5f02\u6b65\u65b9\u5f0f\u91cd\u5199\u5b83\u3002<\/p>\n<p>We are going to modify the code, step by step, to show you how easy is to convert synchronous code to asynchronous code. Hopefully, this will help you understand how asynchronous code works and how to write it from scratch in your applications.<br \/>\n\u6211\u4eec\u5c06\u9010\u6b65\u4fee\u6539\u4ee3\u7801\uff0c\u5411\u60a8\u5c55\u793a\u5c06\u540c\u6b65\u4ee3\u7801\u8f6c\u6362\u4e3a\u5f02\u6b65\u4ee3\u7801\u662f\u591a\u4e48\u5bb9\u6613\u3002\u5e0c\u671b\u8fd9\u5c06\u5e2e\u52a9\u60a8\u4e86\u89e3\u5f02\u6b65\u4ee3\u7801\u7684\u5de5\u4f5c\u539f\u7406\uff0c\u4ee5\u53ca\u5982\u4f55\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4ece\u5934\u5f00\u59cb\u7f16\u5199\u5f02\u6b65\u4ee3\u7801\u3002<\/p>\n<h2>14.1 What is Asynchronous Programming?<\/h2>\n<p>14.1 \u4ec0\u4e48\u662f\u5f02\u6b65\u7f16\u7a0b\uff1f<\/p>\n<p>Async programming is a parallel programming technique that allows the working process to run separately from the main application thread.\u200c<br \/>\n\u5f02\u6b65\u7f16\u7a0b\u662f\u4e00\u79cd\u5e76\u884c\u7f16\u7a0b\u6280\u672f\uff0c\u5b83\u5141\u8bb8\u5de5\u4f5c\u8fdb\u7a0b\u72ec\u7acb\u4e8e\u4e3b\u5e94\u7528\u7a0b\u5e8f\u7ebf\u7a0b\u8fd0\u884c\u3002<\/p>\n<p>By using async programming, we can avoid performance bottlenecks and enhance the responsiveness of our application.<br \/>\n\u901a\u8fc7\u4f7f\u7528\u5f02\u6b65\u7f16\u7a0b\uff0c\u6211\u4eec\u53ef\u4ee5\u907f\u514d\u6027\u80fd\u74f6\u9888\u5e76\u63d0\u9ad8\u5e94\u7528\u7a0b\u5e8f\u7684\u54cd\u5e94\u80fd\u529b\u3002<\/p>\n<p>How so?<br \/>\n\u600e\u4e48\u4f1a\u8fd9\u6837\u5462\uff1f<\/p>\n<p>Because we are not sending requests to the server and blocking it while waiting for the responses anymore (as long as it takes). Now, when we send a request to the server, the thread pool delegates a thread to that request. Eventually, that thread finishes its job and returns to the thread pool freeing itself for the next request. At some point, the data will be fetched from the database and the result needs to be sent to the requester. At that time, the thread pool provides another thread to handle that work. Once the work is done, a thread is going back to the thread pool.<br \/>\n\u56e0\u4e3a\u6211\u4eec\u4e0d\u518d\u5411\u670d\u52a1\u5668\u53d1\u9001\u8bf7\u6c42\u5e76\u5728\u7b49\u5f85\u54cd\u5e94\u65f6\u963b\u6b62\u5b83\uff08\u53ea\u8981\u9700\u8981\uff09\u3002\u73b0\u5728\uff0c\u5f53\u6211\u4eec\u5411\u670d\u52a1\u5668\u53d1\u9001\u8bf7\u6c42\u65f6\uff0c\u7ebf\u7a0b\u6c60\u4f1a\u5c06\u7ebf\u7a0b\u59d4\u6258\u7ed9\u8be5\u8bf7\u6c42\u3002\u6700\u7ec8\uff0c\u8be5\u7ebf\u7a0b\u5b8c\u6210\u5176\u4f5c\u4e1a\u5e76\u8fd4\u56de\u5230\u7ebf\u7a0b\u6c60\uff0c\u4ece\u800c\u4e3a\u4e0b\u4e00\u4e2a\u8bf7\u6c42\u91ca\u653e\u81ea\u8eab\u3002\u5728\u67d0\u4e9b\u65f6\u5019\uff0c\u5c06\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6\u6570\u636e\uff0c\u5e76\u4e14\u9700\u8981\u5c06\u7ed3\u679c\u53d1\u9001\u7ed9\u8bf7\u6c42\u8005\u3002\u6b64\u65f6\uff0c\u7ebf\u7a0b\u6c60\u4f1a\u63d0\u4f9b\u53e6\u4e00\u4e2a\u7ebf\u7a0b\u6765\u5904\u7406\u8be5\u5de5\u4f5c\u3002\u5de5\u4f5c\u5b8c\u6210\u540e\uff0c\u7ebf\u7a0b\u5c06\u8fd4\u56de\u5230\u7ebf\u7a0b\u6c60\u3002<\/p>\n<p>It is very important to understand that if we send a request to an endpoint and it takes the application three or more seconds to process that request, we probably won\u2019t be able to execute this request any faster in async mode. It is going to take the same amount of time as the sync request.<br \/>\n\u8bf7\u52a1\u5fc5\u4e86\u89e3\uff0c\u5982\u679c\u6211\u4eec\u5411\u7ec8\u7aef\u8282\u70b9\u53d1\u9001\u8bf7\u6c42\uff0c\u5e76\u4e14\u5e94\u7528\u7a0b\u5e8f\u9700\u8981 3 \u79d2\u6216\u66f4\u957f\u65f6\u95f4\u6765\u5904\u7406\u8be5\u8bf7\u6c42\uff0c\u5219\u6211\u4eec\u53ef\u80fd\u65e0\u6cd5\u5728\u5f02\u6b65\u6a21\u5f0f\u4e0b\u66f4\u5feb\u5730\u6267\u884c\u6b64\u8bf7\u6c42\u3002\u5b83\u5c06\u82b1\u8d39\u4e0e\u540c\u6b65\u8bf7\u6c42\u76f8\u540c\u7684\u65f6\u95f4\u3002<\/p>\n<p>Let\u2019s imagine that our thread pool has two threads and we have used one thread with a first request. Now, the second request arrives and we have to use the second thread from a thread pool. At this point, our thread pool is out of threads. If a third request arrives now it has to wait for any of the first two requests to complete and return assigned threads to a thread pool. Only then the thread pool can assign that returned thread to a new request:<br \/>\n\u5047\u8bbe\u6211\u4eec\u7684\u7ebf\u7a0b\u6c60\u6709\u4e24\u4e2a\u7ebf\u7a0b\uff0c\u5e76\u4e14\u6211\u4eec\u5728\u7b2c\u4e00\u4e2a\u8bf7\u6c42\u4e2d\u4f7f\u7528\u4e86\u4e00\u4e2a\u7ebf\u7a0b\u3002\u73b0\u5728\uff0c\u7b2c\u4e8c\u4e2a\u8bf7\u6c42\u5230\u8fbe\uff0c\u6211\u4eec\u5fc5\u987b\u4f7f\u7528\u7ebf\u7a0b\u6c60\u4e2d\u7684\u7b2c\u4e8c\u4e2a\u7ebf\u7a0b\u3002\u6b64\u65f6\uff0c\u6211\u4eec\u7684\u7ebf\u7a0b\u6c60\u5df2\u7528\u5b8c\u7ebf\u7a0b\u3002\u5982\u679c\u73b0\u5728\u6536\u5230\u7b2c\u4e09\u4e2a\u8bf7\u6c42\uff0c\u5219\u5fc5\u987b\u7b49\u5f85\u524d\u4e24\u4e2a\u8bf7\u6c42\u4e2d\u7684\u4efb\u4f55\u4e00\u4e2a\u5b8c\u6210\u5e76\u5c06\u5206\u914d\u7684\u7ebf\u7a0b\u8fd4\u56de\u5230\u7ebf\u7a0b\u6c60\u3002\u53ea\u6709\u8fd9\u6837\uff0c\u7ebf\u7a0b\u6c60\u624d\u80fd\u5c06\u8fd4\u56de\u7684\u7ebf\u7a0b\u5206\u914d\u7ed9\u65b0\u8bf7\u6c42\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1401.jpg\" alt=\"alt text\" \/><\/p>\n<p>As a result of a request waiting for an available thread, our client experiences a slow down for sure. Additionally, if the client has to wait too long, they will receive an error response usually the service is unavailable (503). But this is not the only problem. Since the client expects the list of entities from the database, we know that it is an I\/O operation. So, if we have a lot of records in the database and it takes three seconds for the database to return a result to the API, our thread is doing nothing except waiting for the task to complete. So basically, we are blocking that thread and making it three seconds unavailable for any additional requests that arrive at our API.<br \/>\n\u7531\u4e8e\u8bf7\u6c42\u7b49\u5f85\u53ef\u7528\u7ebf\u7a0b\uff0c\u6211\u4eec\u7684\u5ba2\u6237\u80af\u5b9a\u4f1a\u9047\u5230\u901f\u5ea6\u53d8\u6162\u7684\u60c5\u51b5\u3002\u6b64\u5916\uff0c\u5982\u679c\u5ba2\u6237\u7aef\u5fc5\u987b\u7b49\u5f85\u592a\u957f\u65f6\u95f4\uff0c\u4ed6\u4eec\u5c06\u6536\u5230\u9519\u8bef\u54cd\u5e94\uff0c\u901a\u5e38\u662f\u670d\u52a1\u4e0d\u53ef\u7528 \uff08503\uff09\u3002\u4f46\u8fd9\u5e76\u4e0d\u662f\u552f\u4e00\u7684\u95ee\u9898\u3002\u7531\u4e8e\u5ba2\u6237\u7aef\u9700\u8981\u6570\u636e\u5e93\u4e2d\u7684\u5b9e\u4f53\u5217\u8868\uff0c\u56e0\u6b64\u6211\u4eec\u77e5\u9053\u8fd9\u662f\u4e00\u4e2a I\/O\u4f5c\u3002\u56e0\u6b64\uff0c\u5982\u679c\u6211\u4eec\u5728\u6570\u636e\u5e93\u4e2d\u6709\u5f88\u591a\u8bb0\u5f55\uff0c\u5e76\u4e14\u6570\u636e\u5e93\u9700\u8981 3 \u79d2\u949f\u624d\u80fd\u5c06\u7ed3\u679c\u8fd4\u56de\u7ed9 API\uff0c\u90a3\u4e48\u6211\u4eec\u7684\u7ebf\u7a0b\u9664\u4e86\u7b49\u5f85\u4efb\u52a1\u5b8c\u6210\u5916\uff0c\u4ec0\u4e48\u90fd\u4e0d\u505a\u3002\u6240\u4ee5\u57fa\u672c\u4e0a\uff0c\u6211\u4eec\u963b\u6b62\u4e86\u8be5\u7ebf\u7a0b\uff0c\u5e76\u4f7f\u5176\u5728 3 \u79d2\u5185\u65e0\u6cd5\u7528\u4e8e\u5230\u8fbe\u6211\u4eec API \u7684\u4efb\u4f55\u5176\u4ed6\u8bf7\u6c42\u3002<\/p>\n<p>With asynchronous requests, the situation is completely different.<br \/>\n\u5bf9\u4e8e\u5f02\u6b65\u8bf7\u6c42\uff0c\u60c5\u51b5\u5b8c\u5168\u4e0d\u540c\u3002<\/p>\n<p>When a request arrives at our API, we still need a thread from a thread pool. So, that leaves us with only one thread left. But because this action is now asynchronous, as soon as our request reaches the I\/O point where the database has to process the result for three seconds, the thread is returned to a thread pool. Now we again have two available threads and we can use them for any additional request. After the three seconds when the database returns the result to the API, the thread pool assigns the thread again to handle that response:<br \/>\n\u5f53\u8bf7\u6c42\u5230\u8fbe\u6211\u4eec\u7684 API \u65f6\uff0c\u6211\u4eec\u4ecd\u7136\u9700\u8981\u6765\u81ea\u7ebf\u7a0b\u6c60\u7684\u7ebf\u7a0b\u3002\u6240\u4ee5\uff0c\u6211\u4eec\u53ea\u5269\u4e0b\u4e00\u6761\u7ebf\u7d22\u4e86\u3002\u4f46\u662f\uff0c\u7531\u4e8e\u6b64\u4f5c\u73b0\u5728\u662f\u5f02\u6b65\u7684\uff0c\u56e0\u6b64\u4e00\u65e6\u6211\u4eec\u7684\u8bf7\u6c42\u5230\u8fbe\u6570\u636e\u5e93\u5fc5\u987b\u5904\u7406\u7ed3\u679c\u4e09\u79d2\u949f\u7684 I\/O \u70b9\uff0c\u7ebf\u7a0b\u5c31\u4f1a\u8fd4\u56de\u5230\u7ebf\u7a0b\u6c60\u3002\u73b0\u5728\u6211\u4eec\u53c8\u6709\u4e24\u4e2a\u53ef\u7528\u7684\u7ebf\u7a0b\uff0c\u6211\u4eec\u53ef\u4ee5\u5c06\u5b83\u4eec\u7528\u4e8e\u4efb\u4f55\u5176\u4ed6\u8bf7\u6c42\u3002\u5728\u6570\u636e\u5e93\u5c06\u7ed3\u679c\u8fd4\u56de\u7ed9 API \u7684\u4e09\u79d2\u540e\uff0c\u7ebf\u7a0b\u6c60\u4f1a\u518d\u6b21\u5206\u914d\u7ebf\u7a0b\u6765\u5904\u7406\u8be5\u54cd\u5e94\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1402.jpg\" alt=\"alt text\" \/><\/p>\n<p>Now that we've cleared that out, we can learn how to implement asynchronous code in .NET Core and .NET 5+.<br \/>\n\u73b0\u5728\u6211\u4eec\u5df2\u7ecf\u89e3\u51b3\u4e86\u8fd9\u4e2a\u95ee\u9898\uff0c\u6211\u4eec\u53ef\u4ee5\u5b66\u4e60\u5982\u4f55\u5728 .NET Core \u548c .NET 5+ \u4e2d\u5b9e\u73b0\u5f02\u6b65\u4ee3\u7801\u3002<\/p>\n<h2>14.2 Async, Await Keywords and Return Types<\/h2>\n<p>14.2 async\u3001await \u5173\u952e\u5b57\u548c\u8fd4\u56de\u7c7b\u578b<\/p>\n<p>The async and await keywords play a crucial part in asynchronous programming. We use the async keyword in the method declaration and its purpose is to enable the await keyword within that method. So yes,\u200cwe can\u2019t use the await keyword without previously adding the async keyword in the method declaration. Also, using only the async keyword doesn\u2019t make your method asynchronous, just the opposite, that method is still synchronous.<br \/>\nasync \u548c await \u5173\u952e\u5b57\u5728\u5f02\u6b65\u7f16\u7a0b\u4e2d\u8d77\u7740\u81f3\u5173\u91cd\u8981\u7684\u4f5c\u7528\u3002\u6211\u4eec\u5728\u65b9\u6cd5\u58f0\u660e\u4e2d\u4f7f\u7528 async \u5173\u952e\u5b57\uff0c\u5176\u76ee\u7684\u662f\u5728\u8be5\u65b9\u6cd5\u4e2d\u542f\u7528 await \u5173\u952e\u5b57\u3002\u6240\u4ee5\uff0c\u662f\u7684\uff0c\u5982\u679c\u4e0d\u4e8b\u5148\u5728\u65b9\u6cd5\u58f0\u660e\u4e2d\u6dfb\u52a0 async \u5173\u952e\u5b57\uff0c\u6211\u4eec\u5c31\u4e0d\u80fd\u4f7f\u7528 await \u5173\u952e\u5b57\u3002\u6b64\u5916\uff0c\u4ec5\u4f7f\u7528 async \u5173\u952e\u5b57\u4e0d\u4f1a\u4f7f\u60a8\u7684\u65b9\u6cd5\u5f02\u6b65\uff0c\u6070\u6070\u76f8\u53cd\uff0c\u8be5\u65b9\u6cd5\u4ecd\u7136\u662f\u540c\u6b65\u7684\u3002<\/p>\n<p>The await keyword performs an asynchronous wait on its argument. It does that in several steps. The first thing it does is to check whether the operation is already complete. If it is, it will continue the method execution synchronously. Otherwise, the await keyword is going to pause the async method execution and return an incomplete task. Once the operation completes, a few seconds later, the async method can continue with the execution.<br \/>\nawait \u5173\u952e\u5b57\u5bf9\u5176\u53c2\u6570\u6267\u884c\u5f02\u6b65\u7b49\u5f85\u3002\u5b83\u5206\u51e0\u4e2a\u6b65\u9aa4\u6765\u5b9e\u73b0\u3002\u5b83\u505a\u7684\u7b2c\u4e00\u4ef6\u4e8b\u662f\u68c0\u67e5\u4f5c\u662f\u5426\u5df2\u7ecf\u5b8c\u6210\u3002\u5982\u679c\u662f\uff0c\u5b83\u5c06\u540c\u6b65\u7ee7\u7eed\u65b9\u6cd5\u6267\u884c\u3002\u5426\u5219\uff0cawait \u5173\u952e\u5b57\u5c06\u6682\u505c\u5f02\u6b65\u65b9\u6cd5\u7684\u6267\u884c\u5e76\u8fd4\u56de\u672a\u5b8c\u6210\u7684\u4efb\u52a1\u3002\u4f5c\u5b8c\u6210\u540e\uff0c\u51e0\u79d2\u949f\u540e\uff0casync \u65b9\u6cd5\u53ef\u4ee5\u7ee7\u7eed\u6267\u884c\u3002<\/p>\n<p>Let\u2019s see this with a simple example:<br \/>\n\u8ba9\u6211\u4eec\u901a\u8fc7\u4e00\u4e2a\u7b80\u5355\u7684\u4f8b\u5b50\u6765\u4e86\u89e3\u8fd9\u4e00\u70b9\uff1a<\/p>\n<pre><code>public async Task&lt;IEnumerable&lt;Company&gt;&gt; GetCompanies()\n{\n    _logger.LogInfo(&quot;Inside the GetCompanies method.&quot;);\n    var companies = await _repoContext.Companies.ToListAsync();\n    return companies;\n}<\/code><\/pre>\n<p>So, even though our method is marked with the async keyword, it will start its execution synchronously. Once we log the required information synchronously, we continue to the next code line. We extract all the companies from the database and to do that, we use the await keyword. If our database requires some time to process the result and return it, the await keyword is going to pause the GetCompanies method execution and return an incomplete task. During that time the tread will be returned to a thread pool making itself available for another request. After the database operation completes the async method will resume executing and will return the list of companies.<br \/>\n\u56e0\u6b64\uff0c\u5373\u4f7f\u6211\u4eec\u7684\u65b9\u6cd5\u6807\u6709 async \u5173\u952e\u5b57\uff0c\u5b83\u4e5f\u4f1a\u540c\u6b65\u5f00\u59cb\u6267\u884c\u3002\u540c\u6b65\u8bb0\u5f55\u6240\u9700\u4fe1\u606f\u540e\uff0c\u6211\u4eec\u5c06\u7ee7\u7eed\u4e0b\u4e00\u4e2a\u4ee3\u7801\u884c\u3002\u6211\u4eec\u4ece\u6570\u636e\u5e93\u4e2d\u63d0\u53d6\u6240\u6709\u516c\u53f8\uff0c\u4e3a\u6b64\uff0c\u6211\u4eec\u4f7f\u7528 await \u5173\u952e\u5b57\u3002\u5982\u679c\u6211\u4eec\u7684\u6570\u636e\u5e93\u9700\u8981\u4e00\u4e9b\u65f6\u95f4\u6765\u5904\u7406\u7ed3\u679c\u5e76\u8fd4\u56de\u5b83\uff0c\u5219 await \u5173\u952e\u5b57\u5c06\u6682\u505c GetCompanies \u65b9\u6cd5\u7684\u6267\u884c\u5e76\u8fd4\u56de\u4e00\u4e2a\u672a\u5b8c\u6210\u7684\u4efb\u52a1\u3002\u5728\u6b64\u671f\u95f4\uff0ctread \u5c06\u8fd4\u56de\u5230\u7ebf\u7a0b\u6c60\uff0c\u4f7f\u5176\u53ef\u7528\u4e8e\u53e6\u4e00\u4e2a\u8bf7\u6c42\u3002\u6570\u636e\u5e93\u4f5c\u5b8c\u6210\u540e\uff0c\u5f02\u6b65\u65b9\u6cd5\u5c06\u7ee7\u7eed\u6267\u884c\u5e76\u8fd4\u56de\u516c\u53f8\u5217\u8868\u3002<\/p>\n<p>From this example, we see the async method execution flow. But the question is how the await keyword knows if the operation is completed or not. Well, this is where Task comes into play.<br \/>\n\u5728\u6b64\u793a\u4f8b\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u770b\u5230\u5f02\u6b65\u65b9\u6cd5\u6267\u884c\u6d41\u7a0b\u3002\u4f46\u95ee\u9898\u662f await \u5173\u952e\u5b57\u5982\u4f55\u77e5\u9053\u4f5c\u662f\u5426\u5b8c\u6210\u3002\u55ef\uff0c\u8fd9\u5c31\u662f Task \u53d1\u6325\u4f5c\u7528\u7684\u5730\u65b9\u3002<\/p>\n<h2>14.2.1 Return Types of the Asynchronous Methods\u200c<\/h2>\n<p>14.2.1 \u5f02\u6b65\u65b9\u6cd5\u7684\u8fd4\u56de\u7c7b\u578b<\/p>\n<p>In asynchronous programming, we have three return types:<br \/>\n\u5728\u5f02\u6b65\u7f16\u7a0b\u4e2d\uff0c\u6211\u4eec\u6709\u4e09\u79cd\u8fd4\u56de\u7c7b\u578b\uff1a<\/p>\n<p>\u2022 <code>Task&lt;TResult&gt;<\/code>, for an async method that returns a value.<br \/>\n<code>Task&lt;TResult&gt;<\/code>\uff0c\u6709\u8fd4\u56de\u503c\u7684\u5f02\u6b65\u65b9\u6cd5\u3002<\/p>\n<p>\u2022 Task, for an async method that does not return a value.<br \/>\nTask \u6ca1\u6709\u8fd4\u56de\u503c\u7684\u5f02\u6b65\u65b9\u6cd5\u3002<\/p>\n<p>\u2022 void, which we can use for an event handler.<br \/>\nvoid\uff0c\u6211\u4eec\u53ef\u4ee5\u5c06\u5176\u7528\u4e8e\u4e8b\u4ef6\u5904\u7406\u7a0b\u5e8f\u3002<\/p>\n<p>What does this mean?<br \/>\n\u8fd9\u662f\u4ec0\u4e48\u610f\u601d\uff1f<\/p>\n<p>Well, we can look at this through synchronous programming glasses. If our sync method returns an int, then in the async mode it should return <code>Task&lt;int&gt;<\/code> \u2014 or if the sync method\u3000returns <code>IEnumerable&lt;string&gt;<\/code>, then the async method should return <code>Task&lt;IEnumerable&lt;string&gt;&gt;<\/code>.<br \/>\n\u597d\u5427\uff0c\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u540c\u6b65\u7f16\u7a0b\u773c\u955c\u6765\u770b\u5f85\u8fd9\u4e2a\u95ee\u9898\u3002\u5982\u679c\u6211\u4eec\u7684 sync \u65b9\u6cd5\u8fd4\u56de\u4e00\u4e2a int\uff0c\u90a3\u4e48\u5728\u5f02\u6b65\u6a21\u5f0f\u4e0b\u5b83\u5e94\u8be5\u8fd4\u56de <code>Task&lt;int&gt;<\/code> \u2014 \u6216\u8005\u5982\u679c sync \u65b9\u6cd5\u8fd4\u56de<code>IEnumerable&lt;string&gt;<\/code> \uff0c\u5219\u5f02\u6b65\u65b9\u6cd5\u5e94\u8fd4\u56de<code>Task&lt;IEnumerable&lt;string&gt;&gt;<\/code> \u3002<\/p>\n<p>But if our sync method returns no value (has a void for the return type), then our async method should return Task. This means that we can use the await keyword inside that method, but without the return keyword.<br \/>\n\u4f46\u662f\uff0c\u5982\u679c\u6211\u4eec\u7684 sync \u65b9\u6cd5\u6ca1\u6709\u8fd4\u56de\u4efb\u4f55\u503c\uff08\u8fd4\u56de\u7c7b\u578b\u4e3a void\uff09\uff0c\u5219\u6211\u4eec\u7684 async \u65b9\u6cd5\u5e94\u8fd4\u56de Task\u3002\u8fd9\u610f\u5473\u7740\u6211\u4eec\u53ef\u4ee5\u5728\u8be5\u65b9\u6cd5\u4e2d\u4f7f\u7528 await \u5173\u952e\u5b57\uff0c\u4f46\u4e0d\u9700\u8981 return \u5173\u952e\u5b57\u3002<\/p>\n<p>You may wonder now, why not return Task all the time? Well, we should use void only for the asynchronous event handlers which require a void return type. Other than that, we should always return a Task.<br \/>\n\u60a8\u73b0\u5728\u53ef\u80fd\u60f3\u77e5\u9053\uff0c\u4e3a\u4ec0\u4e48\u4e0d\u4e00\u76f4\u8fd4\u56de Task \u5462\uff1f\u597d\u5427\uff0c\u6211\u4eec\u5e94\u8be5\u53ea\u5bf9\u9700\u8981 void \u8fd4\u56de\u7c7b\u578b\u7684\u5f02\u6b65\u4e8b\u4ef6\u5904\u7406\u7a0b\u5e8f\u4f7f\u7528 void\u3002\u9664\u6b64\u4e4b\u5916\uff0c\u6211\u4eec\u5e94\u8be5\u59cb\u7ec8\u8fd4\u56de\u4e00\u4e2a Task\u3002<\/p>\n<p>From C# 7.0 onward, we can specify any other return type if that type includes a GetAwaiter method.<br \/>\n\u4ece C# 7.0 \u5f00\u59cb\uff0c\u6211\u4eec\u53ef\u4ee5\u6307\u5b9a\u4efb\u4f55\u5176\u4ed6\u8fd4\u56de\u7c7b\u578b\uff08\u5982\u679c\u8be5\u7c7b\u578b\u5305\u542b GetAwaiter \u65b9\u6cd5\uff09\u3002<\/p>\n<p>It is very important to understand that the Task represents an execution of the asynchronous method and not the result. The Task has several properties that indicate whether the operation was completed successfully or not (Status, IsCompleted, IsCanceled, IsFaulted). With these properties, we can track the flow of our async operations. So, this is the answer to our question. With Task, we can track whether the operation is completed or not. This is also called TAP (Task-based Asynchronous Pattern).<br \/>\n\u4e86\u89e3 Task \u8868\u793a\u5f02\u6b65\u65b9\u6cd5\u7684\u6267\u884c\u800c\u4e0d\u662f\u7ed3\u679c\uff0c\u8fd9\u4e00\u70b9\u975e\u5e38\u91cd\u8981\u3002Task \u5177\u6709\u591a\u4e2a\u5c5e\u6027\uff0c\u7528\u4e8e\u6307\u793a\u4f5c\u662f\u5426\u5df2\u6210\u529f\u5b8c\u6210\uff08Status\u3001IsCompleted\u3001IsCanceled\u3001IsFaulted\uff09\u3002\u901a\u8fc7\u8fd9\u4e9b\u5c5e\u6027\uff0c\u6211\u4eec\u53ef\u4ee5\u8ddf\u8e2a\u5f02\u6b65\u4f5c\u7684\u6d41\u7a0b\u3002\u6240\u4ee5\uff0c\u8fd9\u5c31\u662f\u6211\u4eec\u95ee\u9898\u7684\u7b54\u6848\u3002\u4f7f\u7528 Task\uff0c\u6211\u4eec\u53ef\u4ee5\u8ddf\u8e2a\u4f5c\u662f\u5426\u5b8c\u6210\u3002\u8fd9\u4e5f\u79f0\u4e3a TAP \uff08\u57fa\u4e8e\u4efb\u52a1\u7684\u5f02\u6b65\u6a21\u5f0f\uff09\u3002<\/p>\n<p>Now, when we have all the information, let\u2019s do some refactoring in our completely synchronous code.<br \/>\n\u73b0\u5728\uff0c\u5f53\u6211\u4eec\u83b7\u5f97\u6240\u6709\u4fe1\u606f\u65f6\uff0c\u8ba9\u6211\u4eec\u5728\u5b8c\u5168\u540c\u6b65\u7684\u4ee3\u7801\u4e2d\u8fdb\u884c\u4e00\u4e9b\u91cd\u6784\u3002<\/p>\n<h2>14.2.2 The IRepositoryBase Interface and the RepositoryBase Class Explanation\u200c<\/h2>\n<p>14.2.2 IRepositoryBase \u63a5\u53e3\u548c RepositoryBase \u7c7b\u8bf4\u660e<\/p>\n<p>We won\u2019t be changing the mentioned interface and class. That\u2019s because we want to leave a possibility for the repository user classes to have either sync or async method execution. Sometimes, the async code could become slower than the sync one because EF Core\u2019s async commands take slightly longer to execute (due to extra code for handling the threading), so leaving this option is always a good choice.<br \/>\n\u6211\u4eec\u4e0d\u4f1a\u66f4\u6539\u4e0a\u8ff0\u63a5\u53e3\u548c\u7c7b\u3002\u8fd9\u662f\u56e0\u4e3a\u6211\u4eec\u5e0c\u671b\u4e3a\u5b58\u50a8\u5e93\u7528\u6237\u7c7b\u4fdd\u7559\u6267\u884c sync \u6216 async \u65b9\u6cd5\u7684\u53ef\u80fd\u6027\u3002\u6709\u65f6\uff0c\u5f02\u6b65\u4ee3\u7801\u53ef\u80fd\u4f1a\u53d8\u5f97\u6bd4\u540c\u6b65\u4ee3\u7801\u6162\uff0c\u56e0\u4e3a EF Core \u7684\u5f02\u6b65\u547d\u4ee4\u7684\u6267\u884c\u65f6\u95f4\u7565\u957f\uff08\u7531\u4e8e\u5904\u7406\u7ebf\u7a0b\u7684\u989d\u5916\u4ee3\u7801\uff09\uff0c\u56e0\u6b64\u4fdd\u7559\u6b64\u9009\u9879\u59cb\u7ec8\u662f\u4e00\u4e2a\u4e0d\u9519\u7684\u9009\u62e9\u3002<\/p>\n<p>It is general advice to use async code wherever it is possible, but if we notice that our async code runes slower, we should switch back to the sync one.<br \/>\n\u4e00\u822c\u5efa\u8bae\u5c3d\u53ef\u80fd\u4f7f\u7528\u5f02\u6b65\u4ee3\u7801\uff0c\u4f46\u5982\u679c\u6211\u4eec\u6ce8\u610f\u5230\u5f02\u6b65\u4ee3\u7801\u7b26\u6587\u901f\u5ea6\u8f83\u6162\uff0c\u6211\u4eec\u5e94\u8be5\u5207\u6362\u56de\u540c\u6b65\u4ee3\u7801\u3002<\/p>\n<h2>14.3 Modifying the ICompanyRepository Interface and the CompanyRepository Class<\/h2>\n<p>14.3 \u4fee\u6539 ICompanyRepository \u63a5\u53e3\u548c CompanyRepository \u7c7b<\/p>\n<p>In the Contracts project, we can find the ICompanyRepository interface with all the synchronous method signatures which we should change.\u200c<br \/>\n\u5728 Contracts \u9879\u76ee\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u627e\u5230 ICompanyRepository \u63a5\u53e3\uff0c\u5176\u4e2d\u5305\u542b\u6211\u4eec\u5e94\u8be5\u66f4\u6539\u7684\u6240\u6709\u540c\u6b65\u65b9\u6cd5\u7b7e\u540d\u3002<\/p>\n<p>So, let\u2019s do that:<br \/>\n\u6240\u4ee5\uff0c\u8ba9\u6211\u4eec\u8fd9\u6837\u505a\uff1a<\/p>\n<pre><code>using Entities.Models;\n\nnamespace Contracts\n{\n    public interface ICompanyRepository\n    {\n    \/\/    IEnumerable&lt;Company&gt; GetAllCompanies(bool trackChanges); \n    \/\/    Company GetCompany(Guid companyId, bool trackChanges);\n    \/\/    void CreateCompany(Company company);\n    \/\/    IEnumerable&lt;Company&gt; GetByIds(IEnumerable&lt;Guid&gt; ids, bool trackChanges);\n    \/\/    void DeleteCompany(Company company);\n\n        Task&lt;IEnumerable&lt;Company&gt;&gt; GetAllCompaniesAsync(bool trackChanges);\n        Task&lt;Company&gt; GetCompanyAsync(Guid companyId, bool trackChanges);\n        void CreateCompany(Company company);\n        Task&lt;IEnumerable&lt;Company&gt;&gt; GetByIdsAsync(IEnumerable&lt;Guid&gt; ids, bool trackChanges);\n        void DeleteCompany(Company company);\n    }\n}<\/code><\/pre>\n<p>The Create and Delete method signatures are left synchronous. That\u2019s because, in these methods, we are not making any changes in the database. All we're doing is changing the state of the entity to Added and Deleted.<br \/>\nCreate \u548c Delete \u65b9\u6cd5\u7b7e\u540d\u4fdd\u6301\u540c\u6b65\u3002\u8fd9\u662f\u56e0\u4e3a\uff0c\u5728\u8fd9\u4e9b\u65b9\u6cd5\u4e2d\uff0c\u6211\u4eec\u4e0d\u4f1a\u5bf9\u6570\u636e\u5e93\u8fdb\u884c\u4efb\u4f55\u66f4\u6539\u3002\u6211\u4eec\u6240\u505a\u7684\u53ea\u662f\u5c06\u5b9e\u4f53\u7684\u72b6\u6001\u66f4\u6539\u4e3a Added \u548c Deleted\u3002<\/p>\n<p>So, in accordance with the interface changes, let\u2019s modify our CompanyRepository.cs class, which we can find in the Repository project:<br \/>\n\u56e0\u6b64\uff0c\u6839\u636e\u63a5\u53e3\u7684\u53d8\u5316\uff0c\u8ba9\u6211\u4eec\u4fee\u6539CompanyRepository.cs\u7c7b\uff0c\u6211\u4eec\u53ef\u4ee5\u5728 Repository \u9879\u76ee\u4e2d\u627e\u5230\u5b83\uff1a<\/p>\n<pre><code>using Contracts;\nusing Entities.Models;\nusing Microsoft.EntityFrameworkCore;\n\nnamespace Repository\n{\n    public class CompanyRepository : RepositoryBase&lt;Company&gt;, ICompanyRepository\n    {\n        public CompanyRepository(RepositoryContext repositoryContext) : base(repositoryContext) { }\n\n        \/\/public IEnumerable&lt;Company&gt; GetAllCompanies(bool trackChanges) =&gt; FindAll(trackChanges).OrderBy(c =&gt; c.Name).ToList();\n        \/\/public Company GetCompany(Guid companyId, bool trackChanges) =&gt; FindByCondition(c =&gt; c.Id.Equals(companyId), trackChanges).SingleOrDefault();\n        \/\/public void CreateCompany(Company company) =&gt; Create(company);\n        \/\/public IEnumerable&lt;Company&gt; GetByIds(IEnumerable&lt;Guid&gt; ids, bool trackChanges) =&gt; FindByCondition(x =&gt; ids.Contains(x.Id), trackChanges).ToList();\n        \/\/public void DeleteCompany(Company company) =&gt; Delete(company);\n\n        public async Task&lt;IEnumerable&lt;Company&gt;&gt; GetAllCompaniesAsync(bool trackChanges) =&gt; await FindAll(trackChanges).OrderBy(c =&gt; c.Name).ToListAsync();\n        public async Task&lt;Company&gt; GetCompanyAsync(Guid companyId, bool trackChanges) =&gt; await FindByCondition(c =&gt; c.Id.Equals(companyId), trackChanges).SingleOrDefaultAsync();\n        public void CreateCompany(Company company) =&gt; Create(company);\n        public async Task&lt;IEnumerable&lt;Company&gt;&gt; GetByIdsAsync(IEnumerable&lt;Guid&gt; ids, bool trackChanges) =&gt; await FindByCondition(x =&gt; ids.Contains(x.Id), trackChanges).ToListAsync();\n        public void DeleteCompany(Company company) =&gt; Delete(company);\n    }\n}<\/code><\/pre>\n<p>We only have to change these methods in our repository class.<br \/>\n\u6211\u4eec\u53ea\u9700\u8981\u5728\u6211\u4eec\u7684 repository \u7c7b\u4e2d\u66f4\u6539\u8fd9\u4e9b\u65b9\u6cd5\u3002<\/p>\n<h2>14.4 IRepositoryManager and RepositoryManager Changes<\/h2>\n<p>14.4 IRepositoryManager \u548c RepositoryManager \u66f4\u6539<\/p>\n<p>If we inspect the mentioned interface and the class, we will see the Save method, which calls the EF Core\u2019s SaveChanges method. We have to change that as well:\u200c<br \/>\n\u5982\u679c\u6211\u4eec\u68c0\u67e5\u4e0a\u8ff0\u63a5\u53e3\u548c\u7c7b\uff0c\u6211\u4eec\u5c06\u770b\u5230 Save \u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u8c03\u7528 EF Core \u7684 SaveChanges \u65b9\u6cd5\u3002\u6211\u4eec\u4e5f\u5fc5\u987b\u6539\u53d8\u8fd9\u4e00\u70b9\uff1a<\/p>\n<pre><code>namespace Contracts\n{\n    public interface IRepositoryManager\n    {\n        ICompanyRepository Company { get; }\n        IEmployeeRepository Employee { get; }\n\n        \/\/ void Save();\n        Task SaveAsync();\n    }\n}<\/code><\/pre>\n<p>And the RepositoryManager class modification:<br \/>\n\u4ee5\u53ca RepositoryManager \u7c7b\u7684\u4fee\u6539\uff1a<\/p>\n<pre><code>\/\/ public void Save() =&gt; _repositoryContext.SaveChanges();\npublic async Task SaveAsync() =&gt; await _repositoryContext.SaveChangesAsync();<\/code><\/pre>\n<p>Because the SaveAsync(), ToListAsync()... methods are awaitable, we may use the await keyword; thus, our methods need to have the async keyword and Task as a return type.<br \/>\n\u7531\u4e8e SaveAsync\uff08\uff09\u3001ToListAsync\uff08\uff09...methods \u662f awaitable \u7684\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 await \u5173\u952e\u5b57;\u56e0\u6b64\uff0c\u6211\u4eec\u7684\u65b9\u6cd5\u9700\u8981\u5c06 async \u5173\u952e\u5b57\u548c Task \u4f5c\u4e3a\u8fd4\u56de\u7c7b\u578b\u3002<\/p>\n<p>Using the await keyword is not mandatory, though. Of course, if we don\u2019t use it, our SaveAsync() method will execute synchronously \u2014 and that is not our goal here.<br \/>\n\u4f46\u662f\uff0c\u4f7f\u7528 await \u5173\u952e\u5b57\u4e0d\u662f\u5fc5\u9700\u7684\u3002\u5f53\u7136\uff0c\u5982\u679c\u6211\u4eec\u4e0d\u4f7f\u7528\u5b83\uff0c\u6211\u4eec\u7684 SaveAsync\uff08\uff09 \u65b9\u6cd5\u5c06\u540c\u6b65\u6267\u884c \u2014 \u8fd9\u4e0d\u662f\u6211\u4eec\u7684\u76ee\u6807\u3002<\/p>\n<h2>14.5 Updating the Service layer<\/h2>\n<p>14.5 \u66f4\u65b0 Service \u5c42<\/p>\n<p>Again, we have to start with the interface modification:\u200c<br \/>\n\u540c\u6837\uff0c\u6211\u4eec\u5fc5\u987b\u4ece\u63a5\u53e3\u4fee\u6539\u5f00\u59cb\uff1a<\/p>\n<pre><code>using Shared.DataTransferObjects;\n\nnamespace Service.Contracts\n{\n    public interface ICompanyService\n    {\n        \/\/IEnumerable&lt;CompanyDto&gt; GetAllCompanies(bool trackChanges);\n        \/\/CompanyDto GetCompany(Guid companyId, bool trackChanges);\n        \/\/CompanyDto CreateCompany(CompanyForCreationDto company);\n        \/\/IEnumerable&lt;CompanyDto&gt; GetByIds(IEnumerable&lt;Guid&gt; ids, bool trackChanges);\n        \/\/(IEnumerable&lt;CompanyDto&gt; companies, string ids) CreateCompanyCollection(IEnumerable&lt;CompanyForCreationDto&gt; companyCollection);\n        \/\/void DeleteCompany(Guid companyId, bool trackChanges);\n        \/\/void UpdateCompany(Guid companyid, CompanyForUpdateDto companyForUpdate, bool trackChanges);\n\n        Task&lt;IEnumerable&lt;CompanyDto&gt;&gt; GetAllCompaniesAsync(bool trackChanges); \n        Task&lt;CompanyDto&gt; GetCompanyAsync(Guid companyId, bool trackChanges); \n        Task&lt;CompanyDto&gt; CreateCompanyAsync(CompanyForCreationDto company); \n        Task&lt;IEnumerable&lt;CompanyDto&gt;&gt; GetByIdsAsync(IEnumerable&lt;Guid&gt; ids, bool trackChanges); \n        Task&lt;(IEnumerable&lt;CompanyDto&gt; companies, string ids)&gt; CreateCompanyCollectionAsync(IEnumerable&lt;CompanyForCreationDto&gt; companyCollection); \n        Task DeleteCompanyAsync(Guid companyId, bool trackChanges); \n        Task UpdateCompanyAsync(Guid companyid, CompanyForUpdateDto companyForUpdate, bool trackChanges);\n    }\n}<\/code><\/pre>\n<p>And then, let\u2019s modify the class methods one by one.<br \/>\n\u7136\u540e\uff0c\u8ba9\u6211\u4eec\u9010\u4e2a\u4fee\u6539\u7c7b\u65b9\u6cd5\u3002<\/p>\n<p>GetAllCompanies:<\/p>\n<pre><code>\/\/public IEnumerable&lt;CompanyDto&gt; GetAllCompanies(bool trackChanges)\n\/\/{\n\/\/    var companies = _repository.Company.GetAllCompanies(trackChanges);\n\/\/    var companiesDto = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companies);\n\/\/    return companiesDto;\n\/\/}\n\npublic async Task&lt;IEnumerable&lt;CompanyDto&gt;&gt; GetAllCompaniesAsync(bool trackChanges)\n{\n    var companies = await _repository.Company.GetAllCompaniesAsync(trackChanges);\n    var companiesDto = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companies);\n    return companiesDto;\n}<\/code><\/pre>\n<p>GetCompany:<\/p>\n<pre><code>\/\/public CompanyDto GetCompany(Guid id, bool trackChanges)\n\/\/{\n\/\/    var company = _repository.Company.GetCompany(id, trackChanges);\n\/\/    if (company is null) throw new CompanyNotFoundException(id);\n\/\/    var companyDto = _mapper.Map&lt;CompanyDto&gt;(company);\n\/\/    return companyDto;\n\/\/}\n\npublic async Task&lt;CompanyDto&gt; GetCompanyAsync(Guid id, bool trackChanges)\n{\n    var company = await _repository.Company.GetCompanyAsync(id, trackChanges);\n    if (company is null)\n        throw new CompanyNotFoundException(id);\n    var companyDto = _mapper.Map&lt;CompanyDto&gt;(company);\n    return companyDto;\n}<\/code><\/pre>\n<p>CreateCompany:<\/p>\n<pre><code>\/\/public CompanyDto CreateCompany(CompanyForCreationDto company)\n\/\/{\n\/\/    var companyEntity = _mapper.Map&lt;Company&gt;(company);\n\/\/    _repository.Company.CreateCompany(companyEntity);\n\/\/    _repository.Save();\n\/\/    var companyToReturn = _mapper.Map&lt;CompanyDto&gt;(companyEntity);\n\/\/    return companyToReturn;\n\/\/}\n\npublic async Task&lt;CompanyDto&gt; CreateCompanyAsync(CompanyForCreationDto company)\n{\n    var companyEntity = _mapper.Map&lt;Company&gt;(company);\n    _repository.Company.CreateCompany(companyEntity);\n    await _repository.SaveAsync();\n    var companyToReturn = _mapper.Map&lt;CompanyDto&gt;(companyEntity);\n    return companyToReturn;\n}<\/code><\/pre>\n<p>GetByIds:<\/p>\n<pre><code>\/\/public IEnumerable&lt;CompanyDto&gt; GetByIds(IEnumerable&lt;Guid&gt; ids, bool trackChanges)\n\/\/{\n\/\/    if (ids is null)\n\/\/        throw new IdParametersBadRequestException();\n\/\/    var companyEntities = _repository.Company.GetByIds(ids, trackChanges);\n\/\/    if (ids.Count() != companyEntities.Count())\n\/\/        throw new CollectionByIdsBadRequestException();\n\/\/    var companiesToReturn = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companyEntities);\n\/\/    return companiesToReturn;\n\/\/}\n\npublic async Task&lt;IEnumerable&lt;CompanyDto&gt;&gt; GetByIdsAsync(IEnumerable&lt;Guid&gt; ids, bool trackChanges)\n{\n    if (ids is null) throw new IdParametersBadRequestException();\n    var companyEntities = await _repository.Company.GetByIdsAsync(ids, trackChanges);\n    if (ids.Count() != companyEntities.Count())\n        throw new CollectionByIdsBadRequestException();\n    var companiesToReturn = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companyEntities);\n    return companiesToReturn;\n}<\/code><\/pre>\n<p>CreateCompanyCollection:<\/p>\n<pre><code>\/\/public (IEnumerable&lt;CompanyDto&gt; companies, string ids) CreateCompanyCollection(IEnumerable&lt;CompanyForCreationDto&gt; companyCollection)\n\/\/{\n\/\/    if (companyCollection is null)\n\/\/        throw new CompanyCollectionBadRequest();\n\/\/    var companyEntities = _mapper.Map&lt;IEnumerable&lt;Company&gt;&gt;(companyCollection);\n\/\/    foreach (var company in companyEntities)\n\/\/    {\n\/\/        _repository.Company.CreateCompany(company);\n\/\/    }\n\/\/    _repository.Save();\n\/\/    var companyCollectionToReturn = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companyEntities);\n\/\/    var ids = string.Join(&quot;,&quot;, companyCollectionToReturn.Select(c =&gt; c.Id));\n\/\/    return (companies: companyCollectionToReturn, ids: ids);\n\/\/}\n\npublic async Task&lt;(IEnumerable&lt;CompanyDto&gt; companies, string ids)&gt; CreateCompanyCollectionAsync(IEnumerable&lt;CompanyForCreationDto&gt; companyCollection)\n{\n    if (companyCollection is null) \n        throw new CompanyCollectionBadRequest();\n    var companyEntities = _mapper.Map&lt;IEnumerable&lt;Company&gt;&gt;(companyCollection);\n    foreach (var company in companyEntities)\n    {\n        _repository.Company.CreateCompany(company);\n    }\n    await _repository.SaveAsync();\n    var companyCollectionToReturn = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companyEntities);\n    var ids = string.Join(&quot;,&quot;, companyCollectionToReturn.Select(c =&gt; c.Id));\n    return (companies: companyCollectionToReturn, ids: ids);\n}<\/code><\/pre>\n<p>DeleteCompany:<\/p>\n<pre><code>\/\/public void DeleteCompany(Guid companyId, bool trackChanges)\n\/\/{\n\/\/    var company = _repository.Company.GetCompany(companyId, trackChanges);\n\/\/    if (company is null)\n\/\/        throw new CompanyNotFoundException(companyId);\n\/\/    _repository.Company.DeleteCompany(company);\n\/\/    _repository.Save();\n\/\/}\n\npublic async Task DeleteCompanyAsync(Guid companyId, bool trackChanges)\n{\n    var company = await _repository.Company.GetCompanyAsync(companyId, trackChanges);\n    if (company is null)\n        throw new CompanyNotFoundException(companyId);\n    _repository.Company.DeleteCompany(company);\n    await _repository.SaveAsync();\n}<\/code><\/pre>\n<p>UpdateCompany:<\/p>\n<pre><code>\/\/public void UpdateCompany(Guid companyId, CompanyForUpdateDto companyForUpdate, bool trackChanges)\n\/\/{\n\/\/    var companyEntity = _repository.Company.GetCompany(companyId, trackChanges);\n\/\/    if (companyEntity is null)\n\/\/        throw new CompanyNotFoundException(companyId);\n\/\/    _mapper.Map(companyForUpdate, companyEntity);\n\/\/    _repository.Save();\n\/\/}\n\npublic async Task UpdateCompanyAsync(Guid companyId, CompanyForUpdateDto companyForUpdate, bool trackChanges)\n{\n    var companyEntity = await _repository.Company.GetCompanyAsync(companyId, trackChanges);\n    if (companyEntity is null)\n        throw new CompanyNotFoundException(companyId);\n    _mapper.Map(companyForUpdate, companyEntity);\n    await _repository.SaveAsync();\n}}<\/code><\/pre>\n<p>That\u2019s all the changes we have to make in the CompanyService class.<br \/>\n\u8fd9\u5c31\u662f\u6211\u4eec\u5728 CompanyService \u7c7b\u4e2d\u5fc5\u987b\u8fdb\u884c\u7684\u6240\u6709\u66f4\u6539\u3002<\/p>\n<p>Now we can move on to the controller modification.<br \/>\n\u73b0\u5728\u6211\u4eec\u53ef\u4ee5\u7ee7\u7eed\u8fdb\u884c\u63a7\u5236\u5668\u4fee\u6539\u3002<\/p>\n<h2>14.6 Controller Modification<\/h2>\n<p>14.6 \u63a7\u5236\u5668\u4fee\u6539<\/p>\n<p>Finally, we need to modify all of our actions in\u200c the CompaniesController to work asynchronously.<br \/>\n\u6700\u540e\uff0c\u6211\u4eec\u9700\u8981\u4fee\u6539CompaniesController \u5f02\u6b65\u5de5\u4f5c\u3002<\/p>\n<p>So, let\u2019s first start with the GetCompanies method:<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u9996\u5148\u4ece GetCompanies \u65b9\u6cd5\u5f00\u59cb\uff1a<\/p>\n<pre><code>\/\/[HttpGet]\n\/\/public IActionResult GetCompanies()\n\/\/{\n\/\/    \/\/ throw new Exception(&quot;Exception&quot;);\n\/\/    var companies = _service.CompanyService.GetAllCompanies(trackChanges: false);\n\/\/    return Ok(companies);\n\/\/}\n\n[HttpGet]\npublic async Task&lt;IActionResult&gt; GetCompanies()\n{\n    var companies = await _service.CompanyService.GetAllCompaniesAsync(trackChanges: false);\n    return Ok(companies);\n}<\/code><\/pre>\n<p>We haven\u2019t changed much in this action. We\u2019ve just changed the return type and added the async keyword to the method signature. In the method body, we can now await the GetAllCompaniesAsync() method. And that is pretty much what we should do in all the actions in our controller.<br \/>\n\u6211\u4eec\u5728\u8fd9\u6b21\u884c\u52a8\u4e2d\u6ca1\u6709\u592a\u5927\u53d8\u5316\u3002\u6211\u4eec\u521a\u521a\u66f4\u6539\u4e86\u8fd4\u56de\u7c7b\u578b\uff0c\u5e76\u5c06 async \u5173\u952e\u5b57\u6dfb\u52a0\u5230\u65b9\u6cd5\u7b7e\u540d\u4e2d\u3002\u5728\u65b9\u6cd5\u4e3b\u4f53\u4e2d\uff0c\u6211\u4eec\u73b0\u5728\u53ef\u4ee5\u7b49\u5f85 GetAllCompaniesAsync\uff08\uff09 \u65b9\u6cd5\u3002\u8fd9\u51e0\u4e4e\u5c31\u662f\u6211\u4eec\u5728\u63a7\u5236\u5668\u4e2d\u7684\u6240\u6709\u4f5c\u4e2d\u5e94\u8be5\u505a\u7684\u4e8b\u60c5\u3002<\/p>\n<p><b>NOTE:<\/b>  We\u2019ve changed all the method names in the repository and service layers by adding the Async suffix. But, we didn\u2019t do that in the controller\u2019s action. The main reason for that is when a user calls a method from your service or repository layers they can see right-away from the method name whether the method is synchronous or asynchronous. Also, your layers are not limited only to sync or async methods, you can have two methods that do the same thing but one in a sync manner and another in an async manner. In that case, you want to have a name distinction between those methods. For the controller\u2019s actions this is not the case. We are not targeting our actions by their names but by their routes. So, the name of the action doesn\u2019t really add any value as it does for the method names.<br \/>\n\u6ce8\u610f\uff1a\u6211\u4eec\u901a\u8fc7\u6dfb\u52a0 Async \u540e\u7f00\u66f4\u6539\u4e86\u5b58\u50a8\u5e93\u548c\u670d\u52a1\u5c42\u4e2d\u7684\u6240\u6709\u65b9\u6cd5\u540d\u79f0\u3002\u4f46\u662f\uff0c\u6211\u4eec\u6ca1\u6709\u5728\u63a7\u5236\u5668\u7684\u4f5c\u4e2d\u6267\u884c\u6b64\u4f5c\u3002\u4e3b\u8981\u539f\u56e0\u662f\uff0c\u5f53\u7528\u6237\u4ece\u60a8\u7684\u670d\u52a1\u6216\u5b58\u50a8\u5e93\u5c42\u8c03\u7528\u65b9\u6cd5\u65f6\uff0c\u4ed6\u4eec\u53ef\u4ee5\u7acb\u5373\u4ece\u65b9\u6cd5\u540d\u79f0\u4e2d\u770b\u5230\u8be5\u65b9\u6cd5\u662f\u540c\u6b65\u7684\u8fd8\u662f\u5f02\u6b65\u7684\u3002\u6b64\u5916\uff0c\u60a8\u7684\u56fe\u5c42\u4e0d\u4ec5\u9650\u4e8e\u540c\u6b65\u6216\u5f02\u6b65\u65b9\u6cd5\uff0c\u60a8\u8fd8\u53ef\u4ee5\u6709\u4e24\u4e2a\u65b9\u6cd5\u6267\u884c\u76f8\u540c\u7684\u4f5c\uff0c\u4f46\u4e00\u4e2a\u4ee5\u540c\u6b65\u65b9\u5f0f\uff0c\u53e6\u4e00\u4e2a\u4ee5\u5f02\u6b65\u65b9\u5f0f\u3002\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c\u60a8\u5e0c\u671b\u5728\u8fd9\u4e9b\u65b9\u6cd5\u4e4b\u95f4\u8fdb\u884c\u540d\u79f0\u533a\u5206\u3002\u5bf9\u4e8e\u63a7\u5236\u5668\u7684\u4f5c\uff0c\u60c5\u51b5\u5e76\u975e\u5982\u6b64\u3002\u6211\u4eec\u4e0d\u662f\u6839\u636e\u4ed6\u4eec\u7684\u540d\u5b57\u6765\u5b9a\u4f4d\u6211\u4eec\u7684\u884c\u52a8\uff0c\u800c\u662f\u6839\u636e\u4ed6\u4eec\u7684\u8def\u7ebf\u6765\u5b9a\u4f4d\u6211\u4eec\u7684\u884c\u52a8\u3002\u56e0\u6b64\uff0c\u4f5c\u7684\u540d\u79f0\u5e76\u4e0d\u50cf\u65b9\u6cd5\u540d\u79f0\u90a3\u6837\u771f\u6b63\u589e\u52a0\u4efb\u4f55\u503c\u3002<\/p>\n<p>So to continue, let\u2019s modify all the other actions.<br \/>\n\u56e0\u6b64\uff0c\u8981\u7ee7\u7eed\uff0c\u8ba9\u6211\u4eec\u4fee\u6539\u6240\u6709\u5176\u4ed6\u4f5c\u3002<\/p>\n<p>GetCompany:<\/p>\n<pre><code>\/\/[HttpGet(&quot;{id:guid}&quot;, Name = &quot;CompanyById&quot;)]\n\/\/public IActionResult GetCompany(Guid id)\n\/\/{\n\/\/    var company = _service.CompanyService.GetCompany(id, trackChanges: false);\n\/\/    return Ok(company);\n\/\/}\n\n[HttpGet(&quot;{id:guid}&quot;, Name = &quot;CompanyById&quot;)]\npublic async Task&lt;IActionResult&gt; GetCompany(Guid id)\n{\n    var company = await _service.CompanyService.GetCompanyAsync(id, trackChanges: false);\n    return Ok(company);\n}<\/code><\/pre>\n<p>GetCompanyCollection:<\/p>\n<pre><code>\/\/[HttpGet(&quot;collection\/({ids})&quot;, Name = &quot;CompanyCollection&quot;)]\n\/\/public IActionResult GetCompanyCollection([ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable&lt;Guid&gt; ids)\n\/\/{\n\/\/    var companies = _service.CompanyService.GetByIds(ids, trackChanges: false);\n\/\/    return Ok(companies);\n\/\/}\n\n[HttpGet(&quot;collection\/({ids})&quot;, Name = &quot;CompanyCollection&quot;)]\npublic async Task&lt;IActionResult&gt; GetCompanyCollection([ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable&lt;Guid&gt; ids)\n{\n    var companies = await _service.CompanyService.GetByIdsAsync(ids, trackChanges: false);\n    return Ok(companies);\n}<\/code><\/pre>\n<p>CreateCompany:<\/p>\n<pre><code>\/\/[HttpPost]\n\/\/public IActionResult CreateCompany([FromBody] CompanyForCreationDto company)\n\/\/{\n\/\/    if (company is null)\n\/\/        return BadRequest(&quot;CompanyForCreationDto object is null&quot;);\n\/\/    var createdCompany = _service.CompanyService.CreateCompany(company);\n\n\/\/    return CreatedAtRoute(&quot;CompanyById&quot;, new { id = createdCompany.Id }, createdCompany);\n\/\/}\n\n[HttpPost]\npublic async Task&lt;IActionResult&gt; CreateCompany([FromBody] CompanyForCreationDto company)\n{\n    if (company is null)\n        return BadRequest(&quot;CompanyForCreationDto object is null&quot;);\n    if (!ModelState.IsValid)\n        return UnprocessableEntity(ModelState);\n    var createdCompany = await _service.CompanyService.CreateCompanyAsync(company);\n    return CreatedAtRoute(&quot;CompanyById&quot;, new { id = createdCompany.Id }, createdCompany);\n}<\/code><\/pre>\n<p>CreateCompanyCollection:<\/p>\n<pre><code>\/\/[HttpPost(&quot;collection&quot;)]\n\/\/public IActionResult CreateCompanyCollection([FromBody] IEnumerable&lt;CompanyForCreationDto&gt; companyCollection)\n\/\/{\n\/\/    var result = _service.CompanyService.CreateCompanyCollection(companyCollection);\n\/\/    return CreatedAtRoute(&quot;CompanyCollection&quot;, new { result.ids }, result.companies);\n\/\/}\n\n[HttpPost(&quot;collection&quot;)]\npublic async Task&lt;IActionResult&gt; CreateCompanyCollection([FromBody] IEnumerable&lt;CompanyForCreationDto&gt; companyCollection)\n{\n    var result = await _service.CompanyService.CreateCompanyCollectionAsync(companyCollection);\n    return CreatedAtRoute(&quot;CompanyCollection&quot;, new { result.ids }, result.companies);\n}<\/code><\/pre>\n<p>DeleteCompany:<\/p>\n<pre><code>\/\/[HttpDelete(&quot;{id:guid}&quot;)]\n\/\/public IActionResult DeleteCompany(Guid id)\n\/\/{\n\/\/    _service.CompanyService.DeleteCompany(id, trackChanges: false);\n\/\/    return NoContent();\n\/\/}\n\n[HttpDelete(&quot;{id:guid}&quot;)]\npublic async Task&lt;IActionResult&gt; DeleteCompany(Guid id)\n{\n    await _service.CompanyService.DeleteCompanyAsync(id, trackChanges: false);\n    return NoContent();\n}<\/code><\/pre>\n<p>UpdateCompany:<\/p>\n<pre><code>\/\/[HttpPut(&quot;{id:guid}&quot;)]\n\/\/public IActionResult UpdateCompany(Guid id, [FromBody] CompanyForUpdateDto company)\n\/\/{\n\/\/    if (company is null)\n\/\/        return BadRequest(&quot;CompanyForUpdateDto object is null&quot;);\n\/\/    _service.CompanyService.UpdateCompany(id, company, trackChanges: true);\n\/\/    return NoContent();\n\/\/}\n\n[HttpPut(&quot;{id:guid}&quot;)]\npublic async Task&lt;IActionResult&gt; UpdateCompany(Guid id, [FromBody] CompanyForUpdateDto company)\n{\n    if (company is null)\n        return BadRequest(&quot;CompanyForUpdateDto object is null&quot;);\n    await _service.CompanyService.UpdateCompanyAsync(id, company, trackChanges: true);\n    return NoContent();\n}<\/code><\/pre>\n<p>Excellent. Now we are talking async.<br \/>\n\u975e\u5e38\u597d\u3002\u73b0\u5728\u6211\u4eec\u8c08\u8bba\u7684\u662f\u5f02\u6b65\u3002<\/p>\n<p>Of course, we have the Employee entity as well and all of these steps have to be implemented for the EmployeeRepository class, IEmployeeRepository interface, and EmployeesController.<br \/>\n\u5f53\u7136\uff0c\u6211\u4eec\u4e5f\u6709 Employee \u5b9e\u4f53\uff0c\u6240\u6709\u8fd9\u4e9b\u6b65\u9aa4\u90fd\u5fc5\u987b\u4e3a EmployeeRepository \u7c7b\u3001IEmployeeRepository \u63a5\u53e3\u548c EmployeesController \u5b9e\u73b0\u3002<\/p>\n<p>You can always refer to the source code for this chapter if you have any trouble implementing the async code for the Employee entity.<br \/>\n\u5982\u679c\u60a8\u5728\u5b9e\u73b0 Employee \u5b9e\u4f53\u7684\u5f02\u6b65\u4ee3\u7801\u65f6\u9047\u5230\u4efb\u4f55\u95ee\u9898\uff0c\u60a8\u59cb\u7ec8\u53ef\u4ee5\u53c2\u8003\u672c\u7ae0\u7684\u6e90\u4ee3\u7801\u3002<\/p>\n<p>After the async implementation in the Employee classes, you can try to send different requests (from any chapter) to test your async actions. All of them should work as before, without errors, but this time in an asynchronous manner.<br \/>\n\u5728 Employee \u7c7b\u4e2d\u5f02\u6b65\u5b9e\u73b0\u4e4b\u540e\uff0c\u60a8\u53ef\u4ee5\u5c1d\u8bd5\u53d1\u9001\u4e0d\u540c\u7684\u8bf7\u6c42\uff08\u6765\u81ea\u4efb\u4f55\u7ae0\u8282\uff09\u6765\u6d4b\u8bd5\u60a8\u7684\u5f02\u6b65\u4f5c\u3002\u5b83\u4eec\u90fd\u5e94\u8be5\u50cf\u4ee5\u524d\u4e00\u6837\u5de5\u4f5c\uff0c\u6ca1\u6709\u9519\u8bef\uff0c\u4f46\u8fd9\u6b21\u4ee5\u5f02\u6b65\u65b9\u5f0f\u3002<\/p>\n<h2>14.7 Continuation in Asynchronous Programming<\/h2>\n<p>14.7 \u5f02\u6b65\u7f16\u7a0b\u4e2d\u7684\u5ef6\u7eed<\/p>\n<p>The await keyword does three things:\u200c<br \/>\nawait \u5173\u952e\u5b57\u6267\u884c\u4e09\u9879\u4f5c\uff1a<\/p>\n<p>\u2022 It helps us extract the result from the async operation \u2013 we already learned about that<br \/>\n\u5b83\u5e2e\u52a9\u6211\u4eec\u4ece\u5f02\u6b65\u4f5c\u4e2d\u63d0\u53d6\u7ed3\u679c\u2014\u2014\u6211\u4eec\u5df2\u7ecf\u4e86\u89e3\u4e86\u8fd9\u4e00\u70b9<\/p>\n<p>\u2022 Validates the success of the operation<br \/>\n\u9a8c\u8bc1\u4f5c\u662f\u5426\u6210\u529f<\/p>\n<p>\u2022 Provides the Continuation for executing the rest of the code in the async method<br \/>\n\u63d0\u4f9b Continuation\uff0c\u7528\u4e8e\u5728 async \u65b9\u6cd5\u4e2d\u6267\u884c\u5176\u4f59\u4ee3\u7801<\/p>\n<p>So, in our GetCompanyAsync service method, all the code after awaiting an async operation is executed inside the continuation if the async operation was successful.<br \/>\n\u56e0\u6b64\uff0c\u5728\u6211\u4eec\u7684 GetCompanyAsync \u670d\u52a1\u65b9\u6cd5\u4e2d\uff0c\u5982\u679c\u5f02\u6b65\u4f5c\u6210\u529f\uff0c\u5219\u7b49\u5f85\u5f02\u6b65\u4f5c\u540e\u7684\u6240\u6709\u4ee3\u7801\u90fd\u5c06\u5728\u5ef6\u7eed\u5185\u6267\u884c\u3002<\/p>\n<p>When we talk about continuation, it can be confusing because you can read in multiple resources about the SynchronizationContext and capturing the current context to enable this continuation. When we await a task, a request context is captured when await decides to pause the method execution. Once the method is ready to resume its execution, the application takes a thread from a thread pool, assigns it to the context (SynchonizationContext), and resumes the execution. But this is the case for ASP.NET applications.<br \/>\n\u5f53\u6211\u4eec\u8c08\u8bba continuation \u65f6\uff0c\u53ef\u80fd\u4f1a\u4ee4\u4eba\u56f0\u60d1\uff0c\u56e0\u4e3a\u60a8\u53ef\u4ee5\u8bfb\u53d6\u6709\u5173 SynchronizationContext \u7684\u591a\u4e2a\u8d44\u6e90\u5e76\u6355\u83b7\u5f53\u524d\u4e0a\u4e0b\u6587\u4ee5\u542f\u7528\u6b64 continuation\u3002\u5f53\u6211\u4eec\u7b49\u5f85\u4efb\u52a1\u65f6\uff0c\u5f53 await \u51b3\u5b9a\u6682\u505c\u65b9\u6cd5\u6267\u884c\u65f6\uff0c\u4f1a\u6355\u83b7\u8bf7\u6c42\u4e0a\u4e0b\u6587\u3002\u4e00\u65e6\u65b9\u6cd5\u51c6\u5907\u597d\u6062\u590d\u6267\u884c\uff0c\u5e94\u7528\u7a0b\u5e8f\u5c31\u4f1a\u4ece\u7ebf\u7a0b\u6c60\u4e2d\u83b7\u53d6\u4e00\u4e2a\u7ebf\u7a0b\uff0c\u5c06\u5176\u5206\u914d\u7ed9\u4e0a\u4e0b\u6587 \uff08SynchonizationContext\uff09\uff0c\u7136\u540e\u6062\u590d\u6267\u884c\u3002\u4f46 ASP.NET \u5e94\u7528\u7a0b\u5e8f\u5c31\u662f\u8fd9\u79cd\u60c5\u51b5\u3002<\/p>\n<p>We don\u2019t have the SynchronizationContext in ASP.NET Core applications. ASP.NET Core avoids capturing and queuing the context, all it does is take the thread from a thread pool and assign it to the request. So, a lot less background works for the application to do.<br \/>\n\u6211\u4eec\u5728 ASP.NET Core \u5e94\u7528\u7a0b\u5e8f\u4e2d\u6ca1\u6709 SynchronizationContext\u3002ASP.NET Core \u907f\u514d\u4e86\u6355\u83b7\u548c\u6392\u961f\u4e0a\u4e0b\u6587\uff0c\u4f46\u5b83\u6240\u505a\u7684\u53ea\u662f\u4ece\u7ebf\u7a0b\u6c60\u4e2d\u83b7\u53d6\u7ebf\u7a0b\u5e76\u5c06\u5176\u5206\u914d\u7ed9\u8bf7\u6c42\u3002\u56e0\u6b64\uff0c\u5e94\u7528\u7a0b\u5e8f\u9700\u8981\u5b8c\u6210\u7684\u540e\u53f0\u5de5\u4f5c\u8981\u5c11\u5f97\u591a\u3002<\/p>\n<p>One more thing. We are not limited to a single continuation. This means that in a single method, we can use multiple await keywords.<br \/>\n\u8fd8\u6709\u4e00\u4ef6\u4e8b\u3002\u6211\u4eec\u4e0d\u9650\u4e8e\u5355\u4e00\u7684\u5ef6\u7eed\u3002\u8fd9\u610f\u5473\u7740\u5728\u5355\u4e2a\u65b9\u6cd5\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528\u591a\u4e2a await \u5173\u952e\u5b57\u3002<\/p>\n<h2>14.8 Common Pitfalls<\/h2>\n<p>14.8 \u5e38\u89c1\u9677\u9631<\/p>\n<p>In our GetAllCompaniesAsync repository method if we didn\u2019t know any better, we could\u2019ve been tempted to use the Result property instead of the await keyword:\u200c<br \/>\n\u5728\u6211\u4eec\u7684 GetAllCompaniesAsync \u5b58\u50a8\u5e93\u65b9\u6cd5\u4e2d\uff0c\u5982\u679c\u6211\u4eec\u4e0d\u77e5\u9053\u66f4\u591a\uff0c\u6211\u4eec\u53ef\u80fd\u4f1a\u60f3\u4f7f\u7528 Result \u5c5e\u6027\u800c\u4e0d\u662f await \u5173\u952e\u5b57\uff1a<\/p>\n<pre><code>public async Task&lt;IEnumerable&lt;Company&gt;&gt; GetAllCompaniesAsync(bool trackChanges) =&gt; \n    FindAll(trackChanges) \n    .OrderBy(c =&gt; c.Name) \n    .ToListAsync() \n    .Result;<\/code><\/pre>\n<p>We can see that the Result property returns the result we require:<br \/>\n\u6211\u4eec\u53ef\u4ee5\u770b\u5230 Result \u5c5e\u6027\u8fd4\u56de\u6211\u4eec\u9700\u8981\u7684\u7ed3\u679c\uff1a<\/p>\n<pre><code>\/\/ Summary: \n\/\/ Gets the result value of this System.Threading.Tasks.Task`1. \n\/\/ \n\/\/ Returns: \n\/\/ The result value of this System.Threading.Tasks.Task`1, which \n\/\/ is of the same type as the task&#039;s type parameter. \npublic TResult Result \n{ \n    get... \n}<\/code><\/pre>\n<p>But don\u2019t use the Result property.<br \/>\n\u4f46\u4e0d\u8981\u4f7f\u7528 Result \u5c5e\u6027\u3002<\/p>\n<p>With this code, we are going to block the thread and potentially cause a deadlock in the application, which is the exact thing we are trying to avoid using the async and await keywords. It applies the same to the Wait method that we can call on a Task.<br \/>\n\u4f7f\u7528\u6b64\u4ee3\u7801\uff0c\u6211\u4eec\u5c06\u963b\u6b62\u7ebf\u7a0b\u5e76\u53ef\u80fd\u5bfc\u81f4\u5e94\u7528\u7a0b\u5e8f\u4e2d\u7684\u6b7b\u9501\uff0c\u8fd9\u6b63\u662f\u6211\u4eec\u8bd5\u56fe\u907f\u514d\u4f7f\u7528 async \u548c await \u5173\u952e\u5b57\u7684\u4e8b\u60c5\u3002\u5b83\u540c\u6837\u9002\u7528\u4e8e\u6211\u4eec\u53ef\u4ee5\u5728 Task \u4e0a\u8c03\u7528\u7684 Wait \u65b9\u6cd5\u3002<\/p>\n<p>So, that\u2019s it regarding the asynchronous implementation in our project. We\u2019ve learned a lot of useful things from this section and we can move on to the next one \u2013 Action filters.<br \/>\n\u6240\u4ee5\uff0c\u8fd9\u5c31\u662f\u6211\u4eec\u9879\u76ee\u4e2d\u7684\u5f02\u6b65\u5b9e\u73b0\u3002\u6211\u4eec\u4ece\u672c\u8282\u4e2d\u5b66\u5230\u4e86\u5f88\u591a\u6709\u7528\u7684\u4e1c\u897f\uff0c\u6211\u4eec\u53ef\u4ee5\u7ee7\u7eed\u4e0b\u4e00\u4e2a \u2013\u4f5c\u8fc7\u6ee4\u5668\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>14 ASYNCHRONOUS CODE 14 \u5f02\u6b65\u4ee3\u7801 In this chapter, we are going to convert synchronous code to asynchronous inside ASP.NET Core. First, we are going to learn a bit about asynchronous programming and why should we write async code. Then we are going to use our code from the previous chapters and rewrite it in an [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-1126","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1126","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=1126"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1126\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1126"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1126"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1126"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}