Consuming a Page in NAV 2013 Using C# and ODATA Part 2: Filtering
One of the cool new features in NAV 2013 is the ability to consume a Page via ODATA (in addition to the classic SOAP method of Page consumption). I did a write-up on how that should work (at http://www.archerpoint.com/blog/Posts/consuming-nav-2013-page-using-c-and-odata), but there was a major omission from that tutorial: filtering.
The truth is, I didn’t know how to do the filtering, and I was running short on time when I wrote the blog in the first place, so I just put the basic entry together and then went on to some other things I had to do. But the filtering question haunted me. I spent nights wandering the empty halls of stately Hunt Manor, mumbling “ODATA . . . filtering . . . pages”.
Thankfully, I got some time in my schedule for research and I figured it all out.
As with my first tutorial, you’ll need to publish the Customer Page and make sure that your NAV server is set up to publish ODATA services. The instructions here use the default ports and settings for NAV.
Just like in my first tutorial, start by creating a new C# console application in Visual Studio, and add a Service Reference to the Customer Card. Add a reference to the System.Data.Services.Client object to your console application so that you’ve got the ODATA consuming client object.
As it turns out, the secret to filtering was to use the AddQueryOption method to add my filtering criteria to the C# query object. I figured this out by looking at this NAV 2013 ODATA Filtering URI article (http://msdn.microsoft.com/en-us/library/hh169248%28v=nav.70%29.aspx) and the documentation for the DataServiceQuery.AddQueryOption method (http://msdn.microsoft.com/en-us/library/cc646860.aspx).
Here’s all the code to make it work. I’ve set this up so that you can get a list of customers filtered by city, filtered by location, or filtered by city or location.
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Data.Services.Client; namespace NAV2013_ODATA_Experiments_02 { class Program { static void Main(string[] args) { string theInput = ""; string instructions = "Press ENTER to start testing, or EXIT to quit."; Console.WriteLine(instructions); theInput = Console.ReadLine(); while (theInput.ToUpper() != "EXIT") { string companyName = "CRONUS%20USA,%20Inc."; string serviceUri = string.Format("http://[yourserver]:7048/DynamicsNAV70/OData/Company('{0}')", companyName); DataServiceContext context = new DataServiceContext(new Uri(serviceUri)); DLP_Customer.NAV theNav = new DLP_Customer.NAV(new Uri(serviceUri)); theNav.Credentials = System.Net.CredentialCache.DefaultNetworkCredentials; // logged-in user's credentials // Note that the filtering syntax here is ODATA filtering syntax, not NAV filtering syntax. Console.WriteLine("Enter a city filter."); string cityFilter = Console.ReadLine(); Console.WriteLine("Enter a location code filter."); string locationFilter = Console.ReadLine(); string filterValue = ""; if (cityFilter != ""&& locationFilter != "") { filterValue = string.Format("City eq '{0}' or Location_Code eq '{1}'", cityFilter, locationFilter); } else { if (cityFilter != "") { filterValue = string.Format("City eq '{0}'",cityFilter); } if (locationFilter != "") { filterValue = string.Format("Location_Code eq '{0}'",locationFilter); } } DataServiceQuery<DLP_Customer.DLP_Customer> q; if (filterValue == "") { q = theNav.DLP_Customer; } else { q = theNav.DLP_Customer.AddQueryOption("$filter", filterValue); } List<DLP_Customer.DLP_Customer> custList = q.Execute().ToList(); foreach (DLP_Customer.DLP_Customer cust in custList) { Console.WriteLine(string.Format("{0} {1} {2} {3}", cust.No, cust.Name, cust.City, cust.Location_Code)); } Console.WriteLine("Success!"); Console.WriteLine(instructions); theInput = Console.ReadLine(); } } } }
I build the service URI by using string.format and a URI-encoded company name. (In case you’re not a C# developer, string.format is used to insert a string value into a string in place of a bracketed number; it’s similar to the STRSUBSTNO function in NAV.) This lets me specify the company to use for my query instead of just accepting the default.
The filtering syntax for accessing pages via ODATA is ODATA filtering syntax, not NAV filtering syntax. If you’re a NAV developer, the bad news is that you have to learn new filtering syntax, but the good news is that you can do a logical OR query between fields. (This is something I’ve always wanted to do in NAV natively but have never been able to do.) If you wanted to do filtering on City and Location Code, you’d just use “City eq '{0}' and Location_Code eq '{1}'” in place of what’s already there.
The theNav.DLP_Customer.AddQueryOption("$filter", filterValue) statement is how we get the filter value into our query. One of the things I discovered while I was doing research for this is that you can have multiple .AddQueryOption calls on the same statement, so you could do something like theNav.DLP_Customer.AddQueryOption("$filter", filterValue).AddQueryOption(“$orderby”, “[sort field] desc”); if you wanted to have a non-standard sorting of your data. (You could also put those on multiple lines if you wanted the code to be a little easier to read, like MSDN example for AddQueryOption referenced above does.)
If you’d like to do filtering on non-string data types, the “Using Filter Expressions in OData URIs” article found at http://msdn.microsoft.com/en-us/library/hh169248%28v=nav.70%29.aspx tells you how at the very end.
Happy coding!