Search code examples
c#.net-coreodata

.NET Core 2.1 OData v4 modify $filter on server side


I need to modify the $filter on the server-side of .NET core 2.1 API. I have looked at these:

https://entityframework.net/knowledge-base/33660648/odata-v4-modify--filter-on-server-side

OData V4 modify $filter on server side

The best way to modify a WebAPI OData QueryOptions.Filter

(and a couple of others)

They all do something along the lines of:

//Get the URL
var url = queryOptions.Request.RequestUri.AbsoluteUri;
//Then modify the URL
url = url.Replace("$filter=ID%20eq%201", "$filter=ID%20eq%202");
//Apply the URL
return queryOptions.ApplyTo(queryable);

The problem being is that it appears in .NET core there is no RequestUri. I am able to get the query multiple different ways:

var query = originalRequest.QueryString.Value;
//or a foreach loop
var filter = Request.Query;
foreach (var x in filter)
//...

However both QueryString.Value Request.Query gives me an error "Value has no setters"

Also the method ApplyTo is not in .NET Core.

What is the correct way of doing this in .NET Core? Is there some package that I am missing?


Solution

  • @Xaphann You could extend EnableQueryAttribute in the manner below:

    public class ExtendedEnableQueryAttribute : EnableQueryAttribute
    {
        public override IQueryable ApplyQuery(IQueryable queryable, ODataQueryOptions queryOptions)
        {
            // ... second check in the if statement might be overkill - abundance of caution?
            if (queryOptions.Filter != null && queryOptions.Request.Query.ContainsKey("$filter"))
            {
                var stringValuesDict = new Dictionary<string, StringValues>();
    
                foreach(var kvPair in queryOptions.Request.Query.Where(d => !d.Key.Equals("$filter")))
                {
                    // This way the new StringValues instances are owned exclusively by substitute query collection
                    var values = new List<string>();
                    foreach(var value in kvPair.Value)
                    {
                        values.Add(value);
                    }
                    stringValuesDict.Add(kvPair.Key, new StringValues(values.ToArray()));
                }
                // Substitute the $filter option
                stringValuesDict.Add("$filter", new StringValues("ID eq 202"));
                // Substitute the request query collection
                queryOptions.Request.Query = new QueryCollection(stringValuesDict);
    
                queryOptions = new ODataQueryOptions(queryOptions.Context, queryOptions.Request);
            }
    
            return base.ApplyQuery(queryable, queryOptions);
        }
    }
    

    You can then proceed to apply this extended attribute to your controller action. I tested it practically and it seemed to work for an expression like this:

    ?$filter=ID eq 201&$orderby=Name&$select=Name

    If this approach works for you, my advise would be to test more extensively and maybe also write a few tests to validate the behaviour