3

I want to use some external server-side logic to modify properties on the results of a query. To do this, I'll need to apply the Breeze query options, modify the result set, and return it.

I know essentially how I can apply OdataQueryOptions to my query, but I don't want to miss out on all the things that BreezeJS does that Web Api's OData doesn't. For example, I want to keep inlineCount.

How can I do this? Is there some way to hook into Breeze's query modifying code?

In case it matters, I'm using Entity Framework 6 and Web Api 2.

Eric
  • 5,842
  • 7
  • 42
  • 71

2 Answers2

4

Ok, I'm not sure if there's a better way (because this seems like a lot of work for what seems like it would be a common use case), but here's how I solved this.

I inherited from the QueryHelper class to modify the PostExecuteQuery method to execute a delegate.

public class ExtendedQueryHelper : QueryHelper
{
    public ExtendedQueryHelperOptions Options { get; set; }

    public ExtendedQueryHelper(ODataQuerySettings querySettings) : base(querySettings)
    {}

    public override IEnumerable PostExecuteQuery(IEnumerable queryResult)
    {
        if (Options != null && Options.PostExecuteQueryHandler != null)
        {
            return Options.PostExecuteQueryHandler(queryResult);
        }

        return base.PostExecuteQuery(queryResult);
    }
}

The delegate is defined in a class called ExtendedQueryHelperOptions

public class ExtendedQueryHelperOptions
{
    private const string EXTENDED_QUERY_HELPER_OPTIONS_KEY = "EXTENDED_QUERY_HELPER_OPTIONS_KEY";
    public delegate IEnumerable PostExecuteQueryDelegate(IEnumerable queryResult);

    public PostExecuteQueryDelegate PostExecuteQueryHandler { get; set; }

    public void InjectIntoRequest(HttpRequestMessage request)
    {
        request.Properties.Add(EXTENDED_QUERY_HELPER_OPTIONS_KEY, this);
    }

    public static ExtendedQueryHelperOptions GetFromRequest(HttpRequestMessage request)
    {
        object options;
        request.Properties.TryGetValue(EXTENDED_QUERY_HELPER_OPTIONS_KEY, out options);

        return (ExtendedQueryHelperOptions)options;
    }
}

In order to set these options, I had to inherit from BreezeQueryableAttribute and inject these options when the QueryHelper is being created:

public class ExtendedBreezeQueryableAttribute : BreezeQueryableAttribute
{
    protected HttpRequestMessage Request { get; set; }

    public override void OnActionExecuting(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        Request = actionContext.Request;
        base.OnActionExecuting(actionContext);
    }

    protected override QueryHelper NewQueryHelper()
    {
        var queryHelper = new ExtendedQueryHelper(GetODataQuerySettings());
        queryHelper.Options = ExtendedQueryHelperOptions.GetFromRequest(Request);

        return queryHelper;
    }
}

Now I can inject code to be run on the filtered results like this:

[BreezeController]
public class BreezeController : BaseController
{
    //...

    [HttpGet]
    [ExtendedBreezeQueryable]
    public IQueryable<Foo> Foos()
    {
        var options = new ExtendedQueryHelperOptions
        {
            PostExecuteQueryHandler = delegate(IEnumerable results) {
                // This code will be run after the querying has been
                // applied by Breeze
                var foos = results.Cast<Foo>().ToList();

                foreach (var foo in foos)
                {
                    foo.ComputedProperty = ComputeSomething();
                }

                return foos;
            }
        };

        // Inject these options into the Request, so the ExtendedBreezeQueryableAttribute
        // can get to them later
        options.InjectIntoRequest(Request);
        return Db.Foos;
    }
}
Eric
  • 5,842
  • 7
  • 42
  • 71
  • I'd be very interested in hearing from Ward or Jay if I'm *Doing It Wrong* or if there could be any problems associated with this. – Eric May 22 '14 at 14:56
0

I looked at the answer here and struggled with it, so came up with something simpler (but possibly far inferior).

Assuming you want to modify the entities which Breeze's query returns, then put a .ToList() on the end of the query, modify the list items, and return them with 'list.AsQueryable()' Like this:

public IQueryable<CameraMetaData> CameraMetaDatas()
{

    var query = ContextProvider.Context.Cameras;
    var list = query.ToList();

    foreach(var l in  list)
    {
        //modify values
    }
    return list.AsQueryable();
}
Joel
  • 516
  • 4
  • 7
  • 1
    That should work, but the problem with it is that it queries all records in the table and pulls them into memory. The query filters are applied on the Queryable list you are returning. That's fine if you don't have many rows in your table. In my case, I can't afford to bring the whole table in, and need the queries to be applied to trim down the result set. – Eric Jan 15 '15 at 21:56