表达式树的转换问题

发布于 2024-12-02 23:24:11 字数 2580 浏览 0 评论 0 原文

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

除非您正在处理可以更大或更小的数据类型(例如 Int32/Int64),否则此代码可以正常工作。

当值适合 Int32 时(例如, 3000)。

我应该吗?

  1. 尝试在代码中解决这个问题? (如果是这样,有什么指示吗?)
  2. 保持代码不变。

    private Func; getExpressionDelegate()
    {
        // 保留 row[string] 属性 
        var indexerProperty = typeof(SqlDataReader).GetProperty("Item", new[] { typeof(string) });
    
        // 动态方法中的语句列表
        var 语句 = new List<表达式>();
    
        // 存储属性设置的实例
        ParameterExpression实例参数 = Expression.Variable(typeof(T));
        ParameterExpression sqlDataReaderParameter = Expression.Parameter(typeof(SqlDataReader));
    
        // 创建新 T 并将其分配给变量: var instance = new T();
        BinaryExpression createInstance = Expression.Assign(instanceParameter, Expression.New(typeof(T)));
        语句.Add(createInstance);
    
        foreach(typeof(T).GetProperties() 中的 var 属性)
        {
    
            // 实例.MyProperty
            MemberExpression getProperty = Expression.Property(instanceParameter, property);
    
            // row[property] -- 注意:这假设列名称与 T 上的 PropertyInfo 名称相同
            IndexExpression readValue = Expression.MakeIndex(sqlDataReaderParameter, indexerProperty, new[] { Expression.Constant(property.Name) });
    
            // 实例.MyProperty = 行[属性]
            BinaryExpression allocateProperty = Expression.Assign(getProperty, Expression.Convert(readValue, property.PropertyType));
            语句。添加(分配属性);
        }
    
        var returnStatement = 实例参数;
        语句.Add(returnStatement);
    
        var body = Expression.Block(instanceParameter.Type, new[] { instanceParameter }, statements.ToArray());
    
        var lambda = Expression.Lambda>(body, sqlDataReaderParameter);
    
        // 缓存我!
        返回 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);
            }

我不认为我离得太远,请随意完成它并发布答案,如果你弄清楚了:)

I have an expression tree function from a previous SO question. It basically allows the conversion of a data row into a specific class.

This code works fine, unless you're dealing with data types that can be bigger or smaller (eg. Int32/Int64).

The code throws an invalid cast exception when going from an Int64 to an Int32 when the value would fit in an Int32 (eg. numbers in the 3000).

Should I?

  1. Attempt to fix this in the code? (If so, any pointers?)
  2. Leave the code as it is.

    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();
    }
    

Update:

I have now given up and decided it is not worth it. From the comments below, I got as far as:

            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);
            }

I don't think I was too far off, feel free to finish it and post the answer if you figure it out :)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

江湖正好 2024-12-09 23:24:12

您可以尝试在使用 Expression 分配 ie 之前通过“转换选中”来修复它.ConvertChecked 值而不是 Expression.Convert

现在无法尝试,但这应该可以解决您描述的情况...

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

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

You could try to fix it by "convert checked" before assigning i.e. using Expression.ConvertChecked on the value instead of Expression.Convert .

Couldn't try it right now but this should take care of the case you describe...

EDIT - as per comment this could be a boxing issue:

In this case you could try using Expression.TypeAs or Expression.Unbox for the conversion or use Expression.Call for calling a method to do the conversion... an example for using Call can be found at http://msdn.microsoft.com/en-us/library/bb349020.aspx

红尘作伴 2024-12-09 23:24:12

如果您想支持 .NET 和 SQL 中 100% 的原语,那么您尝试构建的内容实际上要复杂得多。

如果您不关心一些边缘情况(可以为 null 的类型、枚举、字节数组等),有两个技巧可以让您做到 90%:

不要在 IDataRecord 上使用索引器,它返回一个对象和装箱/拆箱会降低性能。相反,请注意 IDataRecord 具有 Get[typeName] 方法。这些对于所有 .NET 基元类型都存在(注意:它是 GetFloat,而不是 GetSingle,这是一个巨大的烦恼)。

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

What you're trying to build is actually much more complicated if you want to support 100% of the primitives in .NET and SQL.

If you don't care about some of the edge cases (nullable types, enums, byte arrays, etc), two tips to get you 90% there:

Don't use the indexer on IDataRecord, it returns an object and the boxing/unboxing will kill performance. Instead, notice that IDataRecord has Get[typeName] methods on it. These exist for all .NET primitive types (note: it's GetFloat, not GetSingle, huge annoyance).

You can use IDataRecord.GetFieldType to figure out which Get method you need to call for a given column. Once you have that, you can use Expression.Convert to coerce the DB column type to the target property's type (if they're different). This will fail for some of the edge cases I listed above, for those you need custom logic.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文