Click here to Skip to main content
15,799,186 members
Articles / Web Development

Incremental Search on the Web with Dynamic LINQ + SignalR

Rate me:
Please Sign up or sign in to vote.
5.00/5 (7 votes)
15 Jul 2017Apache4 min read 20.9K   14   4
Application of "MVVM over SignalR" library described in a previous CodeProject tip, combined with Dynamic LINQ library to do incremental web search by typing the query


The web-based defect tracking system I've been using is great for the most part, but if there's something that could be improved, it's the visual query builder that creates search filters. When I need to find certain defects on category X, submitted by either A or B during the month of July - or was it May? - and not sure whether it's been closed or merely ready for test, then what follows is a flurry of clicks on nested menu that pops one after the other to build the desired query expression.

Being a software developer, I could have typed the query myself and be done with it a lot quicker, but unfortunately that free-form typing feature is not offered. So I started to ask myself, how would I implement a list search system - on the web - that allows a free-form query typing with a bit of auto-complete helper, and to make it more interesting, produce results incrementally as the query is being typed?

The good news is, there is already a Microsoft library, System.Linq.Dynamic, that's capable of parsing a query string into a LINQ expression tree. I can just build a web application around this functionality.

In the previous CodeProject tip, "Real-time Web Made Simple with MVVM Pattern Over SignalR", I wrote about my open-sourced library named dotNetify which makes it very simple to do web apps as it does away with writing the entire RESTful service layer, and replacing it with automated, declarative 2-way MVVM binding. This would be a perfect job for it.


With dotNetify, the principal source code becomes very simple, consisting of only an HTML view and a .NET view model. Even though this runs on the web, I don't need to write any JavaScript for this.

Let's look at the view model first.

View Model

public class AFITop100VM : BaseVM
      public List<MovieRecord> Movies
            IEnumerable<MovieRecord> results;

            if (!String.IsNullOrEmpty(Query))
               results = AFITop100Model.AllRecords.Where(Query);
               results = AFITop100Model.AllRecords;

            return Paginate(results);

      public string Query
         get { return Get<string>(); }
            if (EnableAutoComplete)
               AutoComplete(ref value);


            if (IsQueryValid(value))
               Changed(() => Movies);

      public string QueryError
         get { return Get<string>(); }
         set { Set(value); }

      //...Properties/fields associated with auto-complete and pagination removed for brevity...

      private List<MovieRecord> _queryTest = new List<MovieRecord>();
      private int _errorPos;

      // Returns whether a query expression is valid.
      // If not, it will set the QueryError property.
      private bool IsQueryValid(string iQuery)
         QueryError = String.Empty;
         if (String.IsNullOrEmpty(iQuery))
            return true;

            return true;
         catch (ParseException ex)
            QueryError = ex.Message;
            _errorPos = ex.Position;
            return false;

      // Paginates the query results.
      private List<MovieRecord> Paginate(IEnumerable<MovieRecord> iQueryResults)
         //...removed for brevity...

      // Auto-completes a query expression in response to user typing the first letter of
      // a known model property or a LINQ method at the end of the expression.
      private void AutoComplete(ref string iQuery)
         //...removed for brevity...

This is an abstraction of the view. It inherits from BaseVM class from the dotNetify library, which I discussed in the mentioned CodeProject tip. It essentially abstracts out the communication nitty gritty between the view model on the server-side and the view on the client-side as an MVVM pattern. When you set a value to the view model property, you can be sure that the value will be sent out to the bound view element on the browser, and the input made on the browser gets sent back and updated to the view model property.

There is the Query property that is bound to the text box to capture your query string as you type, and also send out the auto-completed keyword back to the browser, courtesy of 2-way binding.

As the query gets typed, it gets evaluated in real-time by the System.Linq.Dynamic library in the IsQueryValid method, and either it throws an exception whose error message gets sent to the browser through QueryError property, or the process flow continues to raising the changed event on the Movies property.

The Movies property accessor applies the query string on the Movies list (our model) and returns the filtered list to the browser.

The results are paginated and there are other view model properties that deal with pagination links, but the details for this and the crude auto-complete function I consider out of scope for this CodeProject tip.


   <link href="/Content/bootstrap.min.css" rel="stylesheet">
   <script src="/Scripts/require.js" data-main="/Scripts/app"></script>
<body class="container">
   <h2>AFI's 100 Greatest American Films of All Time</h2>
   <div data-vm="AFITop100VM">
      <div class="well">

         <!-- Query input box -->
         <div class="input-group">
            <span class="input-group-addon">Search:</span>
            <input class="form-control" 
            type="text" data-bind="textInput: Query" />

         <!-- Parse exception error box -->
         <div class="label label-danger pull-right" 
         data-bind="text: QueryError"></div>

      <!-- Movies list -->
      <table class="table table-hover table-striped panel-primary">
         <tbody data-bind="foreach: Movies">
               <td><div data-bind="text: Rank" /></td>
               <td><div data-bind="text: Movie" /></td>
               <td><div data-bind="text: Year" /></td>
               <td><div data-bind="text: Cast" /></td>
               <td><div data-bind="text: Director" /></td>

      <!-- ...Pagination link markups removed for brevity -->

DotNetify client-side library, loaded through the module loader Require.js, uses the data-vm attribute to identify the view model scope. When this HTML is loaded on the browser, it will automatically make a server request to fetch initial data from the named view model.

The data flows between the server view model and the HTML view through the binding mechanism that are defined declaratively on the element markups. The data-bind binding notations belong to Knockout.js library, which not only provide binding with input elements (i.e. textInput, text), but can also generate elements dynamically, e.g., the foreach binding which generates the table row elements for each list item in the Movies property it's bound to.

And that's it! With dotNetify, business logic stays on the server-side while the client-side only handles the UI, and without manually writing the logic to make the communication happen.

Live Demo

Live demo is available on my website at


This article, along with any associated source code and files, is licensed under The Apache License, Version 2.0

Written By
United States United States
Full-stack s/w engineer, open source author.

Comments and Discussions

QuestionSome comments... Pin
Dewey4-Jan-16 21:41
Dewey4-Jan-16 21:41 
AnswerRe: Some comments... Pin
dsuryd5-Jan-16 7:44
dsuryd5-Jan-16 7:44 
I love good discussion, thank you for taking the time.

Although it may seem that way, the true goal is actually about giving developers a choice.

On my day job, we've been working on converting a decade worth of code base of a UI-rich desktop suite into SaaS web app with a completely redesigned modern UI experience. We've been hearing that the only way to go is an almost-complete rewrite to JS client-side framework and a lot of REST APIs. Given our situation and our resource pool, this would've been a daunting task and IMHO led to death march, if I didn't come up with this new way to do things.

I did go to extreme not to write Javascript in this LINQ example just to show that, contrary to popular opinion, it is possible to achieve this kind of interactivity without it. But in larger real-world app, my opinion is that we need to strike a good balance between client-side and server-side programming, because you're absolutely right that some things are better/easier to do on the client (and also true with the opposite). You will find that dotNetify is not opinionated in this regard - you can easily make any Javascript code work with it, or carve out a small section on your app for it, while everything else is done traditionally.

I am less clear with what you said on doing your own data binding; would you help elaborate this further with specifics on what you'd like to see in an example? The dotNetify GitHub Issues forum would be a good place to discuss it.

Thank's for letting me know about the "Bunny". What OS are you running (I assume your Chrome is up-to-date) and what's your ping time to the website? It does worry me to have this kind of high-frequency real-time example on a cheap, shared hosting server like mine. Did you try it locally?
GeneralRe: Some comments... Pin
Dewey5-Jan-16 10:22
Dewey5-Jan-16 10:22 
Generalstriking a balance between clientside/serverside Pin
Leblanc Meneses5-Jan-16 19:40
Leblanc Meneses5-Jan-16 19:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.