How to get property value from MemberExpression without .Compile ()?

I'm having trouble trying to get the value of an object from an expression tree without using .Compile ()

The object is pretty simple.

var userModel = new UserModel { Email = " John@Doe.com "}; 

The method giving me problems is as follows.

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

The tests that I run are pretty simple.

 [Test] // PASS public void ShouldVisitExpressionByGuidObject () { // Setup var id = new Guid( "CCAF57D9-88A4-4DCD-87C7-DB875E0D4E66" ); const string expectedString = "[Id] = @Id"; var expectedParameters = new Dictionary<string, object> { { "@Id", id } }; // Execute var actualExpression = TestExpression<UserModel>( 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 = " john@doe.com "}; const string expectedString = "[Email] = @Email"; var expectedParameters = new Dictionary<string, object> { { "@Email", expectedUser.Email } }; // Execute var actualExpression = TestExpression<UserModel>( u => u.Email == expectedUser.Email ); var actualParameters = actualExpression.Parameters; var actualString = actualExpression.WhereExpression; // Assert Assert.AreEqual( expectedString, actualString ); CollectionAssert.AreEquivalent( expectedParameters, actualParameters ); } 

It should be noted that the change

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

to

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

will allow you to pass the test, however this code needs to be run on iOS and therefore cannot use .Compile()

+6
source share
1 answer

TL; DR;
Reflection is fine if you are not using Emit or Compile . In the question, the value is retrieved for FieldInfo , but it is not retrieved for PropertyInfo . Make sure you can get BOTH.

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

Long term version

So the comments pointed me in the right direction. I worked a bit with getting PropertyInfo, but in the end, here's what I came up with.

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

Essentially, if Member.NodeType is a Parameter , I will use it as an SQL field. [FieldName]

Otherwise, I use it as an SQL parameter @FieldName ... back, I know.

If Member.NodeType NOT a parameter, then I check to see if it is either a Model Field or a Model Property . From there I get the appropriate value and add a key / value pair to the dictionary, which will be used as SQL parameters.

As a result, I create a line that looks something like

 SELECT * FROM TableName WHERE [FieldName] = @FieldName 

Then the parameters are passed

 var parameters = new Dictionary<string, object> Parameters; parameters.Add("@FieldName", "The value of the field"); 
+7
source

Source: https://habr.com/ru/post/957156/


All Articles