WCF RIA Services – Dynamically create a criteria on the client-side

Note: This post is based on the WCF RIA Services VS 2008 PDC Beta and VS 2010 Beta 2 preview, changes can be done before the framework hits RTM.

I have seen questions about how to dynamically create search criteria when using WCF RIA Services and LINQ. So this post is about how you can dynamically create a criteria as a string on the client-side and pass it down to the server for execution.

There is a simple way to create a criteria on the client-side by using WCF RIA Services and LINQ and pass it down to the server for execution, here is an example:

var dx = new CustomerDomainService();

EntityQuery<Customer> query = from customer in dx.GetCustomersQuery()
                              where customer.Country == "Sweden"
                              select customer;

dx.Load<Customer>(dx.GetCustomersQuery());

The problem with the above code and LINQ is that LINQ is static. So if we need to dynamically create a criteria it will be hard.

If we for example have an advanced search form with several ComboBox(s) and also TextBox(s) etc. we want to create a query based on the user’s inputs. It will require a lot of code if we use LINQ and probably an ugly condition statement. Wouldn’t it be great if we could do something like this:

var dx = new CustomerDomainService();

string criteria = string.Format("Country = {0} and CompanyName = {1}", countryComboBox.SelectedItem, companyNameTextBox.Text);

dx.Load<Customer>(dx.GetCustomersQuery(criteria));

The above code will dynamically create a criteria based on the user’s input and pass it to the GetCustomersQuery.

The CustomerDomainService GetCustomers Query method will look something like this:

[EnableClientAccess()]
public class DomainService1 : LinqToEntitiesDomainService<NORTHWNDEntities>
{
    public IQueryable<Customer> GetCustomers(string criteria)
    {
          return this.ObjectContext.CustomerSet;
    }
}

As you may see the criteria isn’t used in the code, and if we want to use it we need to parse the criteria and that will require a lot of code to create different static LINQ queries based on criteria. But there is a solution, that is the point of this blog post ;)

We can use the LINQ Dynamic Query Library to solve the problem. To use the LINQ Dynamic Query Library we need to add “using System.Linq.Dynamic” namespace to our DomainService (this after we have the LINQ Dynamic Query Library). To get it you can simply downloaded the example Lib from one of the following links:

  • VB Dynamic Query Library (included in the \Language Samples\LINQ Samples\DynamicQuery directory)
  • C# Dynamic Query Library (included in the \LinqSamples\DynamicQuery directory)

  • What I did to make it work was to add the Dynamic.cs (.vb) class from the example to my Web project. Then added the “using System.Linq.Dynamic” to get the extension method added to the Dynamic.cs (.vb) file to the IEnumerable<T> interface.

    When this is done we will have an extended version of the Where extension method which will take a criteria as a string and parse it for us to a valid query.

    Here is a simple example where I have created a criteria on the client-side as a string and pass it down to the DomainService, where I use the LINQ Dynamic Query Library:


    CustomerDomainService.cs

    [EnableClientAccess()]
    public class CustomerDomainService : LinqToEntitiesDomainService<NORTHWNDEntities>
    {
        public IQueryable<Customer> GetCustomers(string criteria)
        {
             if (!string.IsNullOrEmpty(criteria))
                 return this.ObjectContext.CustomerSet.Where(criteria, new object[] { });
    
             return this.ObjectContext.CustomerSet;
        }
    }

    MainPage.xaml.cs

    public partial class MainPage : UserControl
    {
        public MainPage()
        {
           InitializeComponent();
    
           var dx = new CustomerDomainService();
    
           string criteria = "Country = \"Sweden\" and City <> \"Luleå\"";
    
           dx.Load<Customer>(dx.GetCustomersQuery(criteria));
    
           customersDataGrid.ItemsSource = dx.Customers;
        }
    }

    When using the LINQ Dynamic Query Library all strings need to be enclosed by “ “, so that is the reason why you see \”Sweden\”. To make the criteria more interesting I also added that the City property shouldn’t be “Luleå” (Luleå is a city far to the north in Sweden).

    The LINQ Dynamic Query Library will also work on Linq To SQL etc.

    Summary

    By using the LINQ Dynamic Query Library we can now in a easier way dynamically create a criteria based on users options, for example on a advanced search form.

    If you want to know when I post a new blog post on my blog etc, you can follow me on twitter: http://www.twitter.com/fredrikn

    6 Comments

    • Great blog post! I think this is a pretty common client with dynamic UIs. When you're using the DomainDataSource, it goes to great pains to generate the queries for you dynamically. If you're using an MVC pattern and don't want to use DDS, you've got to do this yourself.

      Have you considered modifying the DynamicQuery library so that it can function on the client tier against the EntityQuery class? Based on a quick look at the code it seems possible. This would seem a little more natural, so that your client-side code could use the DynamicQuery library to build up the queries it sends to the server. The server would not need to do anything special to handle dyanamically generated queries (such as taking a string parameter).


    • @Jason Allor:
      I was thinking about the same thing. But the EntityQuery doesn't implement the IQueryable&lt;T&gt; or IEnumerable&lt;T&gt; interface, so the extensions method added to the Dynamic class can't be used.

    • using this method of sorting, you open security vulnerability like sql-injection.


    • @Alexander:
      "using this method of sorting, you open security vulnerability like sql-injection"
      The LINQ Dyn. Query Lib. will prevent SQL-injection if it's used with Linq to SQL or EF. You have already exposed for example all Customers, so the only thing a hacker can do is to filter the data.. but it will only reduce the number of Customers to show.. so can you give an example when you think it will be a security risk? You can't with the Dyn. Q. Lib. change anything, just use it to for queries.

    • @Alexander @Fredrik: More to the point, we are only talking about the client side of filtering here which should never be doing any filtering which is related to security. Any filtering that is related to security is being done server side within the Query method of the DomainService.

    • Great post!

      Also the idea mentioned by Jason to build dynamic query on client site looks very solid from point of view of separating client logic from server site.
      Would be nice eventually to built this kind of library on top of EntityQuery.

      Thanks!

    Comments have been disabled for this content.