Passing Parameters in a Breeze Query

In my course Building Data-Centric Single Page Applications with Breeze, I cover Breeze’s ability to pass parameters when you execute a query. The basics of that are that there is a withParameters method on the EntityQuery object that lets you pass in a javascript object with key/value pair properties that will be turned into a query string parameter by breeze and passed along to the service. You need to make sure the resource you are querying has a corresponding method on the target controller that takes those parameters.

So lets say I executed a query on the client side like this:
var em = new breeze.EntityManager(“breeze/Zza”);
var queryWithParams = breeze.EntityQuery
.from(“ProductsWithSimpleParams”)
.withParameters({ name: “foo”, description: “bar”});
em.executeQuery(queryWithParams, function (data) { products(data.results); });
Based on the set up of the EntityManager, I am targeting a BreezeController called ZzaController. Whatever I pass to the .from call is interpreted as the resource that I want to query. Breeze will append that to the URL path requested, and based on the default action-based routing of Breeze, that means I will need to have a method called ProductsWithSimpleParams that responds to HTTP GET requests on my controller. Because I used the .withParameters call, passing a JavaScript object literal with properties name and description on it, Breeze will add these as query string parameters with their values on the request. When I execute the query, I get an HTTP GET request with a URL of:

GET /breeze/Zza/ProductsWithSimpleParams?name=foo&description=bar

To handle this call, I will need a method on the controller that looks like this:
[HttpGet]
public IQueryable ProductsWithSimpleParams(string name, string description)
{
// Do something with params…
return _ContextProvider.Context.Products;
}
A combination of the action-based routing of Breeze and the model binding system of ASP.NET Web API kicks in here and the method is selected by name based on the URL, and the parameters are matched against the query string parameters by name and case, so the call gets dispatched into this method with the parameters populated. Couldn’t be simpler.

However, what might not be so obvious is what if you need to pass more complex parameters to your queries from the client side, including complex types and/or collections of parameters.

Lets take the case of complex types as discrete parameters first. Say that on the server side, you want to take in a single complex object as a parameter:

public class ComplexParam
{
public string name { get; set; }
public string description { get; set; }
}
[HttpGet]
public IQueryable ProductsWithSingleComplexParam(ComplexParam param)
{
// Do something with params…
return _ContextProvider.Context.Products;
}
With model binding in ASP.NET this won’t work by default for a query method because it expects complex types to be in the HTTP body, and a GET request can’t have a body. So what can we do? Well, we know Breeze is going to put parameters in the query string anyway, so we just need to tell ASP.NET model binding where to look. If we use a [FromUri] attribute on the parameter, it will look to what it finds in the query string to try to come up with a ComplexParam object to pass in:

public IQueryable ProductsWithSingleComplexParam([FromUri] ComplexParam param)
Basically once you do that, ASP.NET will create a new ComplexParam object and try to populate any of its properties from query string parameters. So the same client side code that we were using before (other than the resource name changing to “ProductsWithSingleComplexParam”) that put name and description in the query string will now be use to populate the name and description properties of the ComplexParam object instead.

Note that there is case sensitive matching going on here, so that is why I non-conventionally defined my property names camelCase instead of the norm in .NET of PascalCased. You can work around this by using the Breeze camelCase convention to get that conversion to happen automatically on the client side, or you can use the JSON.NET convention conversion to do it on the server side. That is a separate subject, so I will just make sure my client and server are using the same casing for the samples I show here.

What if you want a signature that has multiple complex types in it, such as:

[HttpGet]
public IQueryable ProductsWithTwoComplexParams([FromUri] ComplexParam param1,
[FromUri] ComplexParam2 param2)
{
// Do something with params…
return _ContextProvider.Context.Products;
}
where ComplexParam2 looks like:

public class ComplexParam2
{
public int age { get; set; }
public string comments { get; set; }
}
In this case if we change our query on the client side to this:

