9

I am using the OData sample project at http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/working-with-entity-relations. In the Get I want to be able to change the Filter in the QueryOptions of the EntitySetController:

public class ProductsController : EntitySetController<Product, int>
{
    ProductsContext _context = new ProductsContext();

    [Queryable(AllowedQueryOptions=AllowedQueryOptions.All)]
    public override IQueryable<Product> Get()
    {
        var products = QueryOptions.ApplyTo(_context.Products).Cast<Product>();
        return products.AsQueryable();
    }

I would like to be able to find properties that are specifically referred to. I can do this by parsing this.QueryOptions.Filter.RawValue for the property names but I cannot update the RawValue as it is read only. I can however create another instance of FilterQueryOption from the modified RawValue but I cannot assign it to this.QueryOptions.Filter as this is read only too.

I guess I could call the new filter's ApplyTo passing it _context.Products, but then I will need to separately call the ApplyTo of the other properties of QueryOptions like Skip and OrderBy. Is there a better solution than this?

Update

I tried the following:

    public override IQueryable<Product> Get()
    {
        IQueryable<Product> encryptedProducts = _context.Products;

        var filter = QueryOptions.Filter;
        if (filter != null && filter.RawValue.Contains("Name"))
        {
            var settings = new ODataQuerySettings();
            var originalFilter = filter.RawValue;
            var newFilter = ParseAndEncyptValue(originalFilter);
            filter = new FilterQueryOption(newFilter, QueryOptions.Context);
            encryptedProducts = filter.ApplyTo(encryptedProducts, settings).Cast<Product>();

            if (QueryOptions.OrderBy != null)
            {
                QueryOptions.OrderBy.ApplyTo<Product>(encryptedProducts);
            }
        }
        else
        {
            encryptedProducts = QueryOptions.ApplyTo(encryptedProducts).Cast<Product>();
        }

        var unencryptedProducts = encryptedProducts.Decrypt().ToList();

        return unencryptedProducts.AsQueryable();
    }

and it seems to be working up to a point. If I set a breakpoint I can see my products in the unencryptedProducts list, but when the method returns I don't get any items. I tried putting the [Queryable(AllowedQueryOptions=AllowedQueryOptions.All)] back on again but it had no effect. Any ideas why I am not getting an items?

Update 2

I discovered that my query was being applied twice even though I am not using the Queryable attribute. This meant that even though I had items to return the List was being queried with the unencrypted value and therefore no values were being returned.

I tried using an ODataController instead:

public class ODriversController : ODataController
{

    //[Authorize()]
    //[Queryable(AllowedQueryOptions = AllowedQueryOptions.All)]
    public IQueryable<Products> Get(ODataQueryOptions options)
    {

and this worked! Does this indicate that there is a bug in EntitySetController?

Jason Steele
  • 1,598
  • 3
  • 23
  • 41
  • Are you trying to restrict which properties the client can filter on? – Mike Wasson May 08 '13 at 15:41
  • 2
    Not an answer to your question, but, I noticed something wrong in your code. You either use ODataQueryOptions or the QueryableAttribute. ODataQueryOptions is for taking manual control over the query. QueryableAttribute is for automatically applying the query. Your code has both which means that the query will be applied twice once by ODataQueryOptions.ApplyTo and once by the QueryableAttribute. – RaghuRam Nadiminti May 08 '13 at 18:25
  • Can you explain your scenario further? Are you trying to transform the incoming $filter query and then execute it? – RaghuRam Nadiminti May 08 '13 at 18:27
  • I want to change the values in a filter to encrypted values so that the filter will work against encrypted values in the database. Not all columns are encrypted, so I need to find the relevant ones within the filter string and modify only their values. Thanks for your help and suggestions, this is brief as using s phone on a train – Jason Steele May 09 '13 at 07:12
  • @RaghuRamNadiminti Yes, I want to alter the values in the filter and then execute it. And many thanks for the tip on the QueryableAttribute. – Jason Steele May 10 '13 at 10:58
  • @MikeWasson I'm not restricting what can be filtered on. I am trying to change the values passed – Jason Steele May 10 '13 at 11:00
  • @Jason: Are you parsing the raw filter value at all? If so how? I need to parse the filter value for supporting specific OData operators and using the 'Microsoft.Data.OData.Query.SemanticAst.FilterClause' is not for the faint of heart. – Abhijeet Patel Oct 10 '13 at 01:51
  • I have a answer here: http://stackoverflow.com/questions/33660648/odata-v4-modify-filter-on-server-side/33684667#33684667 – Fan Ouyang Nov 18 '15 at 05:39

1 Answers1

4

You would probably need to regenerate ODataQueryOptions to solve your issue. Let's say if you want to modify to add $orderby, you can do this like:

string url = HttpContext.Current.Request.Url.AbsoluteUri;
url += "&$orderby=name";
var request = new HttpRequestMessage(HttpMethod.Get, url);
ODataModelBuilder modelBuilder = new ODataConventionModelBuilder();
modelBuilder.EntitySet<Product>("Product");
var options = new ODataQueryOptions<Product>(new ODataQueryContext(modelBuilder.GetEdmModel(), typeof(Product)), request);
ericc
  • 41
  • 3