最近,当我开发自制的SQLite ORM时,我遇到了一些关于使用Expression Trees进行拳击的麻烦。我还在编码C#3.5。
总而言之,我将使用这个简单的类定义:
[Table]
public class Michelle
{
[Column(true), PrimaryKey]
public UInt32 A { get; set; }
[Column]
public String B { get; set; }
}
所以从POCO类定义中我实例化了一个新对象,我的ORM引擎将该对象转换为记录并正确插入。现在的诀窍是,当我从SQLite中获取值时,我得到了Int64
。
我认为我的委托制定者是好的,因为它是Action<Object, Object>
,但我仍然得到InvalidCastException
。似乎尝试将Object
(参数,Int64
)强制转换为UInt32
。不幸的是它不起作用。我尝试添加一些Expression.Constant
和Expression.TypeAs
(这对值类型对象没有帮助)。
所以我想知道我的二传手生成中出了什么问题。
以下是我的setter生成方法:
public static Action<Object, Object> GenerateSetter(PropertyInfo propertyInfo)
{
if (!FactoryFastProperties.CacheSetters.ContainsKey(propertyInfo))
{
MethodInfo methodInfoSetter = propertyInfo.GetSetMethod();
ParameterExpression parameterExpressionInstance = Expression.Parameter(FactoryFastProperties.TypeObject, "Instance");
ParameterExpression parameterExpressionValue = Expression.Parameter(FactoryFastProperties.TypeObject, "Value");
UnaryExpression unaryExpressionInstance = Expression.Convert(parameterExpressionInstance, propertyInfo.DeclaringType);
UnaryExpression unaryExpressionValue = Expression.Convert(parameterExpressionValue, propertyInfo.PropertyType);
MethodCallExpression methodCallExpression = Expression.Call(unaryExpressionInstance, methodInfoSetter, unaryExpressionValue);
Expression<Action<Object, Object>> expressionActionObjectObject = Expression.Lambda<Action<Object, Object>>(methodCallExpression, new ParameterExpression[] { parameterExpressionInstance, parameterExpressionValue });
FactoryFastProperties.CacheSetters.Add(propertyInfo, expressionActionObjectObject.Compile());
}
return FactoryFastProperties.CacheSetters[propertyInfo];
}
所以基本上:
// Considering setter as something returned by the generator described above
// So:
// That one works!
setter(instance, 32u);
// This one... hm not really =/
setter(instance, 64);
我应该添加另一个Expression.Convert
,但我真的不知道如何使它工作。因为已经有人应该(尝试)从任何Object
转换为属性类型(在我的示例中为UInt32
类型)。
有什么想法解决这个问题吗?
答案 0 :(得分:3)
对于这个答案,假设定义了以下内容:
object myColValueFromTheDatabase = (object)64L;
Expression.Convert
静态确定如何执行转换。就像C#一样。如果您编写(uint)myColValueFromTheDatabase
,这将无法在运行时成功,因为取消装箱只是不起作用。 Expression.Convert
也进行了简单的拆箱尝试。这就是它失败的原因。
您需要执行以下任一操作:
(uint)(long)myColValueFromTheDatabase
Convert.ToUInt32(myColValueFromTheDatabase)
在情况(1)中,您需要首先取消装箱到完全匹配类型,然后更改位。案例(2)使用一些辅助方法解决了这个问题。案例(1)更快。
要使用表达式API执行此操作,请插入另一个Expression.Convert
。
这应该让你开始:
public static T LogValue<T>(T val)
{
Console.WriteLine(val.GetType().Name + ": " + val);
return val;
}
static void Main(string[] args)
{
Expression myColValueFromTheDatabase = Expression.Convert(Expression.Constant(1234L), typeof(object));
myColValueFromTheDatabase = Expression.Call(typeof(Program), "LogValue", new[] { myColValueFromTheDatabase.Type }, myColValueFromTheDatabase); //log
Expression unboxed = Expression.Convert(myColValueFromTheDatabase, typeof(long));
Expression converted = Expression.Convert(unboxed, typeof(uint));
var result = Expression.Lambda<Func<uint>>(converted).Compile()();
Console.WriteLine(result);
}