var queryWithParams = breeze.EntityQuery
.from(“ProductsWithTwoComplexParams”)
.withParameters({ name: “foo”, description: “bar”, age: 42, comments: “hello” });
em.executeQuery(queryWithParams, function (data) { products(data.results); });
Now model binding will create the two separate objects, ComplexParam and ComplexParam2 and will pull values for their properties out of the individual query string values from the client side.

What if you want to treat them as separate objects on the client side as well, to keep more of a 1:1 object model? Breeze withParameters still wants a single root JavaScript object to be passed to it, but that object can have nested objects. if our parameters on the server side are named param1 and param2 as shown above, then we just need to make sure the structure of the root object we pass matches that parameter naming:

var queryWithTwoComplexParamsAsObjects = breeze.EntityQuery
.from(“ProductsWithTwoComplexParams”)
.withParameters({ param1: { name: “foo”, description: “bar” }, param2: { age: 42, comments: “hello” } });
em.executeQuery(queryWithTwoComplexParamsAsObjects, function (data) { products(data.results); });
Here I am hard coding the objects for demonstration purposes, but those param1 and param2 objects could be variables that point to an object that was originally returned from a query that brought back ComplexParam and ComplexParam2 objects.

Finally, what if you want to be able to pass a collection of objects as parameters to the server side? Well, again you are constrained by the fact that Breeze wants a single root object that you pass to withParameters, but that object can certainly contain a property that is an array of objects. To understand what to do here, we just need to drop back to the fundamentals of what is going to happen on each end of the wire. Breeze withParameters is going to take the JavaScript object you pass it and turn it into a JSON representation and put it into the query string with each root property of the JavaScript object as a separate parameter. On the server side, you need to use the [FromUri] to get that complex object representation deserialized into a server object that you can use in your service method.

So say we had a structure to the collection (array) of JavaScript complex objects that we wanted to pass like this:

var params = [{ propName: “Name”, value: “foo” }, { propName: “Description”, value: “bar” }];
var queryWithCollectionOfParams = breeze.EntityQuery.from(“ProductsWithCollectionOfParams”).withParameters({ values: params });
em.executeQuery(queryWithCollectionOfParams, function (data) { products(data.results); });
What ends up on the wire is a GET request like this:

GET /breeze/Zza/ProductsWithCollectionOfParams?values%5B0%5D%5BpropName%5D=Name&values%5B0%5D%5Bvalue%5D=foo&values%5B1%5D%5BpropName%5D=Description&values%5B1%5D%5Bvalue%5D=bar

Now that is a little hard to read with the URL encoding, so lets look at the decoded query string:

values[0][propName]=Name&values[0][value]=foo&values[1][propName]=Description&values[1][value]=bar

We can see that because we passed a root object with with a property named “values” that points to the array of JavaScript objects, Breeze broke that down and turned each array entry and each property for each entry into its own query string parameter. So what do we need to have that read back in to some strongly typed objects on the server side? First we need a model object corresponding to each entry object:

public class CollectionItem
{
public string propName { get; set; }
public string value { get; set; }
}

Then we need an array argument named values for our service method of those CollectionItems:

[HttpGet]
public IQueryable ProductsWithCollectionOfParams([FromUri] CollectionItem[] values)
{
// Do something with params….
return _ContextProvider.Context.Products;
}
Now we are good to go even with passing a collection of complex objects to the server side.

If you need to go beyond that, my first instinct is to recognize that not every service call from your JavaScript client that happens to be using Breeze MUST use Breeze for every data related call. Ultimately your BreezeController is a Web API controller with an action-based route. You can call any method in your controller (or any other controller for that matter) with simple AJAX calls through JQuery. So if you have highly complex parameter passing and return type requirements that Breeze seems to be getting in the way of accomplishing, just get it out of the way and make those calls as normal AJAX calls yourself.

Hope you find this helpful for understanding some of the options in passing more than simple parameters to a query method through Breeze.