2015-08-31 12 views
6

Sto cercando di ottenere un valore da unottenere un valore da un ConstantExpression

var guid = Guid.Parse("SOMEGUID-GUID-GUID-GUID-SOMEGUIDGUID"); 
Expression<Func<Someobject, bool>> selector = x => x.SomeId == guid; 

Per gli scopi di registrazione ho bisogno di essere in grado di pesce che guid.

Ho provato il seguente codice, che ritengo un po 'vicino a quello che sto cercando, ma non del tutto.

BinaryExpression binaryExpression = (BinaryExpression)selector.Body; 
MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand; 
ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression; 

Ora, ConstantExpression espone un membro di 'Valore', che contiene quello che sto cercando, ma io sono un po 'perplesso come estrarre in realtà questo.

E no:

var val = (Guid)constantExpression.Value; 

non funziona :)

risolto

Il risultato finale assomiglia a:

BinaryExpression binaryExpression = (BinaryExpression)selector.Body; 
MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand; 
var myGuid = Expression.Lambda(memberExpression).Compile().DynamicInvoke(); 

Follow-up

ho fatto dei test di velocità rudementary utilizzando il codice seguente:

static void Main(string[] args) 
    { 
     var id = Guid.Parse("bleh"); 

     Expression<Func<Thingemebob, bool>> selector = x => x.Id == id; 

     var tickList = new List<long>(); 

     for (int i = 0; i < 100000; i++) 
     { 
      var sw = Stopwatch.StartNew(); 
      GetValueWithExpressionsAndReflection(selector); 
      sw.Stop(); 
      tickList.Add(sw.ElapsedTicks); 
     } 

        Trace.WriteLine("GetValueWithExpressionsAndReflection: Average over 100000, first call included: " + tickList.Average()); 
     Trace.WriteLine("GetValueWithExpressionsAndReflection: First call: " + tickList[0]); 
     Trace.WriteLine("GetValueWithExpressionsAndReflection: Average over 100000, first call excluded: " + tickList.Skip(1).Average()); 

     tickList = new List<long>(); 

     for (int i = 0; i < 100000; i++) 
     { 
      var sw = Stopwatch.StartNew(); 
      GetValueWithCompiledExpression(selector); 
      sw.Stop(); 
      tickList.Add(sw.ElapsedTicks); 
     } 

        Trace.WriteLine("GetValueWithCompiledExpression: Average over 100000, first call included: " + tickList.Average()); 
     Trace.WriteLine("GetValueWithCompiledExpression: First call: " + tickList[0]); 
     Trace.WriteLine("GetValueWithCompiledExpression: Average over 100000, first call excluded: " + tickList.Skip(1).Average()); 

     Debugger.Break(); 
    } 

    private static void GetValueWithCompiledExpression(Expression<Func<Note, bool>> selector) 
    { 
     BinaryExpression binaryExpression = (BinaryExpression)selector.Body; 
     MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand; 
     var o = Expression.Lambda(memberExpression).Compile().DynamicInvoke(); 
    } 

    private static void GetValueWithExpressionsAndReflection(Expression<Func<Note, bool>> selector) 
    { 
     BinaryExpression binaryExpression = (BinaryExpression)selector.Body; 
     MemberExpression memberExpression = (MemberExpression)((UnaryExpression)binaryExpression.Right).Operand; 
     ConstantExpression constantExpression = (ConstantExpression)memberExpression.Expression; 

     FieldInfo member = (FieldInfo)memberExpression.Member; 
     var instance = constantExpression.Value; 
     var guid = member.GetValue(instance); 
    } 

Attiva la versione di compilazione è molto più lento. Stiamo osservando un'enorme differenza. (Timing è in tick):

GetValueWithExpressionsAndReflection: Media su 100000, prima chiamata comprese 0,93122

GetValueWithExpressionsAndReflection: Primo bando: 851

GetValueWithExpressionsAndReflection: Media su 100000, prima chiamata esclusi: 0,922719227192272

Versus:

GetValueWithCompiledExpression: Media su 100000, prima chiamata incluso: 499,53669

GetValueWithCompiledExpression: Prima convocazione: 16818

GetValueWithCompiledExpression: Media su 100000, prima chiamata esclusa: 499,373503735037

Test di livello o no: senza dubbio userò la versione di riflessione. miei risultati sembrano essere coerenti con: http://www.minddriven.de/index.php/technology/dot-net/c-sharp/efficient-expression-values

+0

Dal tuo primo frammento di codice sembra che tu non abbia 'Guid' ma' stringa', se non sei _ sicuro del suo formato puoi anche solo usare i metodi di classe 'Convertitore'. –

+0

Qual è il valore di constantExpression.Value.GetType() e cosa ti dice? – usr

+0

@AdrianoRepetti Sono molto sicuro del formato, ho aggiornato la domanda per riflettere questo più correttamente. EFAndExpressions.Program + <> c__DisplayClass0, che al momento non mi dice nulla :) – Apeiron

risposta

2

tua espressione const è di tipo EFAndExpressions.Program+<>c__DisplayClass0. Ciò significa che l'espressione ha la seguente struttura:

var compilerGeneratedClass = new compilerGeneratedClass() { 
    guid = Guid.Parse("SOMEGUID-GUID-GUID-GUID-SOMEGUIDGUID"); }; 
Expression<Func<Someobject, bool>> selector = x => x.SomeId == compilerGeneratedClass.guid; 

Il compilatore fa per voi. Usa un decompilatore per controllare i dettagli.

Ora sai come appare l'albero delle espressioni e puoi scomporlo. È necessario utilizzare la riflessione per ottenere il valore di runtime del campo o della proprietà compilerGeneratedClass.guid.

Questo valore è non parte dell'albero delle espressioni direttamente.

+0

ok, quindi ho bisogno di capire il valore utilizzando Reflection/FieldInfo ecc – Apeiron

+0

Risolto: vedere modifica. – Apeiron

+0

@Apeiron che il codice funziona ma è molto lento grazie alla chiamata a Compile(). Migliorare questo profilo prima che vada in produzione. Inoltre, potrebbe non scalare affatto se il JIT è a thread singolo che non conosco. – usr