I'm having issues trying to get the value of an object out of the Expression Tree without using .Compile()
The object is quite simple.
var userModel = new UserModel { Email = "John@Doe.com"};
The method giving me issues looks like this.
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 I'm running are quite 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 );
}
I should note that changing
var val = (expression.Member as FieldInfo).GetValue((expression.Expression as ConstantExpression).Value);
to
var val = Expression.Lambda( expression ).Compile().DynamicInvoke().ToString();
will allow the test to pass, however this code needs to run on iOS, and therefore can't use .Compile()
TLDR;
Reflection is ok to use as long as you're not using
Emit
orCompile
. In the question, the value is being extracted forFieldInfo
, but it is not being extracted forPropertyInfo
. Make sure you can get BOTH.Long-winded version
So the comments pointed me in the right direction. I struggled slightly with getting the PropertyInfo, but in the end, here's what I came up with.
Essentially, if the
Member.NodeType
is aParameter
, then I'm going to use it as a SQL Field.[FieldName]
Otherwise, I'm using it as a SQL Parameter
@FieldName
... backwards I know.If the
Member.NodeType
is NOT a Parameter, then I check to see if it's either a ModelField
or a ModelProperty
. From there, I get the appropriate value, and add the Key/Value pair to a Dictionary to be used as SQL Parameters.The end result is that I build a string that looks something like
Then the parameters are passed