{"id":1132,"date":"2025-05-27T14:47:08","date_gmt":"2025-05-27T06:47:08","guid":{"rendered":"https:\/\/www.hyy.net\/?p=1132"},"modified":"2025-05-27T14:47:08","modified_gmt":"2025-05-27T06:47:08","slug":"ultimate-asp-net-core-web-api-17-filtering","status":"publish","type":"post","link":"https:\/\/diji.net\/?p=1132","title":{"rendered":"Ultimate ASP.NET Core Web API 17 FILTERING"},"content":{"rendered":"<p>17 FILTERING<br \/>\n17 \u8fc7\u6ee4<\/p>\n<p>In this chapter, we are going to cover filtering in ASP.NET Core Web API. We\u2019ll learn what filtering is, how it\u2019s different from searching, and how to implement it in a real-world project.\u200c<br \/>\n\u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd ASP.NET Core Web API \u4e2d\u7684\u7b5b\u9009\u3002\u6211\u4eec\u5c06\u4e86\u89e3\u4ec0\u4e48\u662f\u7b5b\u9009\uff0c\u5b83\u4e0e\u641c\u7d22\u6709\u4f55\u4e0d\u540c\uff0c\u4ee5\u53ca\u5982\u4f55\u5728\u5b9e\u9645\u9879\u76ee\u4e2d\u5b9e\u73b0\u5b83\u3002<\/p>\n<p>While not critical as paging, filtering is still an important part of a flexible REST API, so we need to know how to implement it in our API projects.<br \/>\n\u867d\u7136\u8fc7\u6ee4\u4e0d\u50cf\u5206\u9875\u90a3\u6837\u91cd\u8981\uff0c\u4f46\u5b83\u4ecd\u7136\u662f\u7075\u6d3b\u7684 REST API \u7684\u91cd\u8981\u7ec4\u6210\u90e8\u5206\uff0c\u56e0\u6b64\u6211\u4eec\u9700\u8981\u77e5\u9053\u5982\u4f55\u5728\u6211\u4eec\u7684 API \u9879\u76ee\u4e2d\u5b9e\u73b0\u5b83\u3002<\/p>\n<p>Filtering helps us get the exact result set we want instead of all the results without any criteria.<br \/>\n\u7b5b\u9009\u53ef\u4ee5\u5e2e\u52a9\u6211\u4eec\u83b7\u5f97\u6240\u9700\u7684\u786e\u5207\u7ed3\u679c\u96c6\uff0c\u800c\u4e0d\u662f\u6ca1\u6709\u4efb\u4f55\u6761\u4ef6\u7684\u6240\u6709\u7ed3\u679c\u3002<\/p>\n<h2>17.1 What is Filtering?<\/h2>\n<p>17.1 \u4ec0\u4e48\u662f\u8fc7\u6ee4\uff1f<\/p>\n<p>Filtering is a mechanism to retrieve results by providing some kind of criterion. We can write many kinds of filters to get results by type of class property, value range, date range, or anything else.\u200c<br \/>\n\u7b5b\u9009\u662f\u4e00\u79cd\u901a\u8fc7\u63d0\u4f9b\u67d0\u79cd\u6807\u51c6\u6765\u68c0\u7d22\u7ed3\u679c\u7684\u673a\u5236\u3002\u6211\u4eec\u53ef\u4ee5\u7f16\u5199\u591a\u79cd\u8fc7\u6ee4\u5668\u6765\u6309\u7c7b\u5c5e\u6027\u7c7b\u578b\u3001\u503c\u8303\u56f4\u3001\u65e5\u671f\u8303\u56f4\u6216\u5176\u4ed6\u4efb\u4f55\u5185\u5bb9\u6765\u83b7\u53d6\u7ed3\u679c\u3002<\/p>\n<p>When implementing filtering, you are always restricted by the predefined set of options you can set in your request. For example, you can send a date value to request an employee, but you won\u2019t have much success.<br \/>\n\u5b9e\u65bd\u7b5b\u9009\u65f6\uff0c\u60a8\u59cb\u7ec8\u53d7\u5230\u53ef\u5728\u8bf7\u6c42\u4e2d\u8bbe\u7f6e\u7684\u9884\u5b9a\u4e49\u9009\u9879\u96c6\u7684\u9650\u5236\u3002\u4f8b\u5982\uff0c\u60a8\u53ef\u4ee5\u53d1\u9001\u65e5\u671f\u503c\u6765\u8bf7\u6c42\u5458\u5de5\uff0c\u4f46\u4e0d\u4f1a\u6709\u592a\u5927\u7684\u6210\u529f\u3002<\/p>\n<p>On the front end, filtering is usually implemented as checkboxes, radio buttons, or dropdowns. This kind of implementation limits you to only those options that are available to create a valid filter.<br \/>\n\u5728\u524d\u7aef\uff0c\u7b5b\u9009\u901a\u5e38\u5b9e\u73b0\u4e3a\u590d\u9009\u6846\u3001\u5355\u9009\u6309\u94ae\u6216\u4e0b\u62c9\u5217\u8868\u3002\u8fd9\u79cd\u5b9e\u73b0\u5c06\u60a8\u9650\u5236\u4e3a\u4ec5\u53ef\u7528\u4e8e\u521b\u5efa\u6709\u6548\u8fc7\u6ee4\u5668\u7684\u90a3\u4e9b\u9009\u9879\u3002<\/p>\n<p>Take for example a car-selling website. When filtering the cars you want, you would ideally want to select:<br \/>\n\u4ee5\u4e00\u4e2a\u6c7d\u8f66\u9500\u552e\u7f51\u7ad9\u4e3a\u4f8b\u3002\u5728\u7b5b\u9009\u6240\u9700\u7684\u6c7d\u8f66\u65f6\uff0c\u7406\u60f3\u60c5\u51b5\u4e0b\u9700\u8981\u9009\u62e9\uff1a<\/p>\n<p>\u2022 Car manufacturer as a category from a list or a dropdown<br \/>\n\u6c7d\u8f66\u5236\u9020\u5546\u4f5c\u4e3a\u5217\u8868\u6216\u4e0b\u62c9\u5217\u8868\u4e2d\u7684\u7c7b\u522b<\/p>\n<p>\u2022 Car model from a list or a dropdown<br \/>\n\u6765\u81ea\u5217\u8868\u6216\u4e0b\u62c9\u5217\u8868\u7684\u6c7d\u8f66\u6a21\u578b<\/p>\n<p>\u2022 Is it new or used with radio buttons<br \/>\n\u5b83\u662f\u65b0\u7684\u8fd8\u662f\u4e0e\u5355\u9009\u6309\u94ae\u4e00\u8d77\u4f7f\u7528\u7684<\/p>\n<p>\u2022 The city where the seller is as a dropdown<br \/>\n\u5356\u5bb6\u6240\u5728\u7684\u57ce\u5e02\u4f5c\u4e3a\u4e0b\u62c9\u5217\u8868<\/p>\n<p>\u2022 The price of the car is an input field (numeric)<br \/>\n\u6c7d\u8f66\u7684\u4ef7\u683c\u662f\u4e00\u4e2a\u8f93\u5165\u5b57\u6bb5 \uff08\u6570\u5b57\uff09<\/p>\n<p>\u2022 ......<\/p>\n<p>You get the point. So, the request would look something like this:<br \/>\n\u4f60\u660e\u767d\u4e86\u3002\u56e0\u6b64\uff0c\u8bf7\u6c42\u5c06\u5982\u4e0b\u6240\u793a\uff1a<\/p>\n<p><a href=\"https:\/\/bestcarswebsite.com\/sale?manufacturer=ford&amp;model=expedition&amp;state=used&amp;city=washington&amp;price_from=30000&amp;price_to=50000\">https:\/\/bestcarswebsite.com\/sale?manufacturer=ford&model=expedition&state=used&city=washington&price_from=30000&price_to=50000<\/a><\/p>\n<p>Or even like this:<br \/>\n\u6216\u8005\u751a\u81f3\u50cf\u8fd9\u6837\uff1a<\/p>\n<p><a href=\"https:\/\/bestcarswebsite.com\/sale\/filter?data[manufacturer]=ford&amp;[model]=expedition&amp;[state]=used&amp;[city]=washington&amp;[price_from]=30000&amp;[price_to]=50000\">https:\/\/bestcarswebsite.com\/sale\/filter?data[manufacturer]=ford&[model]=expedition&[state]=used&[city]=washington&[price_from]=30000&[price_to]=50000<\/a><\/p>\n<p>Now that we know what filtering is, let\u2019s see how it\u2019s different from searching.<br \/>\n\u73b0\u5728\u6211\u4eec\u77e5\u9053\u4e86\u4ec0\u4e48\u662f\u7b5b\u9009\uff0c\u8ba9\u6211\u4eec\u770b\u770b\u5b83\u4e0e\u641c\u7d22\u6709\u4ec0\u4e48\u4e0d\u540c\u3002<\/p>\n<h2>17.2 How is Filtering Different from Searching?<\/h2>\n<p>17.2 \u8fc7\u6ee4\u4e0e\u641c\u7d22\u6709\u4f55\u4e0d\u540c\uff1f<\/p>\n<p>When searching for results, we usually have only one input and that\u2019s the\u200c one you use to search for anything within a website.<br \/>\n\u5728\u641c\u7d22\u7ed3\u679c\u4e2d\uff0c\u6211\u4eec\u901a\u5e38\u53ea\u6709\u4e00\u4e2a\u8f93\u5165\uff0c\u90a3\u5c31\u662f\u60a8\u7528\u6765\u641c\u7d22\u7f51\u7ad9\u5185\u4efb\u4f55\u5185\u5bb9\u7684\u8f93\u5165\u3002<\/p>\n<p>So in other words, you send a string to the API and the API is responsible for using that string to find any results that match it.<br \/>\n\u6362\u53e5\u8bdd\u8bf4\uff0c\u60a8\u5411 API \u53d1\u9001\u4e00\u4e2a\u5b57\u7b26\u4e32\uff0cAPI \u8d1f\u8d23\u4f7f\u7528\u8be5\u5b57\u7b26\u4e32\u67e5\u627e\u4e0e\u5b83\u5339\u914d\u7684\u4efb\u4f55\u7ed3\u679c\u3002<\/p>\n<p>On our car website, we would use the search field to find the \u201cFord Expedition\u201d car model and we would get all the results that match the car name \u201cFord Expedition.\u201d Thus, this search would return every \u201cFord Expedition\u201d car available.<br \/>\n\u5728\u6211\u4eec\u7684\u6c7d\u8f66\u7f51\u7ad9\u4e0a\uff0c\u6211\u4eec\u5c06\u4f7f\u7528\u641c\u7d22\u5b57\u6bb5\u67e5\u627e\u201cFord Expedition\u201d\u6c7d\u8f66\u6a21\u578b\uff0c\u6211\u4eec\u5c06\u83b7\u5f97\u4e0e\u6c7d\u8f66\u540d\u79f0\u201cFord Expedition\u201d\u5339\u914d\u7684\u6240\u6709\u7ed3\u679c\u3002\u56e0\u6b64\uff0c\u6b64\u641c\u7d22\u5c06\u8fd4\u56de\u6240\u6709\u53ef\u7528\u7684\u201cFord Expedition\u201d\u6c7d\u8f66\u3002<\/p>\n<p>We can also improve the search by implementing search terms like Google does, for example. If the user enters the Ford Expedition without quotes in the search field, we would return both what\u2019s relevant to Ford and Expedition. But if the user puts quotes around it, we would search the entire term \u201cFord Expedition\u201d in our database.<br \/>\n\u4f8b\u5982\uff0c\u6211\u4eec\u8fd8\u53ef\u4ee5\u901a\u8fc7\u50cf Google \u4e00\u6837\u5b9e\u65bd\u641c\u7d22\u8bcd\u6765\u6539\u8fdb\u641c\u7d22\u3002\u5982\u679c\u7528\u6237\u5728\u641c\u7d22\u5b57\u6bb5\u4e2d\u8f93\u5165 Ford Expedition\uff0c\u4f46\u4e0d\u5305\u542b\u5f15\u53f7\uff0c\u6211\u4eec\u5c06\u540c\u65f6\u8fd4\u56de\u4e0e Ford \u548c Expedition \u76f8\u5173\u7684\u5185\u5bb9\u3002\u4f46\u662f\uff0c\u5982\u679c\u7528\u6237\u7528\u5f15\u53f7\u62ec\u8d77\u6765\uff0c\u6211\u4eec\u4f1a\u5728\u6211\u4eec\u7684\u6570\u636e\u5e93\u4e2d\u641c\u7d22\u6574\u4e2a\u672f\u8bed \u201cFord Expedition\u201d\u3002<\/p>\n<p>It makes a better user experience. Example:<br \/>\n\u5b83\u53ef\u4ee5\u63d0\u4f9b\u66f4\u597d\u7684\u7528\u6237\u4f53\u9a8c\u3002\u793a\u4f8b\uff1a<br \/>\n<a href=\"https:\/\/bestcarswebsite.com\/sale\/search?name=fordfocus\">https:\/\/bestcarswebsite.com\/sale\/search?name=fordfocus<\/a><\/p>\n<p>Using search doesn\u2019t mean we can\u2019t use filters with it. It makes perfect sense to use filtering and searching together, so we need to take that into account when writing our source code.<br \/>\n\u4f7f\u7528 search \u5e76\u4e0d\u610f\u5473\u7740\u6211\u4eec\u4e0d\u80fd\u5bf9\u5b83\u4f7f\u7528 filter\u3002\u540c\u65f6\u4f7f\u7528 filtering \u548c search \u975e\u5e38\u6709\u610f\u4e49\uff0c\u56e0\u6b64\u6211\u4eec\u5728\u7f16\u5199\u6e90\u4ee3\u7801\u65f6\u9700\u8981\u8003\u8651\u5230\u8fd9\u4e00\u70b9\u3002<\/p>\n<p>But enough theory.<br \/>\n\u4f46\u7406\u8bba\u5df2\u7ecf\u8db3\u591f\u4e86\u3002<\/p>\n<p>Let\u2019s implement some filters.<br \/>\n\u8ba9\u6211\u4eec\u5b9e\u73b0\u4e00\u4e9b\u8fc7\u6ee4\u5668\u3002<\/p>\n<h2>17.3 How to Implement Filtering in ASP.NET Core Web API<\/h2>\n<p>17.3 \u5982\u4f55\u5728 ASP.NET Core Web API \u4e2d\u5b9e\u73b0\u8fc7\u6ee4<\/p>\n<p>We have the Age property in our Employee class. Let\u2019s say we want to find out which employees are between the ages of 26 and 29. We also want to be able to enter just the starting age \u2014 and not the ending one \u2014 and vice versa.\u200c<br \/>\n\u6211\u4eec\u7684 Employee \u7c7b\u4e2d\u6709 Age \u5c5e\u6027\u3002\u5047\u8bbe\u6211\u4eec\u60f3\u8981\u4e86\u89e3\u54ea\u4e9b\u5458\u5de5\u7684\u5e74\u9f84\u5728 26 \u5230 29 \u5c81\u4e4b\u95f4\u3002\u6211\u4eec\u8fd8\u5e0c\u671b\u80fd\u591f\u53ea\u8f93\u5165\u8d77\u59cb\u5e74\u9f84 \u2014 \u800c\u4e0d\u662f\u7ed3\u675f\u5e74\u9f84 \u2014 \u53cd\u4e4b\u4ea6\u7136\u3002<\/p>\n<p>We would need a query like this one:<br \/>\n\u6211\u4eec\u9700\u8981\u4e00\u4e2a\u8fd9\u6837\u7684\u67e5\u8be2\uff1a<\/p>\n<p><a href=\"https:\/\/localhost:5001\/api\/companies\/companyId\/employees?minAge=26&amp;maxAge=29\">https:\/\/localhost:5001\/api\/companies\/companyId\/employees?minAge=26&maxAge=29<\/a><\/p>\n<p>But, we want to be able to do this too:<br \/>\n\u4f46\u662f\uff0c\u6211\u4eec\u4e5f\u5e0c\u671b\u80fd\u591f\u505a\u5230\u8fd9\u4e00\u70b9\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/companyId\/employees?minAge=26\">https:\/\/localhost:5001\/api\/companies\/companyId\/employees?minAge=26<\/a><\/p>\n<p>Or like this:<br \/>\n\u6216\u8005\u50cf\u8fd9\u6837\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/companyId\/employees?maxAge=29\">https:\/\/localhost:5001\/api\/companies\/companyId\/employees?maxAge=29<\/a><\/p>\n<p>Okay, we have a specification. Let\u2019s see how to implement it.<br \/>\n\u597d\u7684\uff0c\u6211\u4eec\u6709\u4e00\u4e2a\u89c4\u8303\u3002\u8ba9\u6211\u4eec\u770b\u770b\u5982\u4f55\u5b9e\u73b0\u5b83\u3002<\/p>\n<p>We\u2019ve already implemented paging in our controller, so we have the necessary infrastructure to extend it with the filtering functionality. We\u2019ve used the EmployeeParameters class, which inherits from the RequestParameters class, to define the query parameters for our paging request.<br \/>\n\u6211\u4eec\u5df2\u7ecf\u5728\u63a7\u5236\u5668\u4e2d\u5b9e\u73b0\u4e86\u5206\u9875\uff0c\u56e0\u6b64\u6211\u4eec\u62e5\u6709\u5fc5\u8981\u7684\u57fa\u7840\u8bbe\u65bd\u6765\u4f7f\u7528\u8fc7\u6ee4\u529f\u80fd\u6765\u6269\u5c55\u5b83\u3002\u6211\u4eec\u4f7f\u7528\u4e86 EmployeeParameters \u7c7b\uff08\u7ee7\u627f\u81ea RequestParameters \u7c7b\uff09\u6765\u5b9a\u4e49\u5206\u9875\u8bf7\u6c42\u7684\u67e5\u8be2\u53c2\u6570\u3002<\/p>\n<p>Let\u2019s extend the EmployeeParameters class:<br \/>\n\u8ba9\u6211\u4eec\u6269\u5c55 EmployeeParameters \u7c7b\uff1a<\/p>\n<pre><code>namespace Shared.RequestFeatures;\n\npublic class EmployeeParameters : RequestParameters\n{\n    public uint MinAge { get; set; }\n    public uint MaxAge { get; set; } = int.MaxValue;\n    public bool ValidAgeRange =&gt; MaxAge &gt; MinAge;\n}<\/code><\/pre>\n<p>We\u2019ve added two unsigned int properties (to avoid negative year values):MinAge and MaxAge.<br \/>\n\u6211\u4eec\u6dfb\u52a0\u4e86\u4e24\u4e2a unsigned int \u5c5e\u6027\uff08\u4ee5\u907f\u514d\u8d1f\u5e74\u4efd\u503c\uff09\uff1aMinAge \u548c MaxAge\u3002<\/p>\n<p>Since the default uint value is 0, we don\u2019t need to explicitly define it; 0 is okay in this case. For MaxAge, we want to set it to the max int value. If we don\u2019t get it through the query params, we have something to work with. It doesn\u2019t matter if someone sets the age to 300 through the params; it won\u2019t affect the results.<br \/>\n\u7531\u4e8e\u9ed8\u8ba4\u7684 uint \u503c\u4e3a 0\uff0c\u56e0\u6b64\u6211\u4eec\u4e0d\u9700\u8981\u663e\u5f0f\u5b9a\u4e49\u5b83;\u5728\u8fd9\u79cd\u60c5\u51b5\u4e0b\uff0c0 \u662f\u53ef\u4ee5\u7684\u3002\u5bf9\u4e8e MaxAge\uff0c\u6211\u4eec\u5e0c\u671b\u5c06\u5176\u8bbe\u7f6e\u4e3a max int \u503c\u3002\u5982\u679c\u6211\u4eec\u6ca1\u6709\u901a\u8fc7 query params \u83b7\u53d6\u5b83\uff0c\u6211\u4eec\u6709\u4e00\u4e9b\u4e1c\u897f\u53ef\u4ee5\u4f7f\u7528\u3002\u5982\u679c\u6709\u4eba\u901a\u8fc7 params \u5c06 age \u8bbe\u7f6e\u4e3a 300 \u5e76\u4e0d\u91cd\u8981;\u5b83\u4e0d\u4f1a\u5f71\u54cd\u7ed3\u679c\u3002<\/p>\n<p>We\u2019ve also added a simple validation property \u2013 ValidAgeRange. Its purpose is to tell us if the max-age is indeed greater than the min-age. If it\u2019s not, we want to let the API user know that he\/she is doing something wrong.<br \/>\n\u6211\u4eec\u8fd8\u6dfb\u52a0\u4e86\u4e00\u4e2a\u7b80\u5355\u7684\u9a8c\u8bc1\u5c5e\u6027 \u2013 ValidAgeRange\u3002\u5b83\u7684\u76ee\u7684\u662f\u544a\u8bc9\u6211\u4eec max-age \u662f\u5426\u786e\u5b9e\u5927\u4e8e min-age\u3002\u5982\u679c\u4e0d\u662f\uff0c\u6211\u4eec\u60f3\u8ba9 API \u7528\u6237\u77e5\u9053\u4ed6\/\u5979\u505a\u9519\u4e86\u4ec0\u4e48\u3002<\/p>\n<p>Okay, now that we have our parameters ready, we can modify the GetEmployeesAsync service method by adding a validation check as a first statement:<br \/>\n\u597d\u4e86\uff0c\u73b0\u5728\u6211\u4eec\u5df2\u7ecf\u51c6\u5907\u597d\u4e86\u53c2\u6570\uff0c\u6211\u4eec\u53ef\u4ee5\u901a\u8fc7\u6dfb\u52a0\u9a8c\u8bc1\u68c0\u67e5\u4f5c\u4e3a\u7b2c\u4e00\u4e2a\u8bed\u53e5\u6765\u4fee\u6539 GetEmployeesAsync \u670d\u52a1\u65b9\u6cd5\uff1a<\/p>\n<pre><code>public async Task&lt;(IEnumerable&lt;EmployeeDto&gt; employees, MetaData metaData)&gt; GetEmployeesAsync\n    (Guid companyId, EmployeeParameters employeeParameters, bool trackChanges)\n{\n    if (!employeeParameters.ValidAgeRange)\n        throw new MaxAgeRangeBadRequestException();\n\n    await CheckIfCompanyExists(companyId, trackChanges);\n\n    var employeesWithMetaData = await _repository.Employee\n        .GetEmployeesAsync(companyId, employeeParameters, trackChanges);\n    var employeesDto = _mapper.Map&lt;IEnumerable&lt;EmployeeDto&gt;&gt;(employeesWithMetaData);\n\n    return (employees: employeesDto, metaData: employeesWithMetaData.MetaData);\n}<\/code><\/pre>\n<p>We\u2019ve added our validation check and a BadRequest response if the validation fails.<br \/>\n\u6211\u4eec\u6dfb\u52a0\u4e86\u9a8c\u8bc1\u68c0\u67e5\u548c\u9a8c\u8bc1\u5931\u8d25\u65f6\u7684 BadRequest \u54cd\u5e94\u3002<\/p>\n<p>But we don\u2019t have this custom exception class so, we have to create it in the Entities\/Exceptions class:<br \/>\n\u4f46\u662f\u6211\u4eec\u6ca1\u6709\u8fd9\u4e2a\u81ea\u5b9a\u4e49\u5f02\u5e38\u7c7b\uff0c\u56e0\u6b64\uff0c\u6211\u4eec\u5fc5\u987b\u5728 Entities\/Exceptions \u7c7b\u4e2d\u521b\u5efa\u5b83\uff1a<\/p>\n<pre><code>namespace Entities.Exceptions;\n\npublic sealed class MaxAgeRangeBadRequestException : BadRequestException\n{\n    public MaxAgeRangeBadRequestException()\n        : base(&quot;Max age can&#039;t be less than min age.&quot;)\n    {\n    }\n}<\/code><\/pre>\n<p>That should do it.<br \/>\n\u90a3\u5e94\u8be5\u53ef\u4ee5\u3002<\/p>\n<p>After the service class modification and creation of our custom exception class, let\u2019s get to the implementation in our EmployeeRepository class:<br \/>\n\u5728\u4fee\u6539\u670d\u52a1\u7c7b\u5e76\u521b\u5efa\u81ea\u5b9a\u4e49\u5f02\u5e38\u7c7b\u4e4b\u540e\uff0c\u8ba9\u6211\u4eec\u5f00\u59cb EmployeeRepository \u7c7b\u4e2d\u7684\u5b9e\u73b0\uff1a<\/p>\n<pre><code>public async Task&lt;PagedList&lt;Employee&gt;&gt; GetEmployeesAsync(Guid companyId,\n    EmployeeParameters employeeParameters, bool trackChanges)\n{\n    var employees = await FindByCondition(e =&gt; e.CompanyId.Equals(companyId) &amp;&amp;\n        (e.Age &gt;= employeeParameters.MinAge &amp;&amp; e.Age &lt;= employeeParameters.MaxAge), trackChanges)\n        .OrderBy(e =&gt; e.Name)\n        .ToListAsync();\n\n    return PagedList&lt;Employee&gt;\n        .ToPagedList(employees, employeeParameters.PageNumber, employeeParameters.PageSize);\n}<\/code><\/pre>\n<p>Actually, at this point, the implementation is rather simple too.<br \/>\n\u5b9e\u9645\u4e0a\uff0c\u5728\u8fd9\u4e00\u70b9\u4e0a\uff0c\u5b9e\u73b0\u4e5f\u76f8\u5f53\u7b80\u5355\u3002<\/p>\n<p>We are using the FindByCondition method to find all the employees with an Age between the MaxAge and the MinAge.<br \/>\n\u6211\u4eec\u4f7f\u7528 FindByCondition \u65b9\u6cd5\u67e5\u627e Age \u4ecb\u4e8e MaxAge \u548c MinAge \u4e4b\u95f4\u7684\u6240\u6709\u5458\u5de5\u3002<\/p>\n<p>Let\u2019s try it out.<br \/>\n\u8ba9\u6211\u4eec\u8bd5\u4e00\u8bd5\u3002<\/p>\n<h2>17.4 Sending and Testing a Query<\/h2>\n<p>17.4 \u53d1\u9001\u548c\u6d4b\u8bd5\u67e5\u8be2<\/p>\n<p>Let\u2019s send a first request with only a MinAge parameter:\u200c<br \/>\n\u8ba9\u6211\u4eec\u53d1\u9001\u7b2c\u4e00\u4e2a\u53ea\u6709\u4e00\u4e2a MinAge \u53c2\u6570\u7684\u8bf7\u6c42\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees?minAge=32\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees?minAge=32<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1701.jpg\" alt=\"alt text\" \/><\/p>\n<p>Next, let\u2019s send one with only a MaxAge parameter:<br \/>\n\u63a5\u4e0b\u6765\uff0c\u8ba9\u6211\u4eec\u53d1\u9001\u4e00\u4e2a\u4ec5\u5305\u542b MaxAge \u53c2\u6570\u7684 Cookie\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees?maxAge=26\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees?maxAge=26<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1702.jpg\" alt=\"alt text\" \/><\/p>\n<p>After that, we can combine those two:<br \/>\n\u4e4b\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u5c06\u8fd9\u4e24\u8005\u5408\u5e76\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78<\/a>- 2D54A9991870\/employees?minAge=26&amp;maxAge=30<\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1703.jpg\" alt=\"alt text\" \/><\/p>\n<p>And finally, we can test the filter with the paging:<br \/>\n\u6700\u540e\uff0c\u6211\u4eec\u53ef\u4ee5\u4f7f\u7528\u5206\u9875\u6765\u6d4b\u8bd5\u8fc7\u6ee4\u5668\uff1a<br \/>\n<a href=\"https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees?pageNumber=1&amp;pageSize=4&amp;minAge=32&amp;maxAge=35\">https:\/\/localhost:5001\/api\/companies\/C9D4C053-49B6-410C-BC78-2D54A9991870\/employees?pageNumber=1&pageSize=4&minAge=32&maxAge=35<\/a><\/p>\n<p><img decoding=\"async\" src=\"\/images\/ultimateaspnetcorewebapi6\/1704.jpg\" alt=\"alt text\" \/><\/p>\n<p>Excellent. The filter is implemented and we can move on to the searching part.<br \/>\n\u975e\u5e38\u597d\u3002\u8fc7\u6ee4\u5668\u5df2\u5b9e\u73b0\uff0c\u6211\u4eec\u53ef\u4ee5\u7ee7\u7eed\u641c\u7d22\u90e8\u5206\u3002<\/p>\n","protected":false},"excerpt":{"rendered":"<p>17 FILTERING 17 \u8fc7\u6ee4 In this chapter, we are going to cover filtering in ASP.NET Core Web API. We\u2019ll learn what filtering is, how it\u2019s different from searching, and how to implement it in a real-world project.\u200c \u5728\u672c\u7ae0\u4e2d\uff0c\u6211\u4eec\u5c06\u4ecb\u7ecd ASP.NET Core Web API \u4e2d\u7684\u7b5b\u9009\u3002\u6211\u4eec\u5c06\u4e86\u89e3\u4ec0\u4e48\u662f\u7b5b\u9009\uff0c\u5b83\u4e0e\u641c\u7d22\u6709\u4f55\u4e0d\u540c\uff0c\u4ee5\u53ca\u5982\u4f55\u5728\u5b9e\u9645\u9879\u76ee\u4e2d\u5b9e\u73b0\u5b83\u3002 While not critical as paging, filtering is still an important part of [&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-1132","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1132","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=1132"}],"version-history":[{"count":0,"href":"https:\/\/diji.net\/index.php?rest_route=\/wp\/v2\/posts\/1132\/revisions"}],"wp:attachment":[{"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=1132"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=1132"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/diji.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=1132"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}