views:

378

answers:

6

What's the simplest way to code against a property in C# when I have the property name as a string? For example, I want to allow the user to order some search results by a property of their choice (using LINQ). They will choose the "order by" property in the UI - as a string value of course. Is there a way to use that string directly as a property of the linq query, without having to use conditional logic (if/else, switch) to map the strings to properties. Reflection?

Logically, this is what I'd like to do:

query = query.OrderBy(x => x."ProductId");

Update: I did not originally specify that I'm using Linq to Entities - it appears that reflection (at least the GetProperty, GetValue approach) does not translate to L2E.

+1  A: 

Yes, I don't think there's another way than Reflection.

Example:

query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));
TTT
+1  A: 

Reflection is the answer!

typeof(YourType).GetProperty("ProductId").GetValue(theInstance);

There's lots of things you can do to cache the reflected PropertyInfo, check for bad strings, write your query comparison function, etc., but at its heart, this is what you do.

Sebastian Good
+1  A: 
query = query.OrderBy(x => x.GetType().GetProperty("ProductId").GetValue(x, null));

Trying to recall exact syntax off the top of my head but I think that is correct.

dkackman
+12  A: 

I would offer this alternative to what everyone else has posted.

System.Reflection.PropertyInfo prop = typeof(YourType).GetProperty("PropertyName");

query = query.OrderBy(x => prop.GetValue(x, null));

This avoids repeated calls to the reflection API for obtaining the property. Now the only repeated call is obtaining the value.

However

I would advocate using a PropertyDescriptor instead, as this will allow for custom TypeDescriptors to be assigned to your type, making it possible to have lightweight operations for retrieving properties and values. In the absence of a custom descriptor it will fall back to reflection anyhow.

PropertyDescriptor prop = TypeDescriptor.GetProperties(typeof(YourType)).Find("PropertyName");

query = query.OrderBy(x => prop.GetValue(x));

As for speeding it up, check out Marc Gravel's HyperDescriptor project on CodeProject. I've used this with great success; it's a life saver for high-performance data binding and dynamic property operations on business objects.

Adam Robinson
good point. gotta be careful with those lambdas
dkackman
Note that reflected invocation (i.e. GetValue) is the most costly part of reflection. Metadata retrieval (i.e. GetProperty) is actually less costly (by an order of magnitude), so by caching that part, you are not really saving yourself that much. This is going to cost pretty much the same either way, and that cost is going to be heavy. Just something to note.
jrista
@jrista: invocation is the most costly, to be certain. However, "less costly" does not mean "free", or even close to it. Metadata retrieval takes a non-trivial amount of time, so there is an advantage to caching it and no *disadvantage* (unless I'm missing something here).In truth this should really be using a `PropertyDescriptor` anyway (to account for custom type descriptors, which *could* make value retrieval a lightweight operation).
Adam Robinson
A: 

I agree that reflection could be a way.

But, why use it when you can define a criteria from outside.
e.g.

if (sortBy == "ProductId")
   query.OrderBy(x => x.ProductId);
else
   query.OrderBy(x => x.ProductName);
shahkalpesh
Because that is a maintenance headache.
dss539
How is this a solution to his problem? What if there were 20 properties that could be sorted by?
Philip Wallace
@dss539: define maintenance headache in this example, please?
shahkalpesh
@Philip: What if the string variable contained a property name which didn't exist, leave aside the 20 properties which OP didn't tell about?
shahkalpesh
+1  A: 
Partha Choudhury