Implementing Search Without Convoluted If Statements In Asp.Net MVC
Introduction
In the beginning, when I start to learn C# and ASP.NET MVC, building search form was a complicated thing to do for me, the more criteria I've had for a search form, the harder it would get, but it was hard because I didn't use the proper tools to do my job, back then, I didn't know about IQueryable
interface and what Linq deferred execution was. In this post, I'm going to show you how you can build any kind of search form without complicating things, using Linq deferred execution.
Search Form, The Wrong Way
First, I'm going to show you the wrong way to do it, and I'm telling you, if your search becomes more than a mere two field, the logic can become a real mess, Imagine we have a search form like this:
And here our ugly, naive, mess of code for this form:
public ActionResult ManageOrders
(DateTime fromDate, DateTime toDate, string tracingCode, string customerName)
{
if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate)
&& string.IsNullOrEmpty(tracingCode) && string.IsNullOrEmpty(customerName))
{
var model = db.Orders.Where(s => s.OrderDate >= fromDate && s.OrderDate <= toDate);
return View(model);
}
else if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate)
&& !string.IsNullOrEmpty(tracingCode) && !string.IsNullOrEmpty(customerName))
{
var model = db.Orders.Where(s => s.OrderDate >= fromDate
&& s.OrderDate <= toDate
&& s.TracingCode == tracingCode && s.CustomerName == customerName);
return View(model);
}
else if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate)
&& string.IsNullOrEmpty(tracingCode) && !string.IsNullOrEmpty(customerName))
{
var model = db.Orders.Where(s => s.OrderDate >= fromDate
&& s.OrderDate <= toDate && s.CustomerName == customerName);
return View(model);
}
else if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate)
&& !string.IsNullOrEmpty(tracingCode) && string.IsNullOrEmpty(customerName))
{
var model = db.Orders.Where(s => s.OrderDate >= fromDate
&& s.OrderDate <= toDate && s.TracingCode == tracingCode);
return View(model);
}
else if (string.IsNullOrEmpty(fromDate) && string.IsNullOrEmpty(toDate)
&& !string.IsNullOrEmpty(tracingCode) && !string.IsNullOrEmpty(customerName))
{
var model = db.Orders.Where(s => s.TracingCode == tracingCode
&& s.CustomerName == customerName);
return View(model);
}
else if (string.IsNullOrEmpty(fromDate) && string.IsNullOrEmpty(toDate)
&& string.IsNullOrEmpty(tracingCode) && !string.IsNullOrEmpty(customerName))
{
var model = db.Orders.Where(s => s.CustomerName == customerName);
return View(model);
}
else if (string.IsNullOrEmpty(fromDate) && string.IsNullOrEmpty(toDate)
&& !string.IsNullOrEmpty(tracingCode) && string.IsNullOrEmpty(customerName))
{
var model = db.Orders.Where(s => s.TracingCode == tracingCode);
return View(model);
}
return View(db.Orders);
}
As you can see, we have to check for each and every one of the conditions, for when one field is filled and others are empty and vise versa, and return the correct filtered model, this approach is just plain wrong and shouldn't be used, at least when we have Linq and deferred execution.
Search From, The Right Way
With the powers that Linq and its deferred execution give us, we turn the above code to this:
public ActionResult ManageOrders
(DateTime fromDate, DateTime toDate, string tracingCode, string customerName)
{
IQueryable<orders> orders = db.Orders;
if (!string.IsNullOrEmpty(fromDate) && !string.IsNullOrEmpty(toDate))
{
orders = orders.Where(s => s.OrderDate >= startDate && s.OrderDate <= endDate);
}
if (!string.IsNullOrEmpty(tracingCode))
{
orders = orders.Where(s => s.TracingCode.Contains(tracingCode));
}
if (!string.IsNullOrEmpty(customerName))
{
orders = orders.Where(s => s.CustomerName.Contains(customerName));
}
return View(orders);
}</orders>
You can make it even shorter if you use ternary if
, but the point is, now all we had to do was to check if the action parameter is not empty, and add another where
to our search query. Here, we use Linq to SQL which is different form Linq to object, but we can use Linq to object too, which means we can use IEnumerable
instead of IQueryable
, but using IQueryable
here is more efficient, here is a good explanation of their difference, to put it briefly, Linq to SQL runs our query in the SQL Server, so we get only what we need, but Linq to object retrieves the whole entity and then filters it in memory.
With deferred execution that Linq gives us, we can build an IQueryable
or IEnumerable
and accumulate our where
s on it based on our conditions, this is far better than what we had before. CodeProject