Support for All()/Any() in WCF Data Services 5.0

By Mirek on (tags: Data Services, OData, WCF, categories: code)

9th of April 2012 the new version of WCF Data Services was released with number 5.0 and support for V3 OData protocol. You can download this version from here.

You can read more about all new features added to this release of Data Services on WCF Data Services Team Blog

Most important features are

  • Vocabularies
  • Actions
  • Spatial types
  • Named streams
  • Patch support
  • Any() All() support

Well there is still missing support for enum types which has at the moment 500 votes on the WCF Data Services wish list.

I started testing new version of Data services with support for Any() and All() linq methods and I found out that quite interesting fact. Any and All methods are supported but only when used in filter syntax for collection properties. When we want to use these method on main entity types we got Not supported exception. Let’s see it in action.

I created two entity types Customer and Order stored in Entity Framework context AppContext

   1: public class Customer
   2: {
   3:     public int Id { get; set; }
   5:     public string Name { get; set; }
   7:     public DateTime CreatedOn { get; set; }
   9:     public ICollection<Order> Orders { get; set; }
  10: }
   1: public class Order
   2: {
   3:     public int Id { get; set; }
   5:     public decimal TotalPrice { get; set; }
   7:     public ushort Quantity { get; set; }
   9:     public Customer Customer { get; set; }
  10: }

Then I exposed the database context AppContext in Data Service

   1: public class DataService : DataService<AppContext>
   2: {
   3:     public static void InitializeService(DataServiceConfiguration config)
   4:     {
   5:         config.UseVerboseErrors = true;
   6:         config.SetEntitySetAccessRule("*", EntitySetRights.All);
   7:         config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V3;
   8:     }
   9: }

Important line is 7 where we define to use OData protocol in version 3, so the support for all mentioned features is enabled.

After adding a reference to the service and creating the service context…

   1: AppContext ctx = new AppContext(new Uri("http://localhost/DataService.svc/"));

…I was surprised that invoking both of following methods…

   1: var anyCustomer = ctx.Customers.Any();
   2: var allCustomers = ctx.Customers.All(c => c.Id > 0);

…notified me with “The method 'Any' is not supported.” and “The method 'All' is not supported.” exceptions. Since I thought that was the default usage of Any and All methods I found the example of usage of these methods on WCF Data Services Team Blog which shows how to use them in a filter expression. According to this I could do following…

   1: var anyOrders = ctx.Customers.Where(c => c.Orders.Any());
   2: var allOrders = ctx.Customers.Where(c => c.Orders.All(o => o.Id > 0));

which resulted in following queries sent to the Data Service

   1: /DataService.svc/Customers()?$filter=Orders/any()
   2: /DataService.svc/Customers()?$filter=Orders/all(o:o/Id%20gt%200)

So it seems that All() and Any() methods are supported only in filter expressions. The result of Any() and All()  methods is evaluated on data service side and then used in expression filter. It’s a pity that these methods are not supported on main resource collections, but their can be easily emulated using Count() method which is supported in WCF Data Services also in earlier versions.

We can emulate Any() method like this

   1: var anyCount = ctx.Customers.Count() > 0;

which is translated into following query

   1: /DataService.svc/Customers()/$count

and returns the count of all Customer entity in Customers collection.

We can also emulate All() method with Count() like this

   1: var allCount = ctx.Customers.Where(c => c.Id > 0).Count() > 0;

which results in query

   1: /DataService.svc/Customers()/$count?$filter=Id%20gt%200