{"id":212,"date":"2023-10-07T23:33:20","date_gmt":"2023-10-07T15:33:20","guid":{"rendered":"https:\/\/miie.net\/?p=212"},"modified":"2023-10-07T23:33:20","modified_gmt":"2023-10-07T15:33:20","slug":"ultimate-asp-net-core-web-api-chapter6-getting-additional-resources","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=212","title":{"rendered":"Chapter 6 Getting Additional Resources"},"content":{"rendered":"<h1>Chapter 6 Getting Additional Resources<\/h1>\n<p>As of now, we can continue with GET requests by adding additional actions to our controller. Moreover, we are going to create one more controller for the Employee resource and implement an additional action in it.<br \/>\n\u622a\u81f3\u76ee\u524d\uff0c\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u5411\u63a7\u5236\u5668\u6dfb\u52a0\u5176\u4ed6\u64cd\u4f5c\u6765\u7ee7\u7eed\u5904\u7406 GET \u8bf7\u6c42\u3002\u6b64\u5916\uff0c\u6211\u4eec\u5c06\u4e3aEmployee\u8d44\u6e90\u518d\u521b\u5efa\u4e00\u4e2a\u63a7\u5236\u5668\uff0c\u5e76\u5728\u5176\u4e2d\u5b9e\u73b0\u4e00\u4e2a\u9644\u52a0\u64cd\u4f5c\u3002<\/p>\n<h2>6.1 Getting a Single Resource From the Database<\/h2>\n<p>Let\u2019s start by modifying the ICompanyRepository interface:<br \/>\n\u8ba9\u6211\u4eec\u4ece\u4fee\u6539 ICompanyRepository \u63a5\u53e3\u5f00\u59cb\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ ICompanyRepository.cs\n\nusing System;\nusing System.Collections.Generic;\nusing 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    }\n\n}\n\n<\/pre>\n<\/p>\n<p>Then, we are going to implement this interface in the CompanyRepository.cs file:<br \/>\n\u7136\u540e\uff0c\u6211\u4eec\u5c06\u5728CompanyRepository.cs\u6587\u4ef6\u4e2d\u5b9e\u73b0\u6b64\u63a5\u53e3<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/ CompanyRepository.cs\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Contracts;\nusing Entities;\nusing Entities.Models;\n\nnamespace Repository\n{\n    public class CompanyRepository : RepositoryBase&lt;Company&gt;, ICompanyRepository\n    {\n        public CompanyRepository(RepositoryContext repositoryContext) : base(repositoryContext) { }\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}\n\n<\/pre>\n<\/p>\n<p>Finally, let\u2019s change the CompanyController class:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u66f4\u6539 CompanyController \u7c7b\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/CompaniesController.cs\n\n        &#x5B;HttpGet(&quot;{id}&quot;)]\n        public IActionResult GetCompany(Guid id)\n        {\n            var company = _repository.Company.GetCompany(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>The route for this action is \/api\/companies\/id and that\u2019s because the \/api\/companies part applies from the root route (on top of the controller) and the id part is applied from the action attribute [HttpGet(\u201c{id}\u201c)].<br \/>\n\u6b64\u64cd\u4f5c\u7684\u8def\u7531\u662f \/api\/companies\/id\uff0c\u8fd9\u662f\u56e0\u4e3a\/api\/companies\u90e8\u5206\u4ece\u6839\u8def\u7531\uff08\u5728\u63a7\u5236\u5668\u9876\u90e8\uff09\u548c ID \u90e8\u5206\u4ece\u64cd\u4f5c\u5c5e\u6027\u5e94\u7528[HttpGet(\u201c{id}\u201c)]\u3002<\/p>\n<p>So, our action returns IActionResult, like the previous one, and we fetch a single company from the database. If it doesn\u2019t exist, we use the NotFound method to return a 404 status code. From this example, we can see that ASP.NET Core provides us with a variety of semantical methods that state what we can use them for, just by reading their names. The Ok method is for the good result (status code 200) and the NotFound method is for the NotFound result (status code 404).<\/p>\n<p>\u56e0\u6b64\uff0c\u6211\u4eec\u7684\u64cd\u4f5c\u8fd4\u56de IActionResult\uff0c\u5c31\u50cf\u4e0a\u4e00\u4e2a\u4e00\u6837\uff0c\u6211\u4eec\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6\u5355\u4e2a\u516c\u53f8\u3002\u5982\u679c\u5b83\u4e0d\u5b58\u5728\uff0c\u6211\u4eec\u4f7f\u7528 NotFound \u65b9\u6cd5\u8fd4\u56de 404 \u72b6\u6001\u4ee3\u7801\u3002\u4ece\u8fd9\u4e2a\u4f8b\u5b50\u4e2d\uff0c\u6211\u4eec\u53ef\u4ee5\u770b\u5230 ASP.NET Core\u4e3a\u6211\u4eec\u63d0\u4f9b\u4e86\u591a\u79cd\u8bed\u4e49\u65b9\u6cd5\uff0c\u53ea\u9700\u8bfb\u53d6\u5b83\u4eec\u7684\u540d\u79f0\u5373\u53ef\u8bf4\u660e\u6211\u4eec\u53ef\u4ee5\u5c06\u5b83\u4eec\u7528\u4e8e\u4ec0\u4e48\u76ee\u7684\u3002Ok \u65b9\u6cd5\u7528\u4e8e\u826f\u597d\u7ed3\u679c\uff08\u72b6\u6001\u4ee3\u7801 200\uff09\uff0cNotFound \u65b9\u6cd5\u7528\u4e8e NotFound \u7ed3\u679c\uff08\u72b6\u6001\u4ee3\u7801 404\uff09\u3002<\/p>\n<p>If a company exists in the database, we just map it to the CompanyDto type and return it to the client.<br \/>\n\u5982\u679c\u6570\u636e\u5e93\u4e2d\u5b58\u5728\u4e00\u5bb6\u516c\u53f8\uff0c\u6211\u4eec\u53ea\u9700\u5c06\u5176\u6620\u5c04\u5230 CompanyDto\u952e\u5165\u5e76\u5c06\u5176\u8fd4\u56de\u7ed9\u5ba2\u6237\u7aef\u3002<\/p>\n<p>Let\u2019s use Postman to send valid and invalid requests towards our API:<br \/>\n\u8ba9\u6211\u4eec\u4f7f\u7528 Postman \u5411\u6211\u4eec\u7684 API \u53d1\u9001\u6709\u6548\u548c\u65e0\u6548\u7684\u8bf7\u6c42:<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce3\"><a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce3\"><a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce3\">https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce3<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_0601.jpg\" alt=\"\" \/><\/p>\n<p>Invalid request:<br \/>\n\u65e0\u6548\u8bf7\u6c42\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce2\"><a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce2\"><a href=\"https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce2\">https:\/\/localhost:5001\/api\/companies\/3d490a70-94ce-4d15-9494-5248280c2ce2<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_0602.jpg\" alt=\"\" \/><\/p>\n<h2>6.2 Parent\/Child Relationships in WebAPI<\/h2>\n<p>Up until now, we have been working only with the company, which is a parent (principal) entity in our API. But for each company, we have a related employee (dependent entity). Every employee must be related to a certain company and we are going to create our URIs in that manner.<br \/>\n\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u4eec\u4e00\u76f4\u53ea\u4e0e\u516c\u53f8\u5408\u4f5c\uff0c\u8be5\u516c\u53f8\u662f\u6211\u4eecAPI\u4e2d\u7684\u7236\uff08\u4e3b\u4f53\uff09\u5b9e\u4f53\u3002\u4f46\u662f\u5bf9\u4e8e\u6bcf\u4e2a\u516c\u53f8\uff0c\u6211\u4eec\u90fd\u6709\u4e00\u4e2a\u76f8\u5173\u7684\u5458\u5de5\uff08\u4f9d\u8d56\u5b9e\u4f53\uff09\u3002\u6bcf\u4e2a\u5458\u5de5\u90fd\u5fc5\u987b\u4e0e\u67d0\u4e2a\u516c\u53f8\u76f8\u5173\uff0c\u6211\u4eec\u5c06\u4ee5\u8fd9\u79cd\u65b9\u5f0f\u521b\u5efa\u6211\u4eec\u7684 URI\u3002<\/p>\n<p>That said, let\u2019s create a new controller and name it EmployeesController:<br \/>\n\u4e5f\u5c31\u662f\u8bf4\uff0c\u8ba9\u6211\u4eec\u521b\u5efa\u4e00\u4e2a\u65b0\u7684\u63a7\u5236\u5668\u5e76\u547d\u540d\u5b83EmployeesController:<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/EmployeesController.cs\n\nusing AutoMapper;\nusing Contracts;\nusing Microsoft.AspNetCore.Mvc;\n\n&#x5B;Route(&quot;api\/companies\/{companyId}\/employees&quot;)]\n&#x5B;ApiController]\npublic class EmployeesController : ControllerBase\n{\n    private readonly IRepositoryManager _repository; \n    private readonly ILoggerManager _logger;\n    private readonly IMapper _mapper;\n\n    public EmployeesController(IRepositoryManager repository, ILoggerManager logger, IMapper mapper)\n    {\n        _repository = repository; _logger = logger; _mapper = mapper;\n    }\n}\n<\/pre>\n<\/p>\n<p>We are familiar with this code, but our main route is a bit different. As we said, a single employee can\u2019t exist without a company entity and this is exactly what are we exposing through this URI. To get an employee or employees from the database, we have to specify the companyId parameter, and that is something all actions will have in common. For that reason, we have specified this route as our root route.<br \/>\n\u6211\u4eec\u719f\u6089\u6b64\u4ee3\u7801\uff0c\u4f46\u6211\u4eec\u7684\u4e3b\u8981\u8def\u7531\u6709\u70b9\u4e0d\u540c\u3002\u6b63\u5982\u6211\u4eec\u6240\u8bf4\uff0c\u6ca1\u6709\u516c\u53f8\u5b9e\u4f53\uff0c\u5355\u4e2a\u5458\u5de5\u5c31\u65e0\u6cd5\u5b58\u5728\uff0c\u8fd9\u662f\u6211\u4eec\u7a76\u7adf\u901a\u8fc7\u8fd9\u4e2a URI \u516c\u5f00\u4e86\u4ec0\u4e48\u3002\u8981\u4ece\u6570\u636e\u5e93\u4e2d\u83b7\u53d6\u4e00\u4e2a\u6216\u591a\u4e2a\u5458\u5de5\uff0c\u6211\u4eec\u5fc5\u987b\u6307\u5b9a companyId \u53c2\u6570\uff0c\u8fd9\u662f\u6240\u6709\u64cd\u4f5c\u7684\u5171\u540c\u70b9\u3002\u56e0\u6b64\uff0c\u6211\u4eec\u5df2\u5c06\u6b64\u8def\u7531\u6307\u5b9a\u4e3a\u6839\u8def\u7531\u3002<\/p>\n<p>Before we create an action to fetch all the employees per company, we have to modify the IEmployeeRepository interface:<\/p>\n<p>\u5728\u6211\u4eec\u521b\u5efa\u4e00\u4e2a\u64cd\u4f5c\u6765\u83b7\u53d6\u6bcf\u4e2a\u516c\u53f8\u7684\u6240\u6709\u5458\u5de5\u4e4b\u524d\uff0c\u6211\u4eec\u5fc5\u987b\u4fee\u6539 IEmployeeRepository \u63a5\u53e3\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\n\/\/IEmployeeRepository.cs\nusing System;\nusing System.Collections.Generic;\nusing Entities.Models;\n\nnamespace Contracts\n{\n    public interface IEmployeeRepository\n    {\n        IEnumerable&lt;Employee&gt; GetEmployees(Guid companyId, bool trackChanges);\n    }\n}\n\n<\/pre>\n<\/p>\n<p>After interface modification, we are going to modify the EmployeeRepository class:<\/p>\n<p>\u63a5\u53e3\u4fee\u6539\u540e\uff0c\u6211\u4eec\u5c06\u4fee\u6539EmployeeRepository\u7c7b\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/EmployeeRepository.cs\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Contracts;\nusing Entities;\nusing Entities.Models;\n\nnamespace Repository\n{\n    public class EmployeeRepository : RepositoryBase&lt;Employee&gt;, IEmployeeRepository\n    {\n        public EmployeeRepository(RepositoryContext repositoryContext) : base(repositoryContext) { }\n\n        public IEnumerable&lt;Employee&gt; GetEmployees(Guid companyId, bool trackChanges) =&gt; \n            FindByCondition(e =&gt; \n            e.CompanyId.Equals(companyId), trackChanges).\n            OrderBy(e =&gt; e.Name);\n    }\n}\n\n<\/pre>\n<\/p>\n<p>Finally, let\u2019s modify the Employees controller:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u4fee\u6539Employees\u63a7\u5236\u5668\uff1a<\/p>\n<pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/EmployeesController.cs\n\nusing System;\nusing AutoMapper;\nusing Contracts;\nusing Microsoft.AspNetCore.Mvc;\n\n&#x5B;Route(&quot;api\/companies\/{companyId}\/employees&quot;)]\n&#x5B;ApiController]\npublic class EmployeesController : ControllerBase\n{\n    private readonly IRepositoryManager _repository; \n    private readonly ILoggerManager _logger;\n    private readonly IMapper _mapper;\n\n    public EmployeesController(IRepositoryManager repository, ILoggerManager logger, IMapper mapper)\n    {\n        _repository = repository; _logger = logger; _mapper = mapper;\n    }\n\n    &#x5B;HttpGet]\n    public IActionResult GetEmployeesForCompany(Guid companyId)\n    {\n        var company = _repository.Company.GetCompany(companyId, trackChanges: false); \n        if (company == null)\n        {\n            _logger.LogInfo($&quot;Company with id: {companyId} doesn&#039;t exist in the database.&quot;);\n            return NotFound();\n        }\n        var employeesFromDb = _repository.Employee.GetEmployees(companyId, trackChanges: false);\n        return Ok(employeesFromDb);\n    }\n}\n<\/pre>\n<\/p>\n<p>This code is pretty straightforward \u2014 nothing we haven\u2019t seen so far \u2014 but we need to explain just one thing. As you can see, we have the companyId parameter in our action and this parameter will be mapped from the main route. For that reason, we didn\u2019t place it in the [HttpGet] attribute as we did with the GetCompany action.<br \/>\n\u8fd9\u6bb5\u4ee3\u7801\u975e\u5e38\u7b80\u5355\u2014\u2014\u5230\u76ee\u524d\u4e3a\u6b62\u6211\u4eec\u8fd8\u6ca1\u6709\u770b\u5230\u2014\u2014\u4f46\u6211\u4eec\u53ea\u9700\u8981\u89e3\u91ca\u4e00\u4ef6\u4e8b\u3002\u5982\u60a8\u6240\u89c1\uff0c\u6211\u4eec\u7684\u64cd\u4f5c\u4e2d\u6709 companyId \u53c2\u6570\uff0c\u6b64\u53c2\u6570\u5c06\u4ece\u4e3b\u8def\u7531\u6620\u5c04\u3002\u51fa\u4e8e\u8fd9\u4e2a\u539f\u56e0\uff0c\u6211\u4eec\u6ca1\u6709\u50cf\u4f7f\u7528 GetCompany \u64cd\u4f5c\u90a3\u6837\u5c06\u5176\u653e\u5728 [HttpGet] \u5c5e\u6027\u4e2d\u3002<\/p>\n<p>But, we know that something is wrong here because we are using a model in our response and not a data transfer object. To fix that, let\u2019s add another class in the DataTransferObjects folder:<\/p>\n<p>\u4f46\u662f\uff0c\u6211\u4eec\u77e5\u9053\u8fd9\u91cc\u6709\u95ee\u9898\uff0c\u56e0\u4e3a\u6211\u4eec\u5728\u54cd\u5e94\u4e2d\u4f7f\u7528\u4e86\u6a21\u578b\u800c\u4e0d\u662f\u6570\u636e\u4f20\u8f93\u5bf9\u8c61\u3002\u4e3a\u4e86\u89e3\u51b3\u8fd9\u4e2a\u95ee\u9898\uff0c\u8ba9\u6211\u4eec\u5728 DataTransferObjects \u6587\u4ef6\u5939\u4e2d\u6dfb\u52a0\u53e6\u4e00\u4e2a\u7c7b\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/EmployeeDto.cs\n\nusing System;\n\nnamespace Entities.DataTransferObjects\n{\n    public class EmployeeDto\n    {\n        public Guid Id { get; set; }\n        public string Name { get; set; }\n        public int Age { get; set; }\n        public string Position { get; set; }\n    }\n}\n\n<\/pre>\n<\/p>\n<p>After that, let\u2019s create another mapping rule:<br \/>\n\u4e4b\u540e\uff0c\u8ba9\u6211\u4eec\u521b\u5efa\u53e6\u4e00\u4e2a\u6620\u5c04\u89c4\u5219\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/MappingProfile.cs\n\nusing AutoMapper;\nusing Entities.DataTransferObjects;\nusing Entities.Models;\n\nnamespace CompanyEmployees\n{\n    public class MappingProfile:Profile\n    {\n        public MappingProfile() {\n            CreateMap&lt;Company, CompanyDto&gt;().ForMember(c =&gt; \n            c.FullAddress, opt =&gt; opt.MapFrom(x =&gt; string.Join(&#039; &#039;, x.Address, x.Country)));\n\n            CreateMap&lt;Employee, EmployeeDto&gt;();\n        }\n    }\n}\n\n<\/pre>\n<\/p>\n<p>Finally, let\u2019s modify our action:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u4fee\u6539\u6211\u4eec\u7684\u64cd\u4f5c\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/EmployeesController.cs\n\nusing System;\nusing System.Collections.Generic;\nusing AutoMapper;\nusing Contracts;\nusing Entities.DataTransferObjects;\nusing Microsoft.AspNetCore.Mvc;\n\n&#x5B;Route(&quot;api\/companies\/{companyId}\/employees&quot;)]\n&#x5B;ApiController]\npublic class EmployeesController : ControllerBase\n{\n    private readonly IRepositoryManager _repository; \n    private readonly ILoggerManager _logger;\n    private readonly IMapper _mapper;\n\n    public EmployeesController(IRepositoryManager repository, ILoggerManager logger, IMapper mapper)\n    {\n        _repository = repository; _logger = logger; _mapper = mapper;\n    }\n\n    \/\/&#x5B;HttpGet]\n    \/\/public IActionResult GetEmployeesForCompany(Guid companyId)\n    \/\/{\n    \/\/    var company = _repository.Company.GetCompany(companyId, trackChanges: false); \n    \/\/    if (company == null)\n    \/\/    {\n    \/\/        _logger.LogInfo($&quot;Company with id: {companyId} doesn&#039;t exist in the database.&quot;);\n    \/\/        return NotFound();\n    \/\/    }\n    \/\/    var employeesFromDb = _repository.Employee.GetEmployees(companyId, trackChanges: false);\n    \/\/    return Ok(employeesFromDb);\n    \/\/}\n\n    &#x5B;HttpGet]\n    public IActionResult GetEmployeesForCompany(Guid companyId)\n    {\n        var company = _repository.Company.GetCompany(companyId, trackChanges: false);\n        if (company == null)\n        {\n            _logger.LogInfo($&quot;Company with id: {companyId} doesn&#039;t exist in the database.&quot;); \n            return NotFound();\n        }\n        var employeesFromDb = _repository.Employee.GetEmployees(companyId, trackChanges: false); \n        var employeesDto = _mapper.Map&lt;IEnumerable&lt;EmployeeDto&gt;&gt;(employeesFromDb);\n        return Ok(employeesDto);\n    }\n}\n<\/pre>\n<\/p>\n<p>That done, we can send a request with a valid companyId:<br \/>\n\u5b8c\u6210\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u53d1\u9001\u5177\u6709\u6709\u6548\u516c\u53f8 ID \u7684\u8bf7\u6c42\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\">https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_0603.jpg\" alt=\"\" \/><\/p>\n<p>And with an invalid companyId:<br \/>\n\u53d1\u9001\u65e0\u6548\u516c\u53f8ID \uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991873\/employees\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991873\/employees\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991873\/employees\">https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991873\/employees<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_0604.jpg\" alt=\"Alt text\" \/><\/p>\n<p>Excellent. Let\u2019s continue by fetching a single employee.<br \/>\n\u975e\u5e38\u597d\u3002\u8ba9\u6211\u4eec\u7ee7\u7eed\u83b7\u53d6\u4e00\u4e2a\u5458\u5de5\u3002<\/p>\n<h2>6.3 Getting a Single Employee for Company<\/h2>\n<p>So, as we did in previous sections, let\u2019s start with an interface modification:<br \/>\n\u56e0\u6b64\uff0c\u6b63\u5982\u6211\u4eec\u5728\u524d\u9762\u7684\u90e8\u5206\u4e2d\u6240\u505a\u7684\u90a3\u6837\uff0c\u8ba9\u6211\u4eec\u4ece\u63a5\u53e3\u4fee\u6539\u5f00\u59cb\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/IEmployeeRepository.cs\n\nusing System;\nusing System.Collections.Generic;\nusing Entities.Models;\n\nnamespace Contracts\n{\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    }\n}\n<\/pre>\n<\/p>\n<p>Now, let\u2019s implement this method in the EmployeeRepository class:<br \/>\n\u73b0\u5728\uff0c\u8ba9\u6211\u4eec\u5728 EmployeeRepository \u7c7b\u4e2d\u5b9e\u73b0\u6b64\u65b9\u6cd5\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/EmployeeRepository.cs\n\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing Contracts;\nusing Entities;\nusing Entities.Models;\n\nnamespace Repository\n{\n    public class EmployeeRepository : RepositoryBase&lt;Employee&gt;, IEmployeeRepository\n    {\n        public EmployeeRepository(RepositoryContext repositoryContext) : base(repositoryContext) { }\n\n        public IEnumerable&lt;Employee&gt; GetEmployees(Guid companyId, bool trackChanges) =&gt; \n            FindByCondition(e =&gt; \n            e.CompanyId.Equals(companyId), trackChanges)\n            .OrderBy(e =&gt; e.Name);\n\n        public Employee GetEmployee(Guid companyId, Guid id, bool trackChanges) =&gt; \n            FindByCondition(e =&gt; \n            e.CompanyId.Equals(companyId) &amp;&amp; e.Id.Equals(id), trackChanges)\n            .SingleOrDefault();\n    }\n}\n\n<\/pre>\n<\/p>\n<p>Finally, let\u2019s modify the EmployeeController class:<br \/>\n\u6700\u540e\uff0c\u8ba9\u6211\u4eec\u4fee\u6539 EmployeeController \u7c7b\uff1a<\/p>\n<p><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n\/\/EmployeesController.cs\n\nusing System;\nusing System.Collections.Generic;\nusing AutoMapper;\nusing Contracts;\nusing Entities.DataTransferObjects;\nusing Microsoft.AspNetCore.Mvc;\n\n&#x5B;Route(&quot;api\/companies\/{companyId}\/employees&quot;)]\n&#x5B;ApiController]\npublic class EmployeesController : ControllerBase\n{\n    ......\n    ......\n\n    &#x5B;HttpGet(&quot;{id}&quot;)]\n    public IActionResult GetEmployeeForCompany(Guid companyId, Guid id)\n    {\n        var company = _repository.Company.GetCompany(companyId, trackChanges: false);\n        if (company == null)\n        {\n            _logger.LogInfo($&quot;Company with id: {companyId} doesn&#039;t exist in the database.&quot;);\n            return NotFound();\n        }\n        var employeeDb = _repository.Employee.GetEmployee(companyId, id, trackChanges: false);\n        if (employeeDb == null)\n        {\n            _logger.LogInfo($&quot;Employee with id: {id} doesn&#039;t exist in the database.&quot;);\n            return NotFound();\n        }\n        var employee = _mapper.Map&lt;EmployeeDto&gt;(employeeDb);\n        return Ok(employee);\n    }\n}\n<\/pre>\n<\/p>\n<p>Excellent.<br \/>\n\u975e\u5e38\u597d\u3002<\/p>\n<p>We can test this action by using already created requests from the Bonus 2-CompanyEmployeesRequests.postman_collection.json file placed in the folder with the exercise files:<br \/>\n\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528\u6765\u81ea Bonus 2-CompanyEmployeesRequests.postman_collection.json \u6587\u4ef6\u4e2d\u5df2\u521b\u5efa\u7684\u8bf7\u6c42\u6765\u6d4b\u8bd5\u6b64\u64cd\u4f5c\uff0c\u8be5\u6587\u4ef6\u653e\u7f6e\u5728\u5305\u542b\u7ec3\u4e60\u6587\u4ef6\u7684\u6587\u4ef6\u5939\u4e2d\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52A\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52A\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52A\">https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52A<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_0605.jpg\" alt=\"Alt text\" \/><\/p>\n<p>When we send the request with an invalid company or employee id:<br \/>\n\u5f53\u6211\u4eec\u4f7f\u7528\u65e0\u6548\u7684\u516c\u53f8\u6216\u5458\u5de5ID \u53d1\u9001\u8bf7\u6c42\u65f6\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52C\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52C\"><a href=\"https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52C\">https:\/\/localhost:5001\/api\/companies\/c9d4c053-49b6-410c-bc78-2d54a9991870\/employees\/86dba8c0-d178-41E7-938C-ED49778FB52C<\/a><\/a><\/a><\/p>\n<p><img decoding=\"async\" src=\"\/img\/20220824-ultimate-asp-net-core-web-api\/Image_0606.jpg\" alt=\"Alt text\" \/><\/p>\n<p>Our results are pretty self-explanatory.<br \/>\n\u6211\u4eec\u7684\u7ed3\u679c\u662f\u4e0d\u8a00\u81ea\u660e\u7684\u3002<\/p>\n<p>Until now, we have received only JSON formatted responses from our API. But what if we want to support some other format, like XML for example?<br \/>\n\u5230\u76ee\u524d\u4e3a\u6b62\uff0c\u6211\u4eec\u53ea\u6536\u5230\u6765\u81ea API \u7684 JSON \u683c\u5f0f\u54cd\u5e94\u3002\u4f46\u662f\uff0c\u5982\u679c\u6211\u4eec\u60f3\u652f\u6301\u5176\u4ed6\u683c\u5f0f\uff0c\u4f8b\u5982XML\uff0c\u8be5\u600e\u4e48\u529e\uff1f<\/p>\n<p>Well, in the next chapter we are going to learn more about Content Negotiation and enabling different formats for our responses.<br \/>\n\u597d\u5427\uff0c\u5728\u4e0b\u4e00\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u4e86\u89e3\u6709\u5173\u5185\u5bb9\u534f\u5546\u548c\u4e3a\u6211\u4eec\u7684\u54cd\u5e94\u542f\u7528\u4e0d\u540c\u683c\u5f0f\u7684\u66f4\u591a\u4fe1\u606f\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Chapter 6 Getting Additional Resources As of now, we can continue with GET requests by adding additional actions to our controller. Moreover, we are going to create one more controller for the Employee resource and implement an additional action in it. \u622a\u81f3\u76ee\u524d\uff0c\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u5411\u63a7\u5236\u5668\u6dfb\u52a0\u5176\u4ed6\u64cd\u4f5c\u6765\u7ee7\u7eed\u5904\u7406 GET \u8bf7\u6c42\u3002\u6b64\u5916\uff0c\u6211\u4eec\u5c06\u4e3aEmployee\u8d44\u6e90\u518d\u521b\u5efa\u4e00\u4e2a\u63a7\u5236\u5668\uff0c\u5e76\u5728\u5176\u4e2d\u5b9e\u73b0\u4e00\u4e2a\u9644\u52a0\u64cd\u4f5c\u3002 6.1 Getting a Single Resource From the Database Let\u2019s start by [&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-212","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\/212","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=212"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/212\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=212"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=212"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=212"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}