2013-01-11 11 views
7

Mam obiekt IQuerable<object> source i muszę pobrać z niego coś takiego (ale za pomocą odbicia).Użyj odbicia, aby utworzyć wyrażenie lambda, takie jak x => nowy {..}

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

Jak mogę to zrobić, lub gdzie mogę znaleźć odniesienia do konstruowania tego rodzaju drzewa wyrażeń.

Dzięki

+0

jeśli znasz typ, którego możesz używać Enumerable.Cast: http://msdn.microsoft.com/en-us/library/bb341406.aspx –

+0

Czy byłaby to opcja rzutowania elementów na 'dynamic'? – Douglas

+0

dynamiczne dźwięki to lepsza opcja niż odbicie. Wygląda dość niewygodnie, aby użyć refleksji. Wy nie możesz użyć czegoś, co jest zgodne z interfejsem takim jak INameValuePair? – mathk

Odpowiedz

12

Można tworzyć Expression S i System.Linq.Expressions nazw (MSDN)

w Twoim przypadku, to będzie wyglądać mniej więcej tak:

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); 

można używać go tak jak to :

IQueryable<Source> list = ... 

List<SelectListItem> items = list.Select(lambda).ToList(); 
0

Jest trochę niejasne, jakie informacje o typie (jeśli są) są znane podczas uruchamiania. Piggybacking na Odpowiedź Nichola Butlera na (ponieważ została przyjęta) Załóżmy, że "znasz" typ źródła (tj. Jaki jest typ T w źródle IQueryable) i "znasz" typ celu (typ pozycji zwrócono z metody rozszerzenia IQueryable.Select). Kiedy mówię "wiem", mam na myśli, że jest to coś, co można odkryć w czasie wykonywania bez dynamiki, refleksji, późnego wiązania itp. W przeciwnym razie jego rozwiązanie będzie działać tylko wtedy, gdy typy źródłowe i docelowe będą miały właściwości z pasującymi nazwami. ("Nazwa/Id" & "Nazwa/Wartość").

Biorąc pod uwagę to, że jest to całkiem proste rozwiązanie bez konieczności ręcznego skonstruować swój wyrażenia lambda ...

Rozwiązanie: Pierwszy pozwala zdefiniować te 2 typy tak wiemy, co mamy do czynienia. I tylko to zrobić, ponieważ nie wiem, jakiego rodzaju używasz, więc są naprawdę zastępcze do czego faktycznie użyciu, więc to nie jest potrzebne do rozwiązania, tylko na demo/przykładowych celów:

//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; } 
} 

Następnie definiujemy prostą klasę statyczną za pomocą 2 metod. Jedną z metod stworzy wyrażenia lambda, a druga metoda będzie konwertować i wybierz źródło (IQueryable) do 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 }; 
    } 
} 

Zastosowanie:

//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}"; 

Więc pójdziesz. Nie wiem, czy to jest to, czego szukasz, lub jeśli odpowiada Twoim potrzebom. Miejmy nadzieję, że ktoś okaże się przydatny.