9

Similar: Convert a string to Linq.Expressions or use a string as Selector?

A similar one of that one: Passing a Linq expression as a string?

Another question with the same answer: How to create dynamic lambda based Linq expression from a string in C#?

Reason for asking something which has so many similar questions:

The accepted answer in those similar questions is unacceptable in that they all reference a library from 4 years ago (granted that it was written by code master Scott Gu) written for an old framework (.net 3.5) , and does not provide anything but a link as an answer.

There is a way to do this in code without including a whole library.

Here is some sample code for this situation:

    public static void getDynamic<T>(int startingId) where T : class
    {
        string classType = typeof(T).ToString();
        string classTypeId = classType + "Id";
        using (var repo = new Repository<T>())
        {
            Build<T>(
             repo.getList(),
             b => b.classTypeId //doesn't compile, this is the heart of the issue
               //How can a string be used in this fashion to access a property in b?
            )
        }
    }

    public void Build<T>(
        List<T> items, 
        Func<T, int> value) where T : class
    {
        var Values = new List<Item>();
        Values = items.Select(f => new Item()
        {
            Id = value(f)
        }).ToList();
    }

    public class Item
    {
     public int Id { get; set; }
    }

Note that this is not looking to turn an entire string into an expression such as

query = "x => x.id == somevalue";

But instead is trying to only use the string as the access

query = x => x.STRING;
Community
  • 1
  • 1
Travis J
  • 81,153
  • 41
  • 202
  • 273
  • Have you ruled out doing simple reflection to make your `Func`? – Paul Phillips Jul 13 '12 at 23:41
  • @PaulPhillips - Reflection will yield the property type or name of the property in class T which is passed in. Class T is passed in only as a reference for type when connection to an Entity Framework Repository. A list of all Class T is returned from the repository (`repo.getList()`). How would reflection help in including properties from Class T in the linq expression? – Travis J Jul 13 '12 at 23:45
  • Your example made it look like you're doing linq-to-objects, in which case you could grab the value of the property reflectively. But since you're working in another context, I'm not sure it would work. I posted my attempt so you could check – Paul Phillips Jul 13 '12 at 23:47
  • Thought about it more, it won't work. The expression tree will just see the text of the function, not get the value. – Paul Phillips Jul 13 '12 at 23:54
  • @PaulPhillips - Thanks for the suggestion, I am definitely looking at all avenues. – Travis J Jul 13 '12 at 23:56
  • 3
    If you want to put a bounty on this question you should do so, saying such-and-such answer will be bountied is silly. – Hogan Jul 14 '12 at 00:00
  • 1
    @Hogan - Sorry, I cannot post a bounty for another 2 days. If I could I would. Perhaps that is an issue you can raise on meta. – Travis J Jul 14 '12 at 00:19
  • @Travis - Well I guess it didn't matter because I solved your problem anyway :D enjoy. – Hogan Jul 14 '12 at 00:24
  • Nobody has suggested opening LinqPad and seeing what they do? I distinctly remember this being included as an example with the Roslyn CTP, and they had an example of how to do it the "old" way (without Roslyn). I think they compiled c# into a memorystream and then executed it. – Michael Graczyk Jul 16 '12 at 02:57

3 Answers3

15

Here's an expression tree attempt. I still don't know if this would work with Entity framework, but I figure it is worth a try.

Func<T, int> MakeGetter<T>(string propertyName)
{
    ParameterExpression input = Expression.Parameter(typeof(T));

    var expr = Expression.Property(input, typeof(T).GetProperty(propertyName));

    return Expression.Lambda<Func<T, int>>(expr, input).Compile();  
}

Call it like this:

Build<T>(repo.getList(), MakeGetter<T>(classTypeId))

If you can use an Expression<Func<T,int>> in place of a just a Func, then just remove the call to Compile (and change the signature of MakeGetter).


Edit: In the comments, TravisJ asked how he could use it like this: w => "text" + w.classTypeId

There's several ways to do this, but for readability I would recommend introducing a local variable first, like this:

var getId = MakeGetter<T>(classTypeId);

return w => "text" + getId(w); 

The main point is that the getter is just a function, and you can use it exactly like you normally would. Read Func<T,int> like this: int DoSomething(T instance)

Paul Phillips
  • 6,093
  • 24
  • 34
  • I was able to get this to work in linqPad -- here is the code https://gist.github.com/3108507 – Hogan Jul 14 '12 at 00:44
  • @Paul - Thank you for some working code and example. I was also able to get this to work. I did not change the signature because it would have interfered with more linq down the line. I did have one question, what would I have to do to use `MakeGetter` in this way: `w => "text" + MakeGetter(classTypeId)`? Similar to `w => "text" + w.classTypeId`. – Travis J Jul 15 '12 at 19:23
  • @PaulPhillips - Thank you for the edit, and clarification. Although I have read a lot of documentation at MSDN and around the web I am still getting used to delegates, Funcs, and Expression trees. I appreciate it. – Travis J Jul 16 '12 at 17:51
2

Here is an extension method for you with my testing code (linqPad):

class test
{
   public string sam { get; set; }
   public string notsam {get; set; }
}

void Main()
{
   var z = new test { sam = "sam", notsam = "alex" };

   z.Dump();

   z.GetPropertyByString("notsam").Dump();

   z.SetPropertyByString("sam","john");

   z.Dump();
}

static class Nice
{
  public static void  SetPropertyByString(this object x, string p,object value) 
  {
     x.GetType().GetProperty(p).SetValue(x,value,null);
  }

  public static object GetPropertyByString(this object x,string p)
  {
     return x.GetType().GetProperty(p).GetValue(x,null);
  }
}

results:

results

Hogan
  • 69,564
  • 10
  • 76
  • 117
  • This works in your example, however, I see no integration with linq here. See Paul's answer for a linq integration. – Travis J Jul 15 '12 at 19:24
  • I guess, his works on a type mine works on an object... 6 of one. I think mine would be more performent since there is no templating needed. – Hogan Jul 15 '12 at 19:30
1

I haven't tried this, and not sure if it would work, but could you use something like:

b => b.GetType().GetProperty(classTypeId).GetValue(b, null);

Duncan Howe
  • 2,965
  • 19
  • 18