表达式树的转换问题

本文关键字:问题 转换 表达式 | 更新日期: 2023-09-27 18:07:45

我有一个来自前一个SO问题的表达式树函数。它基本上允许将数据行转换为特定的类。

这段代码工作得很好,除非你处理的数据类型可以更大或更小(例如:Int32/Int64)。

当从Int64转到Int32时,代码抛出一个无效强制转换异常,而该值适合Int32(例如。

我应该吗?

  1. 尝试在代码中修复此问题?(如果有,有什么提示吗?)
  2. 代码保持原样

    private Func<SqlDataReader, T> getExpressionDelegate<T>()
    {
        // hang on to row[string] property 
        var indexerProperty = typeof(SqlDataReader).GetProperty("Item", new[] { typeof(string) });
        // list of statements in our dynamic method
        var statements = new List<Expression>();
        // store instance for setting of properties
        ParameterExpression instanceParameter = Expression.Variable(typeof(T));
        ParameterExpression sqlDataReaderParameter = Expression.Parameter(typeof(SqlDataReader));
        // create and assign new T to variable: var instance = new T();
        BinaryExpression createInstance = Expression.Assign(instanceParameter, Expression.New(typeof(T)));
        statements.Add(createInstance);
        foreach (var property in typeof(T).GetProperties())
        {
            // instance.MyProperty
            MemberExpression getProperty = Expression.Property(instanceParameter, property);
            // row[property] -- NOTE: this assumes column names are the same as PropertyInfo names on T
            IndexExpression readValue = Expression.MakeIndex(sqlDataReaderParameter, indexerProperty, new[] { Expression.Constant(property.Name) });
            // instance.MyProperty = row[property]
            BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType));
            statements.Add(assignProperty);
        }
        var returnStatement = instanceParameter;
        statements.Add(returnStatement);
        var body = Expression.Block(instanceParameter.Type, new[] { instanceParameter }, statements.ToArray());
        var lambda = Expression.Lambda<Func<SqlDataReader, T>>(body, sqlDataReaderParameter);
        // cache me!
        return lambda.Compile();
    }
    

更新:

我现在已经放弃了,觉得不值得。从下面的评论中,我得到了:

            if (readValue.Type != property.PropertyType)
            {
                BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(Expression.Call(property.PropertyType, "Parse", null, new Expression[] { Expression.ConvertChecked(readValue, typeof(string)) }), property.PropertyType));
                statements.Add(assignProperty);
            }
            else
            {
                // instance.MyProperty = row[property]
                BinaryExpression assignProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType));
                statements.Add(assignProperty);
            }

我不认为我做得太远了,如果你想明白了,请随意完成并发布答案:)

表达式树的转换问题

您可以尝试在分配之前通过"转换检查"来修复它,即使用Expression.ConvertChecked而不是Expression.Convert

现在无法尝试,但这应该会照顾到你描述的情况…

编辑-根据评论,这可能是一个装箱问题:

在这种情况下,您可以尝试使用Expression.TypeAsExpression.Unbox进行转换或使用Expression.Call调用方法来进行转换…可以在http://msdn.microsoft.com/en-us/library/bb349020.aspx

找到使用Call的示例。

如果你想要支持。net和SQL中100%的原语,那么你试图构建的东西实际上要复杂得多。

如果你不关心一些边缘情况(可空类型,枚举,字节数组等),两个技巧可以让你达到90%:

不要在IDataRecord上使用索引器,它返回一个对象,并且装箱/拆箱会降低性能。相反,请注意IDataRecord上有Get[typeName]方法。这些存在于所有。net基本类型(注意:它是GetFloat,而不是GetSingle,非常烦人)。

你可以使用IDataRecord。GetFieldType来确定需要为给定列调用哪个Get方法。一旦你有了这些,你就可以使用Expression了。转换将DB列类型强制转换为目标属性的类型(如果它们不同)。对于我上面列出的一些边缘情况,对于那些需要自定义逻辑的情况,这将失败。