title: OData V4 modify $filter on server side
body: <p>I would like to be able to modify the filter inside the controller and then return the data based on the altered filter.  </p>
  
  <p>So for I have an ODataQueryOptions parameter on the server side that I can use to look at the FilterQueryOption.  </p>
  
  <p>Let's assume the filter is something like this "$filter=ID eq -1" but on the server side if I see "-1" for an ID this tells me that the user wants to select all records.  </p>
  
  <p>I tried to change the "$filter=ID eq -1" to "$filter=ID ne -1" which would give me all by setting the Filter.RawValue but this is read only.<br>
  I tried to create a new FilterQueryOption but this requires a ODataQueryContext and a ODataQueryOptionParser which I can't figure out how to create.</p>
  
  <p>I then tried to set the Filter = Null and then us the ApplyTo which seems to work when I set a break point in the controller and check this on the immediate window but once it leaves the GET method on the controller then it "reverts" back to what was passed in the URL.  </p>
  
  <p>This article talks about doing something very similar "<a href="https://stackoverflow.com/questions/16440644/the-best-way-to-modify-a-webapi-odata-queryoptions-filter">The best way to modify a WebAPI OData QueryOptions.Filter</a>" but once it leaves the controller GET method then it reverts back to the URL query filter.  </p>
  
  <p><strong>UPDATE WITH SAMPLE CODE</strong>  </p>
  
  <pre><code>[EnableQuery]
  [HttpGet]
  public IQueryable&lt;Product&gt; GetProducts(ODataQueryOptions&lt;Product&gt; queryOptions)
  {
      if (queryOptions.Filter != null)
      {
          var url = queryOptions.Request.RequestUri.AbsoluteUri;
          string filter = queryOptions.Filter.RawValue;
  
          url = url.Replace("$filter=ID%20eq%201", "$filter=ID%20eq%202");
          var req = new HttpRequestMessage(HttpMethod.Get, url);
  
          queryOptions = new ODataQueryOptions&lt;Product&gt;(queryOptions.Context, req);
      }
  
      IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
      return query as IQueryable&lt;Product&gt;;
  }
  </code></pre>
  
  <p>Running this code will not return any product this is because the original query in the URL wanted product 1 and I swapped the ID filter of product 1 with product 2.<br>
  Now if I run SQL Profiler, I can see that it added something like "Select * from Product WHERE ID = 1 AND ID = 2".  </p>
  
  <p><strong>BUT</strong> if I try the same thing by replacing the $top then it works fine.  </p>
  
  <pre><code>[EnableQuery]
  [HttpGet]
  public IQueryable&lt;Product&gt; GetProducts(ODataQueryOptions&lt;Product&gt; queryOptions)
  {
      if (queryOptions.Top != null)
      {
          var url = queryOptions.Request.RequestUri.AbsoluteUri;
          string filter = queryOptions.Top.RawValue;
  
          url = url.Replace("$top=2", "$top=1");
          var req = new HttpRequestMessage(HttpMethod.Get, url);
  
          queryOptions = new ODataQueryOptions&lt;Product&gt;(queryOptions.Context, req);
      }
  
      IQueryable query = queryOptions.ApplyTo(db.Products.AsQueryable());
      return query as IQueryable&lt;Product&gt;;
  }
  </code></pre>
  
  <p><strong>END RESULT</strong><br>
  With Microsoft's help. Here is the final output that supports filter, count, and paging.  </p>
  
  <pre class="lang-cs prettyprint-override"><code>using System.Net.Http;
  using System.Web.OData;
  using System.Web.OData.Extensions;
  using System.Web.OData.Query;
  
  /// &lt;summary&gt;
  /// Used to create custom filters, selects, groupings, ordering, etc...
  /// &lt;/summary&gt;
  public class CustomEnableQueryAttribute : EnableQueryAttribute
  {
      public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
      {
          IQueryable result = default(IQueryable);
  
          // get the original request before the alterations
          HttpRequestMessage originalRequest = queryOptions.Request;
  
          // get the original URL before the alterations
          string url = originalRequest.RequestUri.AbsoluteUri;
  
          // rebuild the URL if it contains a specific filter for "ID = 0" to select all records
          if (queryOptions.Filter != null &amp;&amp; url.Contains("$filter=ID%20eq%200")) 
          {
              // apply the new filter
              url = url.Replace("$filter=ID%20eq%200", "$filter=ID%20ne%200");
  
              // build a new request for the filter
              HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url);
  
              // reset the query options with the new request
              queryOptions = new ODataQueryOptions(queryOptions.Context, req);
          }
  
          // set a top filter if one was not supplied
          if (queryOptions.Top == null) 
          {
              // apply the query options with the new top filter
              result = queryOptions.ApplyTo(queryable, new ODataQuerySettings { PageSize = 100 });
          } 
          else 
          {
              // apply any pending information that was not previously applied
              result = queryOptions.ApplyTo(queryable);
          }
  
          // add the NextLink if one exists
          if (queryOptions.Request.ODataProperties().NextLink != null) 
          {
              originalRequest.ODataProperties().NextLink = queryOptions.Request.ODataProperties().NextLink;
          }
          // add the TotalCount if one exists
          if (queryOptions.Request.ODataProperties().TotalCount != null) 
          {
              originalRequest.ODataProperties().TotalCount = queryOptions.Request.ODataProperties().TotalCount;
          }
  
          // return all results
          return result;
      }
  }
  </code></pre>