2013-01-11 14 views
7

Ho un oggetto IQuerable<object> source e devo ottenere qualcosa di simile (ma usando la reflection).Utilizzare reflection per creare un'espressione lambda come x => new {..}

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

Come posso farlo, o dove posso trovare riferimenti per la costruzione di quel tipo di albero di espressioni.

Grazie

+0

se si conosce il tipo che è possibile utilizzare Enumerable.Cast: http://msdn.microsoft.com/en-us/library/bb341406.aspx –

+0

Potrebbe essere un'opzione per trasmettere gli elementi a 'dynamic'? – Douglas

+0

i suoni dinamici sono un'opzione migliore del riflesso. Sembra piuttosto imbarazzante usare la riflessione. Wy non puoi usare qualcosa che sia conforme a un'interfaccia come INameValuePair? – mathk

risposta

12

È possibile creare Expression s utilizzando il System.Linq.Expressions namespace (MSDN)

Nel tuo caso, sarebbe simile a questa:

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

La si potrebbe usare in questo modo :

IQueryable<Source> list = ... 

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

E 'un po' poco chiaro su quali informazioni di tipo (se presenti) siano note durante il runtime. Piggybacking su Risposta di Nicholas Butler (dal momento in cui è stata accettata) Supponiamo che tu "sappia" il tipo di sorgente (cioè qual è il tipo di T nell'origine IQueryable) e "conoscerai" il tipo di destinazione (tipo di articolo restituito dal metodo di estensione IQueryable.Select). Quando dico "so", voglio dire che è qualcosa che può essere scoperto in fase di esecuzione senza dinamiche, riflessioni, late-binding, ecc. In caso contrario, la sua soluzione funzionerà solo se i tipi di origine e destinazione hanno proprietà con quei nomi corrispondenti (es. "Nome/Id" & "Nome/Valore").

Detto questo, c'è una soluzione molto semplice, senza dover costruire manualmente la tua espressione lambda ...

Soluzione: primo luogo permette di definire questi 2 tipi solo così sappiamo che cosa abbiamo a che fare con. Lo faccio solo perché non so quali tipi che si sta utilizzando, quindi queste sono davvero segnaposto per ciò che si sta realmente utilizzando, quindi questo non è necessario per la soluzione, solo per scopi di demo/esempio:

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

Quindi consente di definire una semplice classe statica con 2 metodi. Un metodo creerà l'espressione lambda e l'altro metodo convertirà e selezionare la sorgente (IQueryable) in 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}"; 

Così ci si va. Non sono sicuro che sia quello che stai cercando, o se soddisfa le tue esigenze. Spero che qualcuno lo troverà utile.