如何在没有.Compile()的情况下从MemberExpression获取属性值?

我试图在不使用.Compile()的情况下从表达式树中获取对象的值时遇到问题

对象很简单。

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

给我问题的方法看起来像这样。

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

我正在运行的测试非常简单

 [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 = "john@doe.com"}; 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 ); } 

我应该注意到改变

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

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

将允许测试通过,但是此代码需要在iOS上运行,因此无法使用.Compile()

TLDR;
只要您不使用EmitCompile就可以使用reflection。 在该问题中,正在为FieldInfo提取值,但不会为PropertyInfo提取它。 确保你可以同时获得。

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

冗长的版本

所以评论指出了我正确的方向。 获得PropertyInfo后我有点挣扎,但最后,这就是我想出的。

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

本质上,如果Member.NodeType是一个Parameter ,那么我将把它用作SQL字段。 [FieldName]

否则,我正在使用它作为SQL参数@FieldName …向后我知道。

如果Member.NodeType不是参数,那么我检查它是模型Field还是模型Property 。 从那里,我得到适当的值,并将键/值对添加到字典以用作SQL参数。

最终结果是我构建了一个看起来像的字符串

 SELECT * FROM TableName WHERE [FieldName] = @FieldName 

然后传递参数

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