2012-07-13 18 views
8

Simile: Convert a string to Linq.Expressions or use a string as Selector?come trasformare una stringa in un'espressione linq?

uno simile di quello: Passing a Linq expression as a string?

Un'altra domanda con la stessa risposta: How to create dynamic lambda based Linq expression from a string in C#?

Motivo per chiedere qualcosa che ha così tante domande simili:

La accettato rispondere a quelle domande simili è inaccettabile in quanto tutte fanno riferimento a una libreria di 4 anni fa (a condizione che sia stata scritta dal codice master Scott Gu) scritta per un vecchio framework (.net 3.5), e non pr Ovidio tutto tranne un link come risposta.

C'è un modo per farlo nel codice senza includere un'intera libreria.

Ecco alcuni esempi di codice per questa situazione:

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

Nota che questo non sta cercando di trasformare un'intera stringa in un'espressione come

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

Ma invece sta cercando di solo per uso la stringa come accesso

query = x => x.STRING; 
+0

hai escluso facendo semplice riflessione per fai il tuo 'Func'? –

+0

@PaulPhillips - Reflection produrrà il tipo di proprietà o il nome della proprietà nella classe T che viene inoltrata. La classe T viene passata solo come riferimento per il tipo quando si effettua la connessione a un repository di Entity Framework. Un elenco di tutte le classi T viene restituito dal repository ('repo.getList()'). In che modo la riflessione aiuta a includere le proprietà della classe T nell'espressione linq? –

+0

Il tuo esempio ha fatto sembrare che stai facendo linq-to-objects, nel qual caso potresti prendere il valore della proprietà in modo riflessivo. Ma dal momento che lavori in un altro contesto, non sono sicuro che funzionerebbe. Ho postato il mio tentativo in modo da poter controllare –

risposta

15

Ecco un tentativo di albero di espressione. Non so ancora se questo funzionerebbe con il framework Entity, ma immagino che valga la pena provarlo.

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

chiamare in questo modo:

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

Se è possibile utilizzare un Expression<Func<T,int>> al posto di un solo un Func, quindi è sufficiente rimuovere la chiamata a Compile (e cambiare la firma di MakeGetter).


Edit: Nei commenti, TravisJ chiesto come avrebbe potuto usare in questo modo: w => "text" + w.classTypeId

Ci sono diversi modi per farlo, ma per migliorare la leggibilità mi sento di raccomandare l'introduzione di una variabile locale prima, In questo modo:

Il punto principale è che il getter è solo una funzione e che è possibile utilizzarlo esattamente come faresti normalmente.Leggi Func<T,int> come questo: int DoSomething(T instance)

+0

Sono riuscito a far funzionare questo in linqPad - ecco il codice https://gist.github.com/3108507 – Hogan

+0

@Paul - Grazie per il codice e l'esempio di lavoro. Sono stato anche in grado di farlo funzionare. Non ho cambiato la firma perché avrebbe interferito con più linq lungo la linea. Avevo una domanda, cosa dovrei fare per usare 'MakeGetter' in questo modo:' w => "testo" + MakeGetter (classTypeId) '? Simile a 'w =>" testo "+ w.classTypeId'. –

+0

@TravisJ vedi la mia modifica –

1

Non ho provato questo, e non sono sicuro se funzionerebbe , Ma potresti usare qualcosa come:

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

+0

Questo sembra interessante e almeno compila. Hai ancora più prove da fare. –

2

Ecco un metodo di estensione per voi con il mio codice di prova (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); 
    } 
} 

risultati:

results

+0

Funziona nel tuo esempio, tuttavia, qui non vedo integrazione con linq. Vedi la risposta di Paolo per un'integrazione di linq. –

+0

Suppongo che i suoi lavori su un tipo mio lavori su un oggetto ... 6 di uno. Penso che il mio sarebbe più perforazione in quanto non è necessario alcun modello. – Hogan

Problemi correlati