Introduction
It's probably safe to assume that if you are reading this, then you are somewhat connected to the enterprise applications world. Chances are then your apps did, in one form or the other provide search functionality to users. We may have moved from the filters/parameters to a free form ubiquitous search, but the needs remains.
I had a similar need for one of my recent projects – Users wanted a free form “bing” like search that works across the application, consistently and is super responsive.
While I could have done it in a number of ways, the approach we took was to design a re-usable search skeleton design that anyone creating a web app using the ASP.NET MVC Framework can simply plug in with minimal effort and search-enable their application.
I will use a sample contact manager application to demonstrate how to write a skeleton that allows search functionality to be easily integrated into your ASP.NET MVC web site. Before I jump into the code, let's first take a look at the end result so you'll be able to envision what I am trying to accomplish.
In the simplest form, you type the search text you want to find into the text box, select the search category you want to use from the Dropdown List and click the "Search" icon.
Search Flow
Search Panel
When a user types something and clicks on the search icon, the searchClick
method will execute.
function searchClick() {
var category = $("#searchList").val();
var searchText = $("#txtSearch").val();
var queryString = "SearchText=" + searchText + "&Category=" + category;
$.post("/Search/Search", queryString, callBackSearch, "_default");
}
By using $.post
(I am using jQuery), we are pointing to my Search controller Search action. It will also allow us to pass the query string. In callBackSearch
function, we will embed the return response HTML in the current page.
$('#SearchResult').html(responseText);
Search Controller
public ActionResult Search(RouteInfo routeValues)
{
string actionName = "Search";
string controllerName =
SearchHelper.GetControllerName(routeValues.Category);
return RedirectToAction(actionName, controllerName, routeValues);
}
SearchHelper.GetControllerName
will return the controller name which is responsible for the search result view for a given category.
public static string GetControllerName(string category)
{
return "Contact";
}
For demo purposes, I am returning a hard coded string “Contact
”. In a real scenario, you may need to keep some controller configuration for a different category search. Please change as appropriate.
The Search
controller will now redirect to a specific controller search action. In this case, it will call the Contact
controller.
Contact Controller
public ActionResult Search(RouteInfo routeValues,
SearchCriteriaSubController searchCriteria)
{
ContactManagerEntities _entitities = new ContactManagerEntities();
string criteria = SearchHelper.GetSqlCriteria(routeValues.SearchText,
routeValues.Category);
List<contact> contactList = _entitities.GetContacts(criteria).ToList();
ViewData["contactList"] = contactList;
return View();
}
Contact
controller is responsible for creating the search result view. To get the data for a given search text, we need to create a stored procedure named GetContacts
.
ALTER PROCEDURE dbo.GetContacts
(
@criteria NVARCHAR(3000)
)
AS
SET NOCOUNT ON
DECLARE @SQLString NVARCHAR(4000);
SET @SQLString = N'SELECT ID, FirstName, LastName, Phone, Email
FROM Contacts WHERE ' + @criteria ;
EXECUTE sp_executesql @SQLString
RETURN
If your user types ‘Microsoft’ on the search text, then the criteria which will be passed to this stored procedure will be:
“[FirstName] LIKE '%microsoft%' OR [LastName] LIKE '%microsoft%'
OR [Email] LIKE '%microsoft%' “
Note: You can maintain your own set of SQL symbol format (LIKE
, IN
, BETWEEN
, =
, >
, <
, etc.). In this demo, I have used contain search functionality. Also you can extend this search helper class to allow user Full Text Search functionality. Click on the following link to get an idea of how to make Google style queries to SQL Server's full-text search CONTAINS
predicate syntax.
SQL Criteria Builder
public static string GetSqlCriteria(string searchText, string category)
{
StringBuilder sb = new StringBuilder();
List<searchcolumn> SearchColumns = GetSearchColumns(category);
bool isFirstRow = true;
foreach (SearchColumn row in SearchColumns)
{
string text;
text = string.Format(row.SymbolFormat, row.ColumnName, searchText);
if (isFirstRow)
isFirstRow = false;
else
sb.Append(" OR ");
sb.Append(text);
}
return sb.ToString();
}
Once SQL criteria is ready, the contact
controller will pass it to dbEntitities GetContacts
method to get the search result.
List<contact> contactList = _entitities.GetContacts(criteria).ToList();
Result View
Display Search Criteria using Sub Controller
Since our Search Criteria UI is common for each category search result, I wanted to re-use criteria UI pieces on other pages. I have used sub controller’s action to render the search criteria UI inside the Result view page.
SubController
derives from Controller
and implements an ISubController
interface. But it is a controller, so:
- It has its own view.
- It has ViewData.
- It has access to query string and the rest of the controller context.
You can download and explore a complete working sample on SubController from MvcContrib.
Listing SearchCriteriaSubController.cs
public ViewResult SearchCriteria(RouteInfo routeValues)
{
ViewData["text"] = "SearchText = " + routeValues.SearchText +
" and Category = " + routeValues.Category;
return View();
}
Listing SearchCriteria.ascx
Summary
So to re-iterate, these are the steps you will follow to search enable any other applications:
- Create Search Controller
- Create Search Stored Procedure
- Set necessary changes to redirect routing from search controller
- Create a Results view
Conclusion
My goal was to demonstrate how simple and straightforward it is to enable searches in your applications. The fact that the ASP.NET MVC Framework makes it easier to modularize and componentize your design/code makes it even better as the platform of choice for Windows based web applications.
We created a working sample app, outlined the search requirements, created a reusable skeleton project that implements the search function and showed how to wire that within your app to search enable it.
That's really all there is to it. For those of you not familiar with ASP.NET MVC and entity framework, the code may seem confusing at first, but once you play with it a little and look at a few samples, I'm sure you'll get the hang of it. For questions/concerns, it would be much appreciated if you can leave a comment on the blog.
References