9

Given that I have an IEnumerable<T>, where T is any object, how can I select a specific property from it, given that I know the name of the one of the property names at run time as a string?

For example:

var externalIEnumerable = DataPassedFromConsumingCode(); // `IEnumerable<T>`

string knownPropertyName = "Foo";
var fooSelect = externalIEnumerable.Select(...);

In essence, I'm obviously just doing externalIEnumerable.Select(x=> x.Foo);, but I need to perform this Select at runtime, when I don't have control over when it's initially created.

--

ANSWER: Based on AlanT's answer, here's what I actually did:

public Expression<Func<TItem, object>> SelectExpression<TItem>(string fieldName)
{
    var param = Expression.Parameter(typeof(TItem), "item");
    var field = Expression.Property(param, fieldName);
    return Expression.Lambda<Func<TItem, object>>(field, 
        new ParameterExpression[] { param });
}

I kept it as an Expression, because calling Compile caused the IQueryable to be Enumerated, which meant the database was hit unnecessarily. So, to use it, I just do the following:

string primaryKey = _map.GetPrimaryKeys(typeof(TOriginator)).Single();
var primaryKeyExpression = SelectExpression<TOriginator>(primaryKey);
var primaryKeyResults = query.Select(primaryKeyExpression).ToList();
djdd87
  • 67,346
  • 27
  • 156
  • 195
  • can you constrain T in the IEnumerable? – Jason Jan 24 '12 at 16:20
  • 1
    This isn't a linq-specific question, right? You are asking how to access a property dynamically by name rather than statically in code? – Chris Shain Jan 24 '12 at 16:20
  • To clarify, your T could be anything, your property could be anything, you will simply have the property name as a string? – Anthony Pegram Jan 24 '12 at 16:21
  • I can't constrain T, because it could be any class. @AnthonyPegram - Correct. – djdd87 Jan 24 '12 at 16:22
  • Where are you getting the property name? This sounds very like the functionality that used to be encasulated in the dynamic Linq library. – Lazarus Jan 24 '12 at 16:25
  • @Lazarus, my code takes a `DbContext`, whips out all the mapping information, etc. I then take an `IQuerable` and, given that I know it's primary keys as strings, I want to modify the IQueryable to only return me the primary keys. Basically, I'm taking an Entity Framework `IQueryable`, but I want to change it to only return the primary keys. – djdd87 Jan 24 '12 at 16:27
  • So, why wouldn't you create your own base Interface that defines the primary key property and use that as your `T`? Are you working in a brown field environment? – Lazarus Jan 24 '12 at 16:30
  • @Lazarus The code is meant to work with any entity, but I never know the entity type at design time. I only know what the entity is and it's primary keys at run time. It's a totally generic piece of code meant to work with any DbContext. – djdd87 Jan 24 '12 at 16:31
  • @GenericTypeTea OK. Just saw the last comment. My solution will not work if you do not know the type at design time. AFAIK you are down to using Reflection at this point. It can probably be made to work; just use a selection function that uses reflection to find the value. Do you know the Field Type at design time or will you just get an IEnumerable back? Do you need to know the specific type of the field at some point? – AlanT Jan 24 '12 at 16:57
  • @AlanT I adapted your answer and it works well. Thanks. – djdd87 Jan 24 '12 at 17:05
  • This is a great question. Since what you're actually talking about isn't specific to LINQ, I've generalized the title to more accurately reflect what's being asked here. I also added the [tag:duck-typing] tag. – Justin Morgan - On strike Jan 24 '12 at 21:08

3 Answers3

7

It is possible to do this using an Expression

e.g.

private class Foo {
    public string Bar { get; set; }
}

private IEnumerable<Foo> SomeFoos = new List<Foo>() {
    new Foo{Bar = "Jan"},
    new Foo{Bar = "Feb"},
    new Foo{Bar = "Mar"},
    new Foo{Bar = "Apr"},
};

[TestMethod]
public void GetDynamicProperty() {

        var expr = SelectExpression<Foo, string>("Bar");
        var propValues = SomeFoos.Select(expr);

        Assert.IsTrue(new[] { "Jan", "Feb", "Mar", "Apr" }.SequenceEqual(propValues));

    }

public static Func<TItem, TField> SelectExpression<TItem, TField>(string fieldName) {

    var param = Expression.Parameter(typeof(TItem), "item");
    var field = Expression.Property(param, fieldName);
    return Expression.Lambda<Func<TItem, TField>>(field, new ParameterExpression[] { param }).Compile();

}

hth,
Alan.

AlanT
  • 3,627
  • 20
  • 28
0

The dynamic linq library allows you to specify predicates and projections on the fly and may fit your use case-

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

ilias
  • 2,620
  • 2
  • 27
  • 37
0

You can dynamically build an Expression<Func<T, U>>.

jason
  • 236,483
  • 35
  • 423
  • 525