Use reflection to create lambda expression like x => new { .. }
Asked Answered
L

2

7

I have an IQuerable<object> source object and have to get from it something like that (but using reflection).

source.Select(t => new SelectListItem { Name = t.Name, Value = t.Id })

How can I do that, or where can i find references of constructing that kind of expression tree.

Thanks

Litter answered 11/1, 2013 at 9:36 Comment(4)
if you know the type you may use Enumerable.Cast: msdn.microsoft.com/en-us/library/bb341406.aspxLovegrass
Would it be an option to cast the elements to dynamic?Ophicleide
dynamic sounds a better option than reflection. It look quite awkward to use reflection. Wy can't you use something that conform to an interface like INameValuePair?Felafel
Well, i am afraid a dynamic expression cannot be used in an expression tree.Litter
C
12

You can create Expressions using the System.Linq.Expressions namespace ( MSDN )

In your case, it would look something like this:

var source = typeof( Source );
var target = typeof( SelectListItem );

var t = Expression.Parameter( source, "t" );

var sourceName = Expression.MakeMemberAccess( t, source.GetProperty( "Name" ) );
var sourceId = Expression.MakeMemberAccess( t, source.GetProperty( "Id" ) );

var assignName = Expression.Bind( target.GetProperty( "Name" ), sourceName );
var assignValue = Expression.Bind( target.GetProperty( "Value" ), sourceId );
var targetNew = Expression.New( target );
var init = Expression.MemberInit( targetNew, assignName, assignValue );

var lambda =
  ( Expression<Func<Source,SelectListItem>> ) Expression.Lambda( init, t );

The you could use it like this:

IQueryable<Source> list = ...

List<SelectListItem> items = list.Select( lambda ).ToList();
Chincapin answered 11/1, 2013 at 10:20 Comment(1)
Is there any way to change var lambda = (Expression<Func<Source, SelectListItem>>) to var lambda = ( Expression<Func<Source, object>>)?Pox
S
0

Its a little unclear as to what Type information (if any) is known during runtime. Piggybacking on Nicholas Butler's answer (since its been accepted) Let's assume that you will "know" the source type (i.e. what is the Type of T in the IQueryable source) and you will "know" the target Type (item Type returned from the IQueryable.Select extension method). When I say "know", I mean it is something that can be discovered at runtime without dynamics, reflection, late-binding, etc. Otherwise his solution will only work if the source and target types just happen to have properties with those matching names (i.e. "Name/Id" & "Name/Value").

Given this, there is a pretty simple solution without having to manually construct your lambda expression...

Solution: First lets define these 2 types just so we know what we are dealing with. I only do this because I do not know what types you are using, so these are really placeholders for what you are actually using, so this is not needed for your solution, just for demo/example purposes:

//this is whatever your source item type is (t) 
public class NameIdPair
{
    public string Name { get; set; }

    public string Id { get; set; }
}

//this is whatever the SelectListItem type is you're using
public class SelectListItem
{
    public string Name { get; set; }

    public string Value { get; set; }
}

Next lets define a simple static class with 2 methods. One method will create the lambda expression and the other method will convert and select the source (IQueryable) into IEnumerable:

 public static class QueryableExtensions
{
    public static IEnumerable<TItem> Select<TSource, TItem>(this IQueryable<TSource> source)
        where TSource : NameIdPair
        where TItem : SelectListItem, new()
    {
        if (source == null) throw new ArgumentNullException("source");

        return source.Select(CreateLambda<TSource, TItem>());
    }

    public static Expression<Func<TSource, TItem>> CreateLambda<TSource, TItem>()
        where TSource : NameIdPair
        where TItem : SelectListItem, new()
    {

        return (t) => new TItem { Name = t.Name, Value = t.Id };
    }
}

Usage:

    //create an instance of an IQueryable<T> for demo purposes
var source = new[]
{
    new NameIdPair {Name = "test1_name", Id = "test1_Id"},
    new NameIdPair {Name = "test2_name", Id = "test2_Id"}
}.AsQueryable();

//you can call the "Select" extension method to select the queryable into an enum.
var enumerable = source.Select<NameIdPair, SelectListItem>();   
//'enumerable' is an IEnumerable<SelectListItem> instance

//or if you just want the lambda expression...
var lambda = QueryableExtensions.CreateLambda<NameIdPair, SelectListItem>();

//lambda.ToString() returns "t => new SelectListItem() {Name = t.Name, Value = t.Id}";

So there you go. Not sure if that's is what you are looking for, or if it fits your needs. Hopefully someone will find it useful.

Sotos answered 31/10, 2015 at 0:16 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.