views:

61

answers:

2

If you have a simple Linq query like:

var result = from record in db.Customer
             select new { Text = record.Name,
                          Value = record.ID.ToString() };

which is returning an object that can be mapped to a Drop Down List, is it possible to dynamically specify which fields map to Text and Value?

Of course, you could do a big case (switch) statement, then code each Linq query separately but this isn't very elegant. What would be nice would be something like:

(pseudo code)

var myTextField = db.Customer["Name"];  // Could be an enumeration??
var myValueField = db.Customer["ID"];   // Idea: choose the field outside the query

var result = from record in db.Customer
             select new { Text = myTextField,
                          Value = myValueField };
+1  A: 

Right way to do this is with closures.

Func<Customer, string> myTextField = (Customer c) => c["Name"];
Func<Customer, int> myValueField = (Customer c) => c["ID"];

var result = from record in db.Customer
             select new { Text = myTextField(record),
                          Value = myValueField(record) };

The one limitation is that your definition of myTextField always needs to return a string.

sblom
Nice! That's ok with the string return because we could simply do a .ToString() somehow in the last line of code (myValueField(record)).ToString() ?
Dr. Zim
or maybe Func<Customer, string> myValueField = (Customer c) => c.ID.ToString(); ?
Dr. Zim
Would there be a way to determine the field name in the closure with a variable, say (Customer c) => c["Name"] ?
Dr. Zim
Yeah--I didn't mean to switch syntax out from under you. I see no reason why c["Name"] wouldn't work.
sblom
Oh, also, I don't mean _all_ of your filters need to return string always. I'm just saying that in order for the `select new{}` to work, all of the fields will always have to be of the same types.
sblom
c["Name"] gave a compile error of "Cannot apply indexing with [] to an expression of type "db.Customer"
Dr. Zim
+1  A: 

You could try something like

class Customer
        {
            public int ID { get; set; }
            public string Name { get; set; }
            public string Surname { get; set; }
        }

var dict = new Dictionary<string, Func<Customer, string>>
                      { { "ID", (Customer c) => c.ID.ToString() },
                        { "Name",     (Customer c) => c.Name},
                        { "Surname", (Customer c) => c.Surname } };

            List<Customer> rows = new List<Customer>();
            rows.Add(new Customer { ID = 1, Name = "Foo", Surname = "Bar"});
            var list = from r in rows
                       select new { Text = dict["ID"](r), Value = dict["Name"](r) };

To try to access the properties dynamically, you could try something like

var dict = new Dictionary<string, Func<Customer, string>>
           { { "ID", (Customer c) => c.GetType().GetProperty("ID").GetValue(c,null).ToString() },
           { "Name",     (Customer c) => c.GetType().GetProperty("Name").GetValue(c,null).ToString()},
           { "Surname", (Customer c) => c.GetType().GetProperty("Surname").GetValue(c,null).ToString() } };
astander
This has to be the coolest code I have seen yet in my C# life. It's almost like a builder design pattern: c.GetType().GetProperty("ID").GetValue(c,null).ToString() ... Very nice!
Dr. Zim