11
votes

I have an ASP.NET Web API app using Entity Framework and Odata.

I would like to modify the results of a query when using a GET... currently, within the controller, you effectively just pass the EntityFramework data entities back to the Odata handlers...

[EnableQuery]
public IQueryable<myEntity> GetLineItem()
{
    return db.myEntities;
}

It's simple to prepend whatever query Odata passes into this by simply returning a subset

return db.myEntity.Where(myEntity => myEntity.Name == "Bob")

Odata will add whatever is in the $filter querystring parameter to the expression passed in here and you get the subset of these results.

However, I would like to iterate over the results once the query executes and the SQL results are parsed into entity objects.

I have tried creating a wrapper that implements the IQueryable interface and hooking into the GetEnumerator methods and the same for the IProvider and hooking into the execute method. Odata doesn't seem to be using either one of these.

Is there a way to do this?

3

3 Answers

10
votes

You can do that by implementing a filter. A filter is simply an atribute that you can apply to an action (controller's method), a controller (controller's class) or register for the whole application.

This filter is applied at some step on the pipeline: there's a pipeline that goes from the incoming HTTP request to the controllers action, and back to the response, and you can include a filter in different places of this pipeline, to modify the data flowing through this pipeline.

You particularly need to inherit ActionFilterAttribute, and do your post processing on the OnActionExecuted which is executed after the controller's action has been executed.

Docs:

The last class is the type of the parameter of the OnActuonExecuted method, and contains the response that you can modify.

The first part of this article contains an explanation for action filter: WEB API 2 USING ACTIONFILTERATTRIBUTE, OVERRIDEACTIONFILTERSATTRIBUTE AND IOC INJECTION

1
votes

You don't have to return IQuerable from controller when working with OData. Check "Invoking Query Options Directly" section at https://docs.microsoft.com/en-us/aspnet/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options For your case it will looks like:

public HttpResponseMessage Get(ODataQueryOptions<myEntity> opts)
{
    var settings = new ODataValidationSettings();

    opts.Validate(settings);

    var intermediateResult = opts.ApplyTo(db.myEntities).ToArray();

    var result = //change intermediateResult as you wish.

    return result;
}
1
votes

Building on Ihar's answer, here's a complete example of applying the query options and then modifying the resulting collection.

public async Task<IHttpActionResult> Get(ODataQueryOptions<MyModel> options)
{
    var query = _service.Query();

    var result = ((IQueryable<MyModel>)options.ApplyTo(query, new ODataQuerySettings()))
                                              .ToArray();

    foreach (var model in result)
    {
        model.SomeNonQueriedValue = "Something else";
    }

    return Ok(result);
}