如何从MemberExpression得到属性值不.Compile()? [英] How to get Property Value from MemberExpression without .Compile()?
问题描述
我在试图让一个对象的值了表达式树的问题,而没有使用.Compile()
对象是相当简单的。
VAR的usermodel =新的usermodel {EMAIL =John@Doe.com};
给我的问题的方法是这样的。
私人无效VisitMemberAccess(MemberExpression表达,MemberExpression左)
{
VAR键=留下!= NULL? left.Member.Name:expression.Member.Name;
如果(expression.Expression.NodeType.ToString()==参数)
{
//添加字符串键
_strings.Add(的String.Format([ {0}]键));
}
,否则
{
//添加字符串参数
_strings.Add(的String.Format(@ {0},键));
//潜在的NullReferenceException
VAR VAL =(expression.Member的字段信息).GetValue((expression.Expression为常量表达式)。价值);
//添加参数值
Parameters.Add(@+键,VAL);
}
}
我跑这些测试很简单
[测试] // PASS
公共无效ShouldVisitExpressionByGuidObject()
{
//设置
VAR ID =新的GUID(CCAF57D9-88A4-4DCD-87C7-DB875E0D4E66);
常量字符串expectedString =[ID] = @Id;
变种expectedParameters =新词典<字符串对象> {{@Id,ID}};
//执行
VAR actualExpression = testexpression与<&的usermodel GT;(U => u.Id == ID);
VAR actualParameters = actualExpression.Parameters;
VAR actualString = actualExpression.WhereExpression;
//测试
Assert.AreEqual(expectedString,actualString);
CollectionAssert.AreEquivalent(expectedParameters,actualParameters);
}
[测试] // FAIL [System.NullReferenceException:未将对象引用设置到对象的实例]
公共无效ShouldVisitExpressionByStringObject()
{
//设置
变种expectedUser =新的usermodel {EMAIL =john@doe.com};
常量字符串expectedString =[邮件] = @Email;
变种expectedParameters =新词典<字符串对象> {{@Email,expectedUser.Email}};
//执行
VAR actualExpression = testexpression与<&的usermodel GT;(U => u.Email == expectedUser.Email);
VAR actualParameters = actualExpression.Parameters;
VAR actualString = actualExpression.WhereExpression;
//断言
Assert.AreEqual(expectedString,actualString);
CollectionAssert.AreEquivalent(expectedParameters,actualParameters);
}
我要指出,改变
VAR VAL =(expression.Member的字段信息).GetValue((expression.Expression为常量表达式)。价值);
到
<$ 。C $ C> VAR VAL = Expression.Lambda(表达).Compile()DynamicInvoke()的ToString();
将允许测试通过,然而,这代码需要iOS上运行,并且因此不能使用 .Compile()
的 TLDR ; 的结果
反思是好的,只要使用,你使用的不是的Emit
或编译
。在的问题,被提取的值字段信息
,但它没有被提取的PropertyInfo
。确保你可以同时获得。
IF((expression.Member为的PropertyInfo)!= NULL)
$ { b $ b //从属性
}
,否则的值,如果((expression.Member的字段信息)!= NULL)
{
//获取从外地
值}
,否则
{
抛出新InvalidMemberException();
}
的 <强>啰嗦版的
所以评论中指出我朝着正确的方向。我得到的PropertyInfo稍有挣扎,但最终,这里就是我想出了。
私人无效VisitMemberAccess(MemberExpression表达,MemberExpression左)
{
//为了保护案例键/值对之间,我们总是希望使用表达式的左侧。
//因此,如果不为空,则表达实际上是离开了。
//这样做可以确保我们的'key`参数名和数据库字段之间的匹配
VAR键=留下!= NULL? left.Member.Name:expression.Member.Name;
//如果是的NodeType一个`Parameter`,我们要添加的密钥作为数据库字段名称我们的字符串集合
//否则,我们要添加的密钥作为DB参数我们的字符串集合
如果(expression.Expression.NodeType.ToString()==参数)
{
_strings.Add(的String.Format([{0}] 键));
}
,否则
{
_strings.Add(的String.Format(@ {0},键));
//如果键被添加为DB参数,那么我们也必须参数键/值对添加到集合
//因为我们正在关闭模型这应该只包含属性或字段,
//对象应该只有两种选择。的PropertyInfo或字段信息......让我们来提取相应价值
VAR值=新的对象();
如果((expression.Member为的PropertyInfo)!= NULL)
{
VAR EXP =(MemberExpression)expression.Expression;
VAR常数=(常量表达式)exp.Expression;
VAR fieldInfoValue =((字段信息)exp.Member).GetValue(constant.Value);
值=((的PropertyInfo)expression.Member).GetValue(fieldInfoValue,NULL);
}
,否则如果((expression.Member的字段信息)!= NULL)
{
变种的FieldInfo = expression.Member的字段信息;
VAR常量表达式= expression.Expression为常量表达式;
如果(字段信息= NULL&安培;!常量表达式= NULL)
{
值= fieldInfo.GetValue(constantExpression.Value);
}
}
,否则
{
抛出新InvalidMemberException();
}
//添加参数键/值对。
Parameters.Add(@+键,值);
}
}
从本质上讲,如果会员.NodeType
是参数
,那么我会使用它作为一个SQL场。 [字段名]
否则,我使用它作为一个SQL参数 @FieldName
...向后我知道了。
如果在 Member.NodeType
不是参数,然后我检查,看看它要么模型字段
或模型属性
。从那里,我得到了适当的值,键/值对添加到字典用作SQL参数。
最终的结果是,我建立一个字符串这看起来像
<预类=郎-SQL prettyprint-覆盖>
SELECT * FROM表名WHERE
[字段名] = @FieldName
然后参数传递
VAR参数=新词典与LT;字符串对象>参数;
parameters.Add(@字段名,字段的值);
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
or Compile
. In the question, the value is being extracted for FieldInfo
, but it is not being extracted 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-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.
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);
}
}
Essentially, if the Member.NodeType
is a Parameter
, 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 Model Field
or a Model Property
. 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
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");
这篇关于如何从MemberExpression得到属性值不.Compile()?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!