{"id":1118,"date":"2025-05-27T14:46:38","date_gmt":"2025-05-27T06:46:38","guid":{"rendered":"https:\/\/www.hyy.net\/?p=1118"},"modified":"2025-05-27T14:46:38","modified_gmt":"2025-05-27T06:46:38","slug":"ultimate-asp-net-core-web-api-10-working-with-delete-requests","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=1118","title":{"rendered":"Ultimate ASP.NET Core Web API 10 WORKING WITH DELETE REQUESTS"},"content":{"rendered":"<h2>10 WORKING WITH DELETE REQUESTS<\/h2>\n<p>10 \u4f7f\u7528 DELETE \u8bf7\u6c42<\/p>\n<p>Let\u2019s start this section by deleting a child resource first. So, let\u2019s modify the IEmployeeRepository interface:\u200c<br \/>\n\u8ba9\u6211\u4eec\u5148\u5220\u9664\u5b50\u8d44\u6e90\u6765\u5f00\u59cb\u672c\u8282\u3002\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u4fee\u6539 IEmployeeRepository \u63a5\u53e3\uff1a<\/p>\n<pre><code>using Entities.Models;\n\nnamespace Contracts\n{\n    public interface IEmployeeRepository\n    {\n        IEnumerable&lt;Employee&gt; GetEmployees(Guid companyId, bool trackChanges);\n        Employee GetEmployee(Guid companyId, Guid id, bool trackChanges);\n        void CreateEmployeeForCompany(Guid companyId, Employee employee);\n        void DeleteEmployee(Employee employee);\n    }\n}<\/code><\/pre>\n<p>The next step for us is to modify the EmployeeRepository class:<br \/>\n\u4e0b\u4e00\u6b65\u662f\u4fee\u6539 EmployeeRepository \u7c7b\uff1a<\/p>\n<pre><code>public void DeleteEmployee(Employee employee) =&gt; Delete(employee);<\/code><\/pre>\n<p>After that, we have to modify the IEmployeeService interface:<br \/>\n\u4e4b\u540e\uff0c\u6211\u4eec\u5fc5\u987b\u4fee\u6539 IEmployeeService \u63a5\u53e3\uff1a<\/p>\n<pre><code>using Shared.DataTransferObjects;\n\nnamespace Service.Contracts\n{\n    public interface IEmployeeService\n    {\n        IEnumerable&lt;EmployeeDto&gt; GetEmployees(Guid companyId, bool trackChanges);\n        EmployeeDto GetEmployee(Guid companyId, Guid id, bool trackChanges);\n        EmployeeDto CreateEmployeeForCompany(Guid companyId, EmployeeForCreationDto employeeForCreation, bool trackChanges);\n        void DeleteEmployeeForCompany(Guid companyId, Guid id, bool trackChanges);\n    }\n}<\/code><\/pre>\n<p>And of course, the EmployeeService class:<br \/>\n\u5f53\u7136\uff0c\u8fd8\u6709 EmployeeService \u7c7b\uff1a<\/p>\n<pre><code>public void DeleteEmployeeForCompany(Guid companyId, Guid id, bool trackChanges)\n{\n    var company = _repository.Company.GetCompany(companyId, trackChanges);\n    if (company is null) throw new CompanyNotFoundException(companyId);\n    var employeeForCompany = _repository.Employee.GetEmployee(companyId, id, trackChanges);\n    if (employeeForCompany is null)\nthrow new EmployeeNotFoundException(id);\n    _repository.Employee.DeleteEmployee(employeeForCompany);\n    _repository.Save();\n}<\/code><\/pre>\n<p>Pretty straightforward method implementation where we fetch the company and if it doesn\u2019t exist, we return the Not Found response. If it exists, we fetch the employee for that company and execute the same check, where if it\u2019s true, we return another not found response. Lastly, we delete the employee from the database.<br \/>\n\u975e\u5e38\u7b80\u5355\u7684\u65b9\u6cd5\u5b9e\u73b0\uff0c\u6211\u4eec\u83b7\u53d6\u516c\u53f8\uff0c\u5982\u679c\u5b83\u4e0d\u5b58\u5728\uff0c\u6211\u4eec\u8fd4\u56de Not Found \u54cd\u5e94\u3002\u5982\u679c\u5b58\u5728\uff0c\u6211\u4eec\u83b7\u53d6\u8be5\u516c\u53f8\u7684\u5458\u5de5\u5e76\u6267\u884c\u76f8\u540c\u7684\u68c0\u67e5\uff0c\u5982\u679c\u4e3a true\uff0c\u5219\u8fd4\u56de\u53e6\u4e00\u4e2a not found \u54cd\u5e94\u3002\u6700\u540e\uff0c\u6211\u4eec\u4ece\u6570\u636e\u5e93\u4e2d\u5220\u9664\u8be5\u5458\u5de5\u3002<\/p>\n<p>Finally, we can add a delete action to the controller class:<br \/>\n\u6700\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u5411 controller \u7c7b\u6dfb\u52a0\u4e00\u4e2a delete\u4f5c\uff1a<\/p>\n<pre><code>[HttpDelete(&quot;{id:guid}&quot;)]\npublic IActionResult DeleteEmployeeForCompany(Guid companyId, Guid id)\n{\n    _service.EmployeeService.DeleteEmployeeForCompany(companyId, id, trackChanges: false);\n    return NoContent();\n}<\/code><\/pre>\n<p>There is nothing new with this action. We collect the companyId from the root route and the employee\u2019s id from the passed argument. Call the service method and return the NoContent() method, which returns the status code 204 No Content.<br \/>\n\u6b64\u4f5c\u6ca1\u6709\u4ec0\u4e48\u65b0\u5185\u5bb9\u3002\u6211\u4eec\u4ece\u6839\u8def\u7531\u4e2d\u6536\u96c6 companyId\uff0c\u4ece\u4f20\u9012\u7684\u53c2\u6570\u4e2d\u6536\u96c6\u5458\u5de5\u7684 ID\u3002\u8c03\u7528 service \u65b9\u6cd5\u5e76\u8fd4\u56de NoContent\uff08\uff09 \u65b9\u6cd5\uff0c\u8be5\u65b9\u6cd5\u8fd4\u56de\u72b6\u6001\u4ee3\u7801 204 No Content\u3002<\/p>\n<p>Let\u2019s test this:<br \/>\n\u6211\u4eec\u6765\u6d4b\u8bd5\u4e00\u4e0b<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/14759d51-e9c1-4afc-f9bf-08d98898c9c3\/employees\/e06cfcc6-e353-4bd8-0870-08d988af0956\">https:\/\/localhost:5001\/api\/companies\/14759d51-e9c1-4afc-f9bf-08d98898c9c3\/employees\/e06cfcc6-e353-4bd8-0870-08d988af0956<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1001.jpg\" alt=\"alt text\" \/><\/p>\n<p>Excellent. It works great.<br \/>\n\u975e\u5e38\u597d\u3002\u5b83\u6548\u679c\u5f88\u597d\u3002<\/p>\n<p>You can try to get that employee from the database, but you will get 404 for sure:<br \/>\n\u4f60\u53ef\u4ee5\u5c1d\u8bd5\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6\u8be5\u5458\u5de5\uff0c\u4f46\u4f60\u80af\u5b9a\u4f1a\u5f97\u5230 404\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/14759d51-e9c1-4afc-f9bf-08d98898c9c3\/employees\/e06cfcc6-e353-4bd8-0870-08d988af0956\">https:\/\/localhost:5001\/api\/companies\/14759d51-e9c1-4afc-f9bf-08d98898c9c3\/employees\/e06cfcc6-e353-4bd8-0870-08d988af0956<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1002.jpg\" alt=\"alt text\" \/><\/p>\n<p>We can see that the DELETE request isn\u2019t safe because it deletes the resource, thus changing the resource representation. But if we try to send this delete request one or even more times, we would get the same 404 result because the resource doesn\u2019t exist anymore. That\u2019s what makes the DELETE request idempotent.<br \/>\n\u6211\u4eec\u53ef\u4ee5\u770b\u5230 DELETE \u8bf7\u6c42\u4e0d\u5b89\u5168\uff0c\u56e0\u4e3a\u5b83\u4f1a\u5220\u9664\u8d44\u6e90\uff0c\u4ece\u800c\u66f4\u6539\u8d44\u6e90\u8868\u793a\u5f62\u5f0f\u3002\u4f46\u662f\uff0c\u5982\u679c\u6211\u4eec\u5c1d\u8bd5\u53d1\u9001\u4e00\u6b21\u751a\u81f3\u591a\u6b21\u6b64\u5220\u9664\u8bf7\u6c42\uff0c\u6211\u4eec\u5c06\u5f97\u5230\u76f8\u540c\u7684 404 \u7ed3\u679c\uff0c\u56e0\u4e3a\u8d44\u6e90\u4e0d\u518d\u5b58\u5728\u3002\u8fd9\u5c31\u662f DELETE \u8bf7\u6c42\u5177\u6709\u5e42\u7b49\u6027\u7684\u539f\u56e0\u3002<\/p>\n<h2>10.1 Deleting a Parent Resource with its Children<\/h2>\n<p>10.1 \u5220\u9664\u7236\u8d44\u6e90\u53ca\u5176\u5b50\u9879<\/p>\n<p>With Entity Framework Core, this action is pretty simple. With the basic configuration, cascade deleting is enabled, which means deleting a parent resource will automatically delete all of its children. We can confirm that from the migration file:\u200c<br \/>\n\u4f7f\u7528 Entity Framework Core\uff0c\u6b64\u4f5c\u975e\u5e38\u7b80\u5355\u3002\u4f7f\u7528\u57fa\u672c\u914d\u7f6e\u65f6\uff0c\u542f\u7528\u4e86\u7ea7\u8054\u5220\u9664\uff0c\u8fd9\u610f\u5473\u7740\u5220\u9664\u7236\u8d44\u6e90\u5c06\u81ea\u52a8\u5220\u9664\u5176\u6240\u6709\u5b50\u8d44\u6e90\u3002\u6211\u4eec\u53ef\u4ee5\u4ece\u8fc1\u79fb\u6587\u4ef6\u4e2d\u786e\u8ba4\uff1a<\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1003.jpg\" alt=\"alt text\" \/><\/p>\n<p>So, all we have to do is to create a logic for deleting the parent resource.<br \/>\n\u56e0\u6b64\uff0c\u6211\u4eec\u6240\u8981\u505a\u7684\u5c31\u662f\u521b\u5efa\u4e00\u4e2a\u7528\u4e8e\u5220\u9664\u7236\u8d44\u6e90\u7684\u903b\u8f91\u3002<\/p>\n<p>Well, let\u2019s do that following the same steps as in a previous example:<br \/>\n\u597d\u5427\uff0c\u8ba9\u6211\u4eec\u6309\u7167\u4e0e\u4e0a\u4e00\u4e2a\u793a\u4f8b\u76f8\u540c\u7684\u6b65\u9aa4\u6765\u6267\u884c\u6b64\u4f5c\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\n        IEnumerable&lt;Company&gt; GetByIds(IEnumerable&lt;Guid&gt; ids, bool trackChanges);\n        void DeleteCompany(Company company);\n    }\n}<\/code><\/pre>\n<p>Then let\u2019s modify the repository class:<br \/>\n\u7136\u540e\u8ba9\u6211\u4eec\u4fee\u6539 repository \u7c7b\uff1a<\/p>\n<pre><code>public void DeleteCompany(Company company) =&gt; Delete(company);<\/code><\/pre>\n<p>Then we have to modify the service interface:<br \/>\n\u7136\u540e\u6211\u4eec\u5c31\u5f97\u4fee\u6539\u670d\u52a1\u63a5\u53e3\u4e86\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    }\n}<\/code><\/pre>\n<p>And the service class:<br \/>\n\u800c service \u7c7b\uff1a<\/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}<\/code><\/pre>\n<p>Finally, let\u2019s modify our controller:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u4fee\u6539\u6211\u4eec\u7684\u63a7\u5236\u5668\uff1a<\/p>\n<pre><code>[HttpDelete(&quot;{id:guid}&quot;)]\npublic IActionResult DeleteCompany(Guid id)\n{\n    _service.CompanyService.DeleteCompany(id, trackChanges: false);\n    return NoContent();\n}<\/code><\/pre>\n<p>And let\u2019s test our action:<br \/>\n\u8ba9\u6211\u4eec\u6d4b\u8bd5\u4e00\u4e0b\u6211\u4eec\u7684\u64cd\u4f5c\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/0AD5B971-FF51-414D-AF01-34187E407557\">https:\/\/localhost:5001\/api\/companies\/0AD5B971-FF51-414D-AF01-34187E407557<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1004.jpg\" alt=\"alt text\" \/><\/p>\n<p>It works.<br \/>\n\u5b83\u6709\u6548\u3002<\/p>\n<p>You can check in your database that this company alongside its children doesn\u2019t exist anymore.<br \/>\n\u60a8\u53ef\u4ee5\u5728\u6570\u636e\u5e93\u4e2d\u68c0\u67e5\u8fd9\u5bb6\u516c\u53f8\u53ca\u5176\u5b50\u516c\u53f8\u662f\u5426\u4e0d\u518d\u5b58\u5728\u3002<\/p>\n<p>There we go. We have finished working with DELETE requests and we are ready to continue to the PUT requests.<br \/>\n\u597d\u4e86\u3002\u6211\u4eec\u5df2\u7ecf\u5b8c\u6210\u4e86 DELETE \u8bf7\u6c42\u7684\u5904\u7406\uff0c\u5e76\u51c6\u5907\u7ee7\u7eed\u5904\u7406 PUT \u8bf7\u6c42\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>10 WORKING WITH DELETE REQUESTS 10 \u4f7f\u7528 DELETE \u8bf7\u6c42 Let\u2019s start this section by deleting a child resource first. So, let\u2019s modify the IEmployeeRepository interface:\u200c \u8ba9\u6211\u4eec\u5148\u5220\u9664\u5b50\u8d44\u6e90\u6765\u5f00\u59cb\u672c\u8282\u3002\u56e0\u6b64\uff0c\u8ba9\u6211\u4eec\u4fee\u6539 IEmployeeRepository \u63a5\u53e3\uff1a using Entities.Models; namespace Contracts { public interface IEmployeeRepository { IEnumerable&lt;Employee&gt; GetEmployees(Guid companyId, bool trackChanges); Employee GetEmployee(Guid companyId, Guid id, bool trackChanges); void CreateEmployeeForCompany(Guid companyId, Employee employee); void [&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-1118","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1118","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=1118"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1118\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1118"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1118"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1118"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}