Problema de conversión con árboles de expresión

c# expression-trees

Pregunta

Tengo una función de árbol de expresión de una pregunta SO anterior. Básicamente, permite la conversión de una fila de datos en una clase específica.

Este código funciona bien, a menos que esté tratando con tipos de datos que pueden ser más grandes o más pequeños (por ejemplo, Int32 / Int64).

El código lanza una excepción de Int64 no válida cuando se pasa de un Int64 a un Int32 cuando el valor cabría en un Int32 (por ejemplo, números en el 3000).

¿Debería?

  1. ¿Intenta arreglar esto en el código? (Si es así, ¿algún puntero?)
  2. Deja el código como está.

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

Actualizar:

Ahora me he rendido y he decidido que no vale la pena. De los comentarios a continuación, llegué hasta:

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

No creo que estuviera demasiado lejos, siéntete libre de terminarlo y publica la respuesta si lo averiguas :)

Respuesta aceptada

Puede intentar solucionarlo "convertir lo seleccionado" antes de asignarlo, es decir, usar Expression.ConvertChecked en el valor en lugar de Expression.Convert .

No podría intentarlo ahora mismo, pero esto debería hacerse cargo del caso que describe ...

EDITAR - según el comentario, esto podría ser un problema de boxeo:

En este caso, puede intentar usar Expression.TypeAs o Expression.Unbox para la conversión o usar Expression.Call para llamar a un método para hacer la conversión ... se puede encontrar un ejemplo para usar Call en http://msdn.microsoft. com / en-us / library / bb349020.aspx


Respuesta popular

Lo que está intentando construir es en realidad mucho más complicado si quiere admitir el 100% de las primitivas en .NET y SQL.

Si no se preocupa por algunos de los casos de borde (tipos anulables, enumeraciones, matrices de bytes, etc.), dos consejos para obtener el 90% allí:

No use el indexador en IDataRecord, devuelve un objeto y el boxeo / unboxing matará el rendimiento. En su lugar, observe que IDataRecord tiene métodos Get [typeName]. Estos existen para todos los tipos primitivos .NET (nota: es GetFloat, no GetSingle, gran molestia).

Puede usar IDataRecord.GetFieldType para averiguar qué método Get necesita llamar para una columna dada. Una vez que tenga eso, puede usar Expression.Convert para forzar el tipo de columna DB al tipo de propiedad de destino (si son diferentes). Esto fallará en algunos de los casos de borde que enumeré anteriormente, para aquellos que necesitan lógica personalizada.



Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow