最近,当我开发自制的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(对于值类型对象,它实际上没有帮助).
所以我想知道我的二传手生成中出了什么问题.
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类型).
有什么想法解决它吗?
解决方法
对于这个答案,假设定义如下:
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); }