4

I'm trying out BreezeJS. There is a requirement that I can use .expand in the client side code, but based on the role of the user, the server side will not return all the records for the .expand requested type. I tried to create a custom BreezeQueryable attribute and override a method to completely filter out the extra data first just to try. But it threw an exception.

I don't see any entry point where I can do that on the server side.

Please guide me in the right direction, or let me know if that's not possible. I only have access to generic IQueryable, how do I perform queries on this?

Here's some sample code:

Server:

[BreezeController]
[EnableCors("*", "*", "*")]
public class MyDataController : ApiController
{
    readonly EFContextProvider<MyDbContext> _contextProvider;

    public MyDataController()
    {
        _contextProvider = new EFContextProvider<MyDbContext>();
        _contextProvider.Context.Configuration.ProxyCreationEnabled = false;
        _contextProvider.Context.Configuration.LazyLoadingEnabled = false;
    }

    // GET api/<controller>
    //Trying to use a custom attribute to filter data here
    [CustomBreezeQueryable(AllowedQueryOptions = AllowedQueryOptions.All)]
    [HttpGet]
    public IQueryable<MyData> GetAllData()
    {
        var data = _contextProvider.Context.MyData;
        return data;
    }
}

public class CustomBreezeQueryableAttribute : BreezeQueryableAttribute
{
    public override IQueryable ApplyQuery(IQueryable queryable, 
                       ODataQueryOptions queryOptions)
    {
        var data = base.ApplyQuery(queryable, queryOptions);
        //trying to filter out MyDataHistory for MyData for testing, 
        //it throws exception
        //data = data.OfType<MyDataHistory>(); 
        return data;
    }
}

Client side:

breeze.EntityQuery.from("GetAllData").expand('MyDataHistory')
                   .using(this.manager)
                   .execute()
                   .then((data) => {               
                        console.log(data.results[0]);
                        def.resolve(data.results);
                    });

This is the exception I get when using OfType, and I would like to filter, not use that anyways.

{"DbOfTypeExpression requires an expression argument with a polymorphic result type that is compatible with the type argument."}
Ömer Erden
  • 7,680
  • 5
  • 36
  • 45
AD.Net
  • 13,352
  • 2
  • 28
  • 47

1 Answers1

1

Not entirely sure I understand your issue, but you can perform the 'expand' on the server side via an EF 'Include' like that shown below:

 [HttpGet]
 public IQueryable<Customer> CustomersAndOrders() {
   var custs = ContextProvider.Context.Customers.Include("Orders");
   return custs;
 }

Which will return 'Customer' objects each with its 'Orders' property fully populated and loading into the Breeze cache.

If you want to actually suppress 'expansion' on the server for a given resource name you can use the the [BreezeQueryableAttribute]. Note that AllowedQueryOptions.Expand is omitted from the list of supported operations in the example below.

[HttpGet]
[BreezeQueryable(AllowedQueryOptions = AllowedQueryOptions.Filter | AllowedQueryOptions.Skip | AllowedQueryOptions.Top | AllowedQueryOptions.OrderBy)]
public IQueryable<Employee> Employees() {
  return ContextProvider.Context.Employees;
}

The [BreezeQueryableAttribute] supports the same parameters as Microsoft's [QueryableAttribute] described here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options

The other option if you want to actually restrict/filter what gets expanded can only be done by performing the filtering expansion yourself, possibly with the help of parameters passed into the method via 'withParameters' (This is because EF does not yet support filtering on 'Includes'. I have not tested the example below but the general idea should work.

[HttpGet]
public IQueryable<Employee> Employees(double minWeight) {
  var emps = ContextProvider.Context.Employees.Include("Orders").ToList();
  // remove selected orders from what gets returned to the client.
  emps.ForEach(emp => {
    var ordersToRemove = emp.Orders.Where(o => o.Freight < minWeight).ToList();
    ordersToRemove.ForEach(o => emp.Orders.Remove(o));
  });
  return emps;
}
Jay Traband
  • 17,053
  • 1
  • 23
  • 44
  • Yes, I know that. My problem is I want to control (in the server) the returned entities when .expand is used on the client side – AD.Net Mar 18 '14 at 23:17
  • Hi there, i'm also very interested in how to get more control over the .expand feature on serverside (as it looks right now my controllers just get bypassed so the business logic gets ignored...) – fops Mar 20 '14 at 13:25
  • You can suppress the ability of the server to do any 'expands' for a given resource name using the [BreezeQueryableAttribute] . It supports the same parameters as Microsoft's [QueryableAttribute] described here: http://www.asp.net/web-api/overview/odata-support-in-aspnet-web-api/supporting-odata-query-options Simply omit the AllowedQueryOptions.Expand from your list of supported operations. – Jay Traband Mar 20 '14 at 17:30