Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The best way to modify a WebAPI OData QueryOptions.Filter

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?

like image 826
Jason Steele Avatar asked May 08 '13 12:05

Jason Steele


1 Answers

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);
like image 192
ericc Avatar answered Oct 22 '22 04:10

ericc