views:

289

answers:

3

What I'm trying to do is provide a generic search capability on a table. So the user is presented with all data in a table, they enter some text to filter on, and voila, the table is now filtered on all results that match that entry.

I have this working with a single field:

public ActionResult Index(string user_name)
{

    var dataContext = new PurchaseOrderDataContext();

    var orders = from o in dataContext.purchase_orders
                 where o.approved_by.display_name.StartsWith(user_name ?? "")
                 select o;

    if (Request.IsAjaxRequest())
    {
        return PartialView("list", orders);
    }
    else
    {
        return View(orders);
    }
}

But what I'm looking for is the ability for them to have a dropdown that allows them to pass in the field that they want to filter on. It would like something like this:

public ActionResult Index(string field, string query_value)
{

    var dataContext = new PurchaseOrderDataContext();

    var orders = from o in dataContext.purchase_orders
                 where o["field"].StartsWith(query_value ?? "")
                 select o;

    if (Request.IsAjaxRequest())
    {
        return PartialView("list", orders);
    }
    else
    {
        return View(orders);
    }
}

...Except that the o["field"] is just my silly guess at syntax (which doesn't work).

I guess I'm just confused because although I'm using this nice class-ified data model, I sometimes want to explicitly refer to the columns by name.

How would I go about doing this?

My apologies if this is obvious...

Edit: I think I'm going to leave this open for now to see if there's a neater, less hack-ish feeling solution than suggested below. Thanks!

+1  A: 

The way I would do this is with a switch statement:

var orders = dataContext.purchase_orders;

if(!string.IsNullOrEmpty(query_value))
{
    switch(field)
    {
        case "firstname":
            orders = orders.Where(x => x.FirstName.StartsWith(query_value));
            return;
        case "company":
            orders = orders.Where(x => x.Company.StartsWith(query_value));
            return;
        // etc.
    }
}

This also allows you to validate field.

Keltex
So much work... :-) I'm lazy. I was kind of hoping for a reusable routine because I'm dealing with quite a few tables in exactly this way and hate to keep repeating code that maps a string to a data column of the same name.
Andrew Flanagan
Am I wrong in thinking that there are a few jQuery plugins that provide this functionality on the client side?
Will
You can use dynamic link query that BFree mentions.
Keltex
+1  A: 

The main reason why you can't just put in the field name as the property name of your class is because Linq builds an expression tree.

Now what you can do is split up how you're building your expression tree by doing.

var orders = from o in dataContext.purchase_orders
                 select o;

if (!string.IsNullOrEmpty(user_name)) {
     orders = orders.Where(x => x.StartsWith(user_name);
}

And just add more if statements.... ugh... Yeah it's definitely not pretty, and someone can probably make it look a lot better by making a IDictionary<string, Func<Expression, string>> instead of a bunch of if statements, but it still feels like a hack.

Min
I was hoping you wouldn't say this... What a pain. Thanks though!
Andrew Flanagan
You know something, I thought about it a little bit. I found this article http://msdn.microsoft.com/en-us/library/bb882637.aspx. Tell us if you come up with something.
Min
Thanks -- that may be an option. I'm trying some stuff now...
Andrew Flanagan
+2  A: 

The only simple (depends on your definition of "simple" I guess) way to do this would be to use the Dynamic Linq API. See here:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

BFree
I ended up mainly going this route... It turned out to be fairly elegant and useful. I have a separate implementation for each model, but it's really not bad. Many thanks!
Andrew Flanagan