{"id":259,"date":"2023-10-15T17:19:07","date_gmt":"2023-10-15T09:19:07","guid":{"rendered":"https:\/\/miie.net\/?p=259"},"modified":"2023-10-15T17:19:07","modified_gmt":"2023-10-15T09:19:07","slug":"ultimate-asp-net-core-web-api-chapter14-asynchronous-code","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=259","title":{"rendered":"Chapter14:ASYNCHRONOUS CODE"},"content":{"rendered":"<h1>14 ASYNCHRONOUS CODE<\/h1>\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.<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u5728 Core \u5185\u90e8\u5c06\u540c\u6b65\u4ee3\u7801\u8f6c\u6362\u4e3a\u5f02\u6b65\u4ee3\u7801 ASP.NET\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\u8981\u7f16\u5199\u5f02\u6b65\u4ee3\u7801\u3002\u7136\u540e\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u524d\u51e0\u7ae0\u4e2d\u7684\u4ee3\u7801\u5e76\u4ee5\u5f02\u6b65\u65b9\u5f0f\u91cd\u5199\u5b83\u3002<\/p>\n<\/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\u4ee5\u53ca\u5982\u4f55\u5728\u5e94\u7528\u7a0b\u5e8f\u4e2d\u4ece\u5934\u5f00\u59cb\u7f16\u5199\u5b83\u3002<\/p>\n<h2>14.1 Whatis Asynchronous Programmming?<\/h2>\n<p>Async programming is a parallel programming technique that allows the working process to run separately from the main application thread. As soon as the work completes, it informs the main thread about the result whether it was successful or not.<br \/>\n\u5f02\u6b65\u7f16\u7a0b\u662f\u4e00\u79cd\u5e76\u884c\u7f16\u7a0b\u6280\u672f\uff0c\u5b83\u5141\u8bb8\u5de5\u4f5c\u8fdb\u7a0b\u4e0e\u4e3b\u5e94\u7528\u7a0b\u5e8f\u7ebf\u7a0b\u5206\u5f00\u8fd0\u884c\u3002\u4e00\u65e6\u5de5\u4f5c\u5b8c\u6210\uff0c\u5b83\u5c31\u4f1a\u901a\u77e5\u4e3b\u7ebf\u7a0b\u7ed3\u679c\u662f\u5426\u6210\u529f\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\u589e\u5f3a\u5e94\u7528\u7a0b\u5e8f\u7684\u54cd\u5e94\u80fd\u529b\u3002<\/p>\n<p>How so?<br \/>\n\u600e\u4e48\u4f1a\u8fd9\u6837\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\u5de5\u4f5c\u5e76\u8fd4\u56de\u5230\u7ebf\u7a0b\u6c60\uff0c\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\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\u4e86\u89e3\u8fd9\u4e00\u70b9\u975e\u5e38\u91cd\u8981\uff0c\u5982\u679c\u6211\u4eec\u5411\u7aef\u70b9\u53d1\u9001\u8bf7\u6c42\uff0c\u5e76\u4e14\u5e94\u7528\u7a0b\u5e8f\u9700\u8981\u4e09\u79d2\u6216\u66f4\u957f\u65f6\u95f4\u6765\u5904\u7406\u8be5\u8bf7\u6c42\uff0c\u6211\u4eec\u53ef\u80fd\u65e0\u6cd5\u5728\u5f02\u6b65\u6a21\u5f0f\u4e0b\u66f4\u5feb\u5730\u6267\u884c\u6b64\u8bf7\u6c42\u3002\u8fd9\u5c06\u82b1\u8d39\u4e0e\u540c\u6b65\u8bf7\u6c42\u76f8\u540c\u7684\u65f6\u95f4\u3002<\/p>\n<p>The only advantage is that in the async mode the thread won\u2019t be blocked three or more seconds, and thus it will be able to process other requests. This is what makes our solution scalable.<br \/>\n\u552f\u4e00\u7684\u4f18\u70b9\u662f\uff0c\u5728\u5f02\u6b65\u6a21\u5f0f\u4e0b\uff0c\u7ebf\u7a0b\u4e0d\u4f1a\u88ab\u963b\u585e\u4e09\u79d2\u6216\u66f4\u957f\u65f6\u95f4\uff0c\u56e0\u6b64\u5b83\u5c06\u80fd\u591f\u5904\u7406\u5176\u4ed6\u8bf7\u6c42\u3002\u8fd9\u5c31\u662f\u4f7f\u6211\u4eec\u7684\u89e3\u51b3\u65b9\u6848\u5177\u6709\u53ef\u6269\u5c55\u6027\u7684\u539f\u56e0\u3002<\/p>\n<p>Here is a visual representation of the asynchronous workflow:<br \/>\n\u4e0b\u9762\u662f\u5f02\u6b65\u5de5\u4f5c\u6d41\u7684\u53ef\u89c6\u5316\u8868\u793a\u5f62\u5f0f\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_1401.png\" alt=\"Alt text\" \/><\/p>\n<p>Now that we've cleared that out, we can learn how to implement asynchronous code in .NET Core.<br \/>\n\u73b0\u5728\u6211\u4eec\u5df2\u7ecf\u6e05\u9664\u4e86\u5b83\uff0c\u6211\u4eec\u53ef\u4ee5\u5b66\u4e60\u5982\u4f55\u5728 .NET Core \u4e2d\u5b9e\u73b0\u5f02\u6b65\u4ee3\u7801\u3002<\/p>\n<h2>14.2 Async,Await Keywords, and Return Types<\/h2>\n<p>The async and await keywords play a crucial part in asynchronous programming. By using those keywords, we can easily write asynchronous methods without too much effort.<br \/>\nasync \u548c await \u5173\u952e\u5b57\u5728\u5f02\u6b65\u7f16\u7a0b\u4e2d\u8d77\u7740\u81f3\u5173\u91cd\u8981\u7684\u4f5c\u7528\u3002\u901a\u8fc7\u4f7f\u7528\u8fd9\u4e9b\u5173\u952e\u5b57\uff0c\u6211\u4eec\u53ef\u4ee5\u8f7b\u677e\u5730\u7f16\u5199\u5f02\u6b65\u65b9\u6cd5\uff0c\u800c\u65e0\u9700\u592a\u591a\u52aa\u529b\u3002<\/p>\n<p>For example, if we want to create a method asynchronously, we need to add the async keyword next to the method\u2019s return type:<br \/>\n\u4f8b\u5982\uff0c\u5982\u679c\u6211\u4eec\u60f3\u5f02\u6b65\u521b\u5efa\u4e00\u4e2a\u65b9\u6cd5\uff0c\u6211\u4eec\u9700\u8981\u5728\u65b9\u6cd5\u7684\u8fd4\u56de\u7c7b\u578b\u65c1\u8fb9\u6dfb\u52a0 async \u5173\u952e\u5b57\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nasync Task&lt;IEnumerable&lt;Company&gt;&gt; GetAllCompaniesAsync()\n<\/pre>\n<\/p>\n<p>By using the async keyword, we are enabling the await keyword and modifying how method results are handled (from synchronous to asynchronous):<br \/>\n\u901a\u8fc7\u4f7f\u7528 async \u5173\u952e\u5b57\uff0c\u6211\u4eec\u5c06\u542f\u7528 await \u5173\u952e\u5b57\u5e76\u4fee\u6539\u65b9\u6cd5\u7ed3\u679c\u7684\u5904\u7406\u65b9\u5f0f\uff08\u4ece\u540c\u6b65\u5230\u5f02\u6b65\uff09\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nawait FindAllAsync();\n<\/pre>\n<\/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<ul>\n<li>Task<TResult>, for an async method that returns a value.<br \/>\nTask<TResult>\uff0c\u7528\u4e8e\u8fd4\u56de\u503c\u7684\u5f02\u6b65\u65b9\u6cd5\u3002<\/li>\n<li>Task, for an async method that does not return a value.<br \/>\n\u4efb\u52a1\uff0c\u7528\u4e8e\u4e0d\u8fd4\u56de\u503c\u7684\u5f02\u6b65\u65b9\u6cd5\u3002<\/li>\n<li>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<\/li>\n<\/ul>\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 Task<int> \u2014 or if the sync method returns IEnumerable<string>, then the async method should return Task&lt;IEnumerable<string>&gt;.<br \/>\n\u597d\u5427\uff0c\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u540c\u6b65\u7f16\u7a0b\u773c\u955c\u6765\u770b\u5f85\u8fd9\u4e00\u70b9\u3002\u5982\u679c\u6211\u4eec\u7684\u540c\u6b65\u65b9\u6cd5\u8fd4\u56de\u4e00\u4e2a int\uff0c\u90a3\u4e48\u5728\u5f02\u6b65\u6a21\u5f0f\u4e0b\u5b83\u5e94\u8be5\u8fd4\u56de Task<int> \u2014 \u6216\u8005\u5982\u679c\u540c\u6b65\u65b9\u6cd5\u8fd4\u56de IEnumerable<string>\uff0c\u5219\u5f02\u6b65\u65b9\u6cd5\u5e94\u8fd4\u56de Task&lt;IEnumerable<string>&gt;\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\u540c\u6b65\u65b9\u6cd5\u4e0d\u8fd4\u56de\u4efb\u4f55\u503c\uff08\u8fd4\u56de\u7c7b\u578b\u4e3a\u7a7a\uff09\uff0c\u90a3\u4e48\u6211\u4eec\u7684\u5f02\u6b65\u65b9\u6cd5\u5e94\u8be5\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\u6ca1\u6709 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\u4f60\u73b0\u5728\u53ef\u80fd\u60f3\u77e5\u9053\uff0c\u4e3a\u4ec0\u4e48\u4e0d\u4e00\u76f4\u8fd4\u56de\u4efb\u52a1\uff1f\u597d\u5427\uff0c\u6211\u4eec\u5e94\u8be5\u53ea\u5bf9\u9700\u8981\u7a7a\u8fd4\u56de\u7c7b\u578b\u3002\u9664\u6b64\u4e4b\u5916\uff0c\u6211\u4eec\u5e94\u8be5\u59cb\u7ec8\u8fd4\u56de\u4e00\u4e2a\u4efb\u52a1\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>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\u638c\u63e1\u4e86\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<h3>14.2.1 The IRepositoryBase Interface and the RepositoryBase Class Explanation<\/h3>\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\u63d0\u5230\u7684\u63a5\u53e3\u548c\u7c7b\u3002\u8fd9\u662f\u56e0\u4e3a\u6211\u4eec\u5e0c\u671b\u4e3a\u5b58\u50a8\u5e93\u7528\u6237\u7c7b\u4fdd\u7559\u540c\u6b65\u6216\u5f02\u6b65\u65b9\u6cd5\u6267\u884c\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\u6267\u884c\u65f6\u95f4\u7a0d\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\u662f\u5982\u679c\u6211\u4eec\u6ce8\u610f\u5230\u5f02\u6b65\u4ee3\u7801\u7684\u8fd0\u884c\u901f\u5ea6\u8f83\u6162\uff0c\u5219\u5e94\u5207\u6362\u56de\u540c\u6b65\u4ee3\u7801\u3002<\/p>\n<h2>14.3 Modifying the ICompanyRepository Interface and the CompanyRepository Class<\/h2>\n<p>In the Contracts project, we can find the ICompanyRepository interface with all the synchronous method signatures which we should change.<br \/>\n\u5728\u5408\u540c\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<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n    public interface ICompanyRepository\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<\/pre>\n<\/p>\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 \/>\n\u201c\u521b\u5efa\u201d\u548c\u201c\u5220\u9664\u201d\u65b9\u6cd5\u7b7e\u540d\u4fdd\u6301\u540c\u6b65\u3002\u8fd9\u662f\u56e0\u4e3a\uff0c\u5728\u8fd9\u4e9b\u65b9\u6cd5\u4e2d\uff0c\u6211\u4eec\u6ca1\u6709\u5bf9\u6570\u636e\u5e93\u3002\u6211\u4eec\u6240\u505a\u7684\u53ea\u662f\u5c06\u5b9e\u4f53\u7684\u72b6\u6001\u66f4\u6539\u4e3a\u201c\u5df2\u6dfb\u52a0\u201d\u548c\u201c\u5df2\u5220\u9664\u201d\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\u6240\u4ee5\uff0c\u6839\u636e\u63a5\u53e3\u7684\u53d8\u5316\uff0c\u8ba9\u6211\u4eec\u4fee\u6539\u6211\u4eec\u7684<\/p>\n<p>CompanyRepository.cs\u7c7b\uff0c\u6211\u4eec\u53ef\u4ee5\u5728\u5b58\u50a8\u5e93\u9879\u76ee\u4e2d\u627e\u5230\u5b83\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic 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;\n    \/\/    FindAll(trackChanges).OrderBy(c =&gt; c.Name).ToList();\n\n    \/\/public Company GetCompany(Guid companyId, bool trackChanges) =&gt;\n    \/\/    FindByCondition(c =&gt;\n    \/\/    c.Id.Equals(companyId), trackChanges)\n    \/\/    .SingleOrDefault();\n\n    \/\/public IEnumerable&lt;Company&gt; GetByIds(IEnumerable&lt;Guid&gt; ids, bool trackChanges) =&gt;\n    \/\/    FindByCondition(x =&gt; ids.Contains(x.Id), trackChanges).ToList();\n\n    public void CreateCompany(Company company) =&gt; Create(company);\n    public void DeleteCompany(Company company) { Delete(company); }\n\n    public async Task&lt;IEnumerable&lt;Company&gt;&gt; GetAllCompaniesAsync(bool trackChanges) =&gt; \n        await FindAll(trackChanges).OrderBy(c =&gt; c.Name).ToListAsync();\n\n    public async Task&lt;Company&gt; GetCompanyAsync(Guid companyId, bool trackChanges) =&gt; \n        await FindByCondition(c =&gt; c.Id.Equals(companyId), trackChanges).SingleOrDefaultAsync();\n\n    public async Task&lt;IEnumerable&lt;Company&gt;&gt; GetByIdsAsync(IEnumerable&lt;Guid&gt; ids, bool trackChanges) =&gt; \n        await FindByCondition(x =&gt; ids.Contains(x.Id), trackChanges).ToListAsync();\n}\n<\/pre>\n<\/p>\n<p>We only have to change these methods in our repository class.<br \/>\n\u6211\u4eec\u53ea\u9700\u8981\u5728\u5b58\u50a8\u5e93\u7c7b\u4e2d\u66f4\u6539\u8fd9\u4e9b\u65b9\u6cd5\u3002<\/p>\n<h2>14.4 IRepositoryManager and RepositoryManager Changes<\/h2>\n<p>If we inspect the mentioned interface and the class, we will see the Save method, which just calls the EF Core\u2019s SaveChanges method. We have to change that as well:<br \/>\n\u5982\u679c\u6211\u4eec\u68c0\u67e5\u63d0\u5230\u7684\u63a5\u53e3\u548c\u7c7b\uff0c\u6211\u4eec\u5c06\u770b\u5230 Save \u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u4ec5\u8c03\u7528 EF Core \u7684 SaveChanges \u65b9\u6cd5\u3002\u6211\u4eec\u4e5f\u5fc5\u987b\u6539\u53d8\u8fd9\u4e00\u70b9\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\npublic interface IRepositoryManager\n{\n    ICompanyRepository Company { get; }\n    IEmployeeRepository Employee { get; }\n\n    \/\/ void Save();\n    Task SaveAsync();\n\n}\n<\/pre>\n<\/p>\n<p>And class modification:<br \/>\n\u548c\u7c7b\u4fee\u6539\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/RepositoryManager.cs\n\n\/\/public void Save() =&gt; _repositoryContext.SaveChanges();\npublic Task SaveAsync() =&gt; _repositoryContext.SaveChangesAsync();\n<\/pre>\n<\/p>\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\u56e0\u4e3a SaveAsync\uff08\uff09\uff0c ToListAsync\uff08\uff09... \u65b9\u6cd5\u662f\u7b49\u5f85\u7684\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528 await \u5173\u952e\u5b57;\u56e0\u6b64\uff0c\u6211\u4eec\u7684\u65b9\u6cd5\u9700\u8981\u6709\u5f02\u6b65\u5173\u952e\u5b57\u548c\u4efb\u52a1\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\u5f3a\u5236\u6027\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\u2014\u8fd9\u4e0d\u662f\u6211\u4eec\u7684\u76ee\u6807\u3002<\/p>\n<h2>14.5 Controller Modification<\/h2>\n<p>Finally, we need to modify all of our actions in the CompaniesController to work asynchronously.<br \/>\n\u6700\u540e\uff0c\u6211\u4eec\u9700\u8981\u4fee\u6539\u6211\u4eec\u6240\u6709\u7684\u64cd\u4f5c<\/p>\n<p>So, let\u2019s first start with the GetCompanies method:<br \/>\n\u516c\u53f8\u63a7\u5236\u5668\u5f02\u6b65\u5de5\u4f5c\u3002\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u9996\u5148\u4ece GetCompanies \u65b9\u6cd5\u5f00\u59cb\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpGet]\npublic async Task&lt;IActionResult&gt; GetCompanies()\n{\n    var companies = await _repository.Company.GetAllCompaniesAsync(trackChanges: false);\n    var companiesDto = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companies);\n    return Ok(companiesDto);\n}\n<\/pre>\n<\/p>\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.<\/p>\n<p>\u6211\u4eec\u5728\u8fd9\u4e2a\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\u662f\u6211\u4eec\u5728\u63a7\u5236\u5668\u4e2d\u7684\u6240\u6709\u64cd\u4f5c\u4e2d\u5e94\u8be5\u505a\u7684\u4e8b\u60c5\u3002<\/p>\n<p>So, let\u2019s modify all the other actions.<br \/>\n\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u4fee\u6539\u6240\u6709\u5176\u4ed6\u63a7\u5236\u5668\u3002<\/p>\n<p>GetCompany:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpGet(&quot;{id}&quot;, Name = &quot;CompanyById&quot;)]\npublic async Task&lt;IActionResult&gt; GetCompany(Guid id)\n{\n    var company = await _repository.Company.GetCompanyAsync(id, trackChanges: false);\n    if (company == null)\n    {\n        _logger.LogInfo($&quot;Company with id: {id} doesn&#039;t exist in the database.&quot;);\n        return NotFound();\n    }\n    else\n    {\n        var companyDto = _mapper.Map&lt;CompanyDto&gt;(company);\n        return Ok(companyDto);\n    }\n}\n<\/pre>\n<\/p>\n<p>GetCompanyCollection:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpGet(&quot;collection\/({ids})&quot;, Name = &quot;CompanyCollection&quot;)]\npublic async Task&lt;IActionResult&gt; GetCompanyCollection(&#x5B;ModelBinder(BinderType = typeof(ArrayModelBinder))] IEnumerable&lt;Guid&gt; ids)\n{\n    if (ids == null)\n    {\n        _logger.LogError(&quot;Parameter ids is null&quot;);\n        return BadRequest(&quot;Parameter ids is null&quot;);\n    }\n    var companyEntities = await _repository.Company.GetByIdsAsync(ids, trackChanges: false);\n    if (ids.Count() != companyEntities.Count())\n    {\n        _logger.LogError(&quot;Some ids are not valid in a collection&quot;);\n        return NotFound();\n    }\n    var companiesToReturn = _mapper.Map&lt;IEnumerable&lt;CompanyDto&gt;&gt;(companyEntities);\n    return Ok(companiesToReturn);\n}\n<\/pre>\n<\/p>\n<p>CreateCompany:<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpPost]\npublic async Task&lt;IActionResult&gt; CreateCompany(&#x5B;FromBody] CompanyForCreationDto company)\n{\n    if (company == null)\n    {\n        _logger.LogError(&quot;CompanyForCreationDto object sent from client is null.&quot;);\n        return BadRequest(&quot;CompanyForCreationDto object is null&quot;);\n    }\n    if (!ModelState.IsValid)\n    {\n        _logger.LogError(&quot;Invalid model state for the CompanyForCreationDto object&quot;);\n        return UnprocessableEntity(ModelState);\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 CreatedAtRoute(&quot;CompanyById&quot;, new { id = companyToReturn.Id }, companyToReturn);\n}\n<\/pre>\n<\/p>\n<p>CreateCompanyCollection:<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpPost(&quot;collection&quot;)]\npublic async Task&lt;IActionResult&gt; CreateCompanyCollection(&#x5B;FromBody] IEnumerable&lt;CompanyForCreationDto&gt; companyCollection)\n{\n    if (companyCollection == null)\n    {\n        _logger.LogError(&quot;Company collection sent from client is null.&quot;);\n        return BadRequest(&quot;Company collection is null&quot;);\n    }\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 CreatedAtRoute(&quot;CompanyCollection&quot;, new { ids }, companyCollectionToReturn);\n}\n<\/pre>\n<\/p>\n<p>DeleteCompany:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpDelete(&quot;{id}&quot;)]\npublic async Task&lt;IActionResult&gt; DeleteCompany(Guid id)\n{\n    var company = await _repository.Company.GetCompanyAsync(id, trackChanges: false);\n    if (company == null)\n    {\n        _logger.LogInfo($&quot;Company with id: {id} doesn&#039;t exist in the database.&quot;);\n        return NotFound();\n    }\n    _repository.Company.DeleteCompany(company);\n    await _repository.SaveAsync();\n    return NoContent();\n}\n<\/pre>\n<\/p>\n<p>UpdateCompany:<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n&#x5B;HttpPut(&quot;{id}&quot;)]\npublic async Task&lt;IActionResult&gt; UpdateCompany(Guid id, &#x5B;FromBody] CompanyForUpdateDto company)\n{\n    if (company == null)\n    {\n        _logger.LogError(&quot;CompanyForUpdateDto object sent from client is null.&quot;);\n        return BadRequest(&quot;CompanyForUpdateDto object is null&quot;);\n    }\n    if (!ModelState.IsValid)\n    {\n        _logger.LogError(&quot;Invalid model state for the CompanyForUpdateDto object&quot;);\n        return UnprocessableEntity(ModelState);\n    }\n    var companyEntity = await _repository.Company.GetCompanyAsync(id, trackChanges: true);\n    if (companyEntity == null)\n    {\n        _logger.LogInfo($&quot;Company with id: {id} doesn&#039;t exist in the database.&quot;);\n        return NotFound();\n    }\n    _mapper.Map(company, companyEntity);\n    await _repository.SaveAsync();\n    return NoContent();\n}\n<\/pre>\n<\/p>\n<p>Excellent. Now we are talking async.<br \/>\n\u975e\u5e38\u597d\u3002\u73b0\u5728\u6211\u4eec\u6b63\u5728\u8c08\u8bba\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 async code for the Employee entity.<br \/>\n\u5982\u679c\u60a8\u5728\u4e3a Employee \u5b9e\u4f53\u5b9e\u73b0\u5f02\u6b65\u4ee3\u7801\u65f6\u9047\u5230\u4efb\u4f55\u95ee\u9898\uff0c\u5219\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\u5b9e\u73b0\u5f02\u6b65\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\u5f02\u6b65\u64cd\u4f5c\u3002\u6240\u6709\u8fd9\u4e9b\u90fd\u5e94\u8be5\u50cf\u4ee5\u524d\u4e00\u6837\u5de5\u4f5c\uff0c\u6ca1\u6709\u9519\u8bef\uff0c\u4f46\u8fd9\u6b21\u662f\u4ee5\u5f02\u6b65\u65b9\u5f0f\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>14 ASYNCHRONOUS CODE 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. [&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":[23],"class_list":["post-259","post","type-post","status-publish","format-standard","hentry","category-csharp","tag-ultimate-asp-net-core-web-api"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/259","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=259"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/259\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=259"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=259"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=259"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}