WCF QueryTranslator

07.01.2011 15:49Comments
In this post I will show you how to resolve a Linq query from an OData formatted url. As from the WCF WebHttp release in codeplex, the logic to perform this was moved from the WCF DataServices dlls into a stand alone dll called "Microsoft.QueryComposition". This dll required for this sample can be found in the libs folder of the WCF WebHttp source code. First we are going to take a look at a static function taken from the source code of the UrlQueryComposer from the System.ServiceModel namespace:
        public virtual IEnumerable ComposeQuery(IEnumerable rootedQuery, string url)
            Debug.Assert(rootedQuery != null, "queryableRoot should not be null");
            Debug.Assert(!String.IsNullOrEmpty(url), "url should not be null or empty");

            // cast the queryableRoot to an IQueryable type to further compose the query
            IQueryable queryable = rootedQuery as IQueryable;
            if (queryable == null)
                queryable = rootedQuery.AsQueryable();

            if (queryable != null)
                IQueryable finalQueryable = QueryTranslator.Translate(queryable, url);
                return finalQueryable;

            return rootedQuery;
So basically, we can pass any IEnumerable, and an OData formatted url, and it will return another IEnumerable that will filter the original one. We can easily use this from any type of project, including a console app. Add a reference to the Microsoft.QueryComposition dll to your project, and then try something like this:
            var list = new List<Product> {
                new Product { ID = 1, Name = "Tenis Ball", Category = new  Category { ID = 1, Name = "Sports"} },
                new Product { ID = 2, Name = "Electric Guitar", Category = new Category { ID = 2, Name = "Music"} } };

            //Filter your IEnumerable using OData syntax. (ie x => x.ID == 2).
            var url = "http://localhost/service/products/?$filter=ID%20eq%202";

            foreach (var i in ComposeQuery(list, url))
                Console.WriteLine("Name: {0}", ((Product)i).Name);

I think this is very cool, and can be very useful for cases in which you want to allow the user to define a filter of some sort, and you want an easy way to store the filter serialized in some repository. Could be a "save search" feature in a search screen, or a security filter to  allow or deny access, etc.

comments powered by Disqus