Come ottenere il valore della proprietà da MemberExpression senza .Compile ()?

Sto riscontrando problemi nel tentativo di estrarre il valore di un object dall’albero delle espressioni senza utilizzare .Compile ()

L’object è abbastanza semplice.

var userModel = new UserModel { Email = "[email protected]"}; 

Il metodo che mi dà problemi assomiglia a questo.

 private void VisitMemberAccess(MemberExpression expression, MemberExpression left) { var key = left != null ? left.Member.Name : expression.Member.Name; if (expression.Expression.NodeType.ToString() == "Parameter") { // add the string key _strings.Add(string.Format("[{0}]", key)); } else { // add the string parameter _strings.Add(string.Format("@{0}", key)); // Potential NullReferenceException var val = (expression.Member as FieldInfo).GetValue((expression.Expression as ConstantExpression).Value); // add parameter value Parameters.Add("@" + key, val); } } 

I test che sto eseguendo sono abbastanza semplici

 [Test] // PASS public void ShouldVisitExpressionByGuidObject () { // Setup var id = new Guid( "CCAF57D9-88A4-4DCD-87C7-DB875E0D4E66" ); const string expectedString = "[Id] = @Id"; var expectedParameters = new Dictionary { { "@Id", id } }; // Execute var actualExpression = TestExpression( u => u.Id == id ); var actualParameters = actualExpression.Parameters; var actualString = actualExpression.WhereExpression; // Test Assert.AreEqual( expectedString, actualString ); CollectionAssert.AreEquivalent( expectedParameters, actualParameters ); } 
 [Test] // FAIL [System.NullReferenceException : Object reference not set to an instance of an object.] public void ShouldVisitExpressionByStringObject () { // Setup var expectedUser = new UserModel {Email = "[email protected]"}; const string expectedString = "[Email] = @Email"; var expectedParameters = new Dictionary { { "@Email", expectedUser.Email } }; // Execute var actualExpression = TestExpression( u => u.Email == expectedUser.Email ); var actualParameters = actualExpression.Parameters; var actualString = actualExpression.WhereExpression; // Assert Assert.AreEqual( expectedString, actualString ); CollectionAssert.AreEquivalent( expectedParameters, actualParameters ); } 

Dovrei notare che cambiando

 var val = (expression.Member as FieldInfo).GetValue((expression.Expression as ConstantExpression).Value); 

a

 var val = Expression.Lambda( expression ).Compile().DynamicInvoke().ToString(); 

consentirà il passaggio del test, tuttavia questo codice deve essere eseguito su iOS e, quindi, non può essere utilizzato .Compile()

TLDR;
Reflection è ok da usare finché non usi Emit o Compile . Nella domanda, il valore viene estratto per FieldInfo , ma non viene estratto per PropertyInfo . Assicurati di poter ottenere ENTRAMBI.

 if ((expression.Member as PropertyInfo) != null) { // get the value from the PROPERTY } else if ((expression.Member as FieldInfo) != null) { // get the value from the FIELD } else { throw new InvalidMemberException(); } 

Versione prolissa

Quindi i commenti mi hanno indirizzato nella giusta direzione. Ho faticato leggermente a ottenere il PropertyInfo, ma alla fine, ecco cosa mi è venuto in mente.

 private void VisitMemberAccess(MemberExpression expression, MemberExpression left) { // To preserve Case between key/value pairs, we always want to use the LEFT side of the expression. // therefore, if left is null, then expression is actually left. // Doing this ensures that our `key` matches between parameter names and database fields var key = left != null ? left.Member.Name : expression.Member.Name; // If the NodeType is a `Parameter`, we want to add the key as a DB Field name to our string collection // Otherwise, we want to add the key as a DB Parameter to our string collection if (expression.Expression.NodeType.ToString() == "Parameter") { _strings.Add(string.Format("[{0}]", key)); } else { _strings.Add(string.Format("@{0}", key)); // If the key is being added as a DB Parameter, then we have to also add the Parameter key/value pair to the collection // Because we're working off of Model Objects that should only contain Properties or Fields, // there should only be two options. PropertyInfo or FieldInfo... let's extract the VALUE accordingly var value = new object(); if ((expression.Member as PropertyInfo) != null) { var exp = (MemberExpression) expression.Expression; var constant = (ConstantExpression) exp.Expression; var fieldInfoValue = ((FieldInfo) exp.Member).GetValue(constant.Value); value = ((PropertyInfo) expression.Member).GetValue(fieldInfoValue, null); } else if ((expression.Member as FieldInfo) != null) { var fieldInfo = expression.Member as FieldInfo; var constantExpression = expression.Expression as ConstantExpression; if (fieldInfo != null & constantExpression != null) { value = fieldInfo.GetValue(constantExpression.Value); } } else { throw new InvalidMemberException(); } // Add the Parameter Key/Value pair. Parameters.Add("@" + key, value); } } 

In sostanza, se Member.NodeType è un Parameter , verrà utilizzato come campo SQL. [FieldName]

Altrimenti, lo sto usando come parametro SQL @FieldName … all’indietro lo so.

Se Member.NodeType NON è un parametro, quindi controllo se è un Field modello o una Property modello. Da lì, ottengo il valore appropriato e aggiungo la coppia chiave / valore a un dizionario da utilizzare come parametri SQL.

Il risultato finale è che costruisco una stringa che assomigli a qualcosa

 SELECT * FROM TableName WHERE [FieldName] = @FieldName 

Quindi i parametri vengono passati

 var parameters = new Dictionary Parameters; parameters.Add("@FieldName", "The value of the field");