Custom Query Options on the WCF Data Services Client


So, the OData Protocol talks about custom query options, but how exactly do you use them from the WCF Data Services client?

If you’ve used service operations at all, you already know the answer: use the AddQueryOption method. There is a subtle difference though, and things are actually easier for custom query options.

For example, here is a console program that will set up a service and query for all customers that have a "2" in their name.

First, we’ll set up the classes to handle data and include a service operation to filter the customers (normally you’d just use a $filter option, but I’m keeping the operation simple).

public class Customer {
  public int ID { get; set; }
  public string Name { get; set; }
}

public class MyContext {
  private static Customer[] customers = Enumerable.Range(1, 20)
    .Select(i => new Customer() { ID = i, Name = "Customer #" + i })
    .ToArray();
  public IQueryable<Customer> Customers {
    get { return customers.AsQueryable(); }
  }
}

public class MyService : DataService<MyContext> {
  public static void InitializeService(DataServiceConfiguration c) {
    c.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
    c.SetServiceOperationAccessRule("*", ServiceOperationRights.AllRead);
  }

  [WebGet]
  public IQueryable<Customer> CustomOps(string name) {
    return this.CurrentDataSource.Customers
      .Where(c => c.Name.Contains(name));
  }
}

Next, we’ll write the code that drives everything. Note how we need to escape the "2" with quotes so it gets correctly interpreted as a string, as specified in the Service Operations Parameters section.

public class Scratch {

public static void Main() {
  Uri uri = new Uri("http://localhost:3000/");
  DataServiceHost host = new DataServiceHost(typeof(MyService), new Uri[] {uri}); 
  host.Open();

  DataServiceContext client = new DataServiceContext(uri);
  var q = client.CreateQuery<Customer>("CustomOps")
    .AddQueryOption("name", "’2’");
  Console.WriteLine("Querying for " + q.ToString());
  foreach (var c in q) {
    Console.WriteLine("Customer: " + c.Name);
  }

  Console.WriteLine("done!"); 
  host.Close(); 
  Console.ReadKey();
}

}

If you run this code, you’ll see this output:

Querying for http://localhost:3000/CustomOps()?name=’2′
Customer: Customer #2
Customer: Customer #12
Customer: Customer #20
done!

Now, custom query options are exactly what the AddQueryOption method maps to, so there is no escaping to be done. On the server, however, the WCF Data Services runtime won’t process the query option, which means that either your code or some other layer will need to process it "out-of-band".

Here I’m modifying the program a little bit, just to show you how you can get at the query parameter, and to show that there is no escaping or any kind of processing done to it.

[WebGet]
public IQueryable<Customer> CustomOps(string name) {
  // This could be done at a completely different layer,
  // for example to handle session, caching, auth information, etc.
  var op = WebOperationContext.Current;
  var parameters =
    (op == null) ? null :
    (op.IncomingRequest.UriTemplateMatch == null) ? null :
    op.IncomingRequest.UriTemplateMatch.QueryParameters;
  if (parameters["worldcup"] != null) {
    return new Customer[] { new Customer() {
      ID = 1,
      Name = "The world cup is " + parameters["worldcup"]
    }}.AsQueryable();
  }

  return this.CurrentDataSource.Customers
    .Where(c => c.Name.Contains(name));
}

public static void Main() {

  DataServiceContext client = new DataServiceContext(uri);
  var q = client.CreateQuery<Customer>("CustomOps")
    .AddQueryOption("name", "’2’");
  Console.WriteLine("Querying for " + q.ToString());
  foreach (var c in q) {
    Console.WriteLine("Customer: " + c.Name);
  }

  // Now for something quite different:
  q = client.CreateQuery<Customer>("CustomOps")
    .AddQueryOption("name", "’3’")
    .AddQueryOption("worldcup", "awesome");
  Console.WriteLine("Querying for " + q.ToString());
  foreach (var c in q) {
    Console.WriteLine("Customer: " + c.Name);
  }

  Console.WriteLine("done!");

Now when you run this, the service operation will query ‘out-of-band’ directly to the WCF operation context for the URL, intercept the request, and you will get the following.

Querying for http://localhost:3000/CustomOps()?name=’2′
Customer: Customer #2
Customer: Customer #12
Customer: Customer #20
Querying for http://localhost:3000/CustomOps()?name=’3’&worldcup=awesome
Customer: The world cup is awesome
done!

Enjoy!

Comments (0)

Skip to main content