2011-09-05 2 views
3

У меня есть функция дерева выражений из предыдущего вопроса SO. Это в основном позволяет преобразовать строку данных в определенный класс.Проблема преобразования с деревьями выражений

Этот код отлично работает, если только вы не имеете дело с типами данных, которые могут быть больше или меньше (например, Int32/Int64).

Код генерирует недопустимое исключение литых при переходе от Int64 к Int32, когда значение будет соответствовать Int32 (например, номера в 3000).

Должен ли я?

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

Update:

Я теперь отказался и решил, что не стоит. Из приведенных ниже замечаний, я дошел до:

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

Я не думаю, что я был слишком далеко, не стесняйтесь, чтобы закончить его и отправить ответ, если вы понять это :)

+0

Я не думаю, что вы действительно должны беспокоиться об этом. Если вы хотите использовать эту функцию для интегральных типов, полагайтесь на класс 'System.Convert'. См. [Аналогичный вопрос] (http: // stackoverflow.com/questions/19841120/listt-property-binding-to-dbdatareader-issue? lq = 1) здесь для более гибкого решения lil. – nawfal

ответ

3

вас может попытаться исправить это путем «конвертировать проверенный» перед назначением, используя Expression.ConvertChecked по значению вместо Expression.Convert.

Не удалось попробовать прямо сейчас, но это должно заботиться о случае вы описываете ...

EDIT - в соответствии с комментарием это может быть проблемой бокса:

В этом случае вы можете попробуйте использовать Expression.TypeAs или Expression.Unbox для преобразования или использования Expression.Call для вызова метода, чтобы сделать преобразование ... пример использования Call можно найти на http://msdn.microsoft.com/en-us/library/bb349020.aspx

+1

Я не думаю, что это проблема с переполнением и т. Д. Это больше похоже на проблему с распаковкой: ящик 'Int64' не может быть распакован как' Int32' и т. Д .; значения в коробке должны быть распакованы точно таким же типом. – LukeH

+0

К сожалению, это не сработало. Я попробовал '.TypeAs'. У меня такое чувство, что мне нужно будет добавить в него больше чеков. Это не конец света, но я хотел бы сохранить это как можно быстрее. –

+0

@ LukeH спасибо ... добавил вопрос о боксе как EDIT ... – Yahia

1

Что вы пытаетесь построить на самом деле гораздо сложнее, если вы хотите поддерживать 100% примитивов в .NET и SQL.

Если вы не заботитесь о некоторых случаях края (NULLABLE типы, Перечисления, массивы байтов и т.д.), две подсказки, чтобы получить Вас 90% там:

Не использовать индексацию на IDataRecord, он возвращает объект, и бокс/unboxing убьет производительность. Вместо этого обратите внимание, что IDataRecord имеет методы Get [typeName]. Они существуют для всех примитивных типов .NET (обратите внимание: это GetFloat, а не GetSingle, огромное раздражение).

Вы можете использовать IDataRecord.GetFieldType, чтобы выяснить, какой метод Get необходимо вызвать для данного столбца. После этого вы можете использовать Expression.Convert, чтобы принудить тип столбца DB к типу целевого объекта (если они разные). Это не удастся для некоторых из крайних случаев, перечисленных выше, для тех, кому нужна специальная логика.

+0

+1 для информации о методах 'Get [typeName]', действительно, они быстрее, но в реальном мире вы не можете их использовать, учитывая, что он не обрабатывает DbNulls, поэтому общая обработка совершенно невозможна. – nawfal

+0

Вы можете использовать IDataRecord.IsDBNull, чтобы выяснить, является ли столбец для текущей строки нулевым или нет, прежде чем вы его прочитаете. – Steve

+0

Стив, в этом случае его производительность становится похожей на другие перегрузки, более или менее. – nawfal

Смежные вопросы