Назначьте общие свойства классов с деревьями выражений - PullRequest
1 голос
/ 04 августа 2011

Помогите, мне показалось, что я несколько сбился с пути.

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

  // Gets the property type
  ParameterExpression paramProperty = Expression.Parameter(property.PropertyType);
  // Gets the value from row[0] (SqlDataReader)
  ParameterExpression paramValue = Expression.Parameter(row[0].GetType());
  // really no clue, makes a property so to speak?
  MemberExpression prop = Expression.Property(paramProperty, property);
  // assigns the property the value from the SqlDataReader
  BinaryExpression assign = Expression.Assign(prop, paramValue);
  // adds to an expression list ready for compilation
  exps.Add(assign);
  // allows things to be executed sequentially?
  BlockExpression blockExpression = exps.Count > 0 ? Expression.Block(exps) : Expression.Block(Expression.Empty());
  // create the parameter array
  List<ParameterExpression> paramArr = new List<ParameterExpression>();
  paramArr.Add(paramProperty);
  paramArr.Add(paramValue);
  // get a lambda so I can compile this for re-use
  Expression<Action<T>> lamExp = Expression.Lambda<Action<T>>(blockExpression, paramArr);

Прежде всего, мои комментарии верны?Я складываю эту информацию в старом добром стиле из учебника и документации MSDN.

Из фрагментов, которые я читал, я думаю Я должен был скомпилировать это,сохранить его в словаре с типом в качестве ключа и вызвать его, когда мне нужно.Например,

  ConcurrentDictionary<Type, ??> ExpressionCache;
  if(ExpressionCache.ContainsKey(typeof(T))
  {
         // property is the variable of a foreach loop of type PropertyInfo
         ExpressionCache[typeof(T)](property); 
  } // else do the first piece of code...

Итак, в итоге,

  1. Я иду по правильному пути?
  2. Верны ли мои комментарии о выражениях?
  3. Какой тип я должен использовать для параллельного словаря?
  4. Когда я кеширую ссылку, как я передаю различные параметры лямбда-выражению?

Любые улучшения или предложения приветствуются какПока это хорошо объяснено.Я пытаюсь понять, как это работает, а не просто заставить его работать:)

1 Ответ

3 голосов
/ 04 августа 2011

Звучит так, как будто вы ищете комментарии больше, чем что-либо еще ... верно?

Прежде всего, как у вас будет SqlReader во время компиляции?

// Gets the value from row[0] (SqlDataReader)
ParameterExpression paramValue = Expression.Parameter(Type.GetTypeFromHandle(row[0].GetType()));

Ваша лямбда принимает два параметра, хотя ... так что что-то не так (Действие принимает один параметр).

Я думаю, что вы действительно хотите это Func<SqlDataReader, T>.Таким образом, вы даете ему свой SqlDataReader, и он выдает T. Итак:

var list = new List<T>();
// get a SqlDataReader
while (reader.Read())
{
    Func<SqlDataReader, T> readRow = GetReader<T>();
    list.Add(readRow(reader);
}

Я бы сохранил ваш кеш как

 ConcurrentDictionary<Type, Delegate> ExpressionCache;  // can't use T here since each Func will have a different T

Затем приведу его к соответствующему типу делегатавызывающий при извлечении (пусть ваш метод принимает универсальный параметр:

 public Func<SqlDataReader, T> GetReader<T>() 
 { 
     Delegate d;
     if(!ExpressionCache.TryGetValue(typeof(T), out d)
     {
         ExpressionCache[typeof(T)] = d = // build and compile lambda
     } 
     // cast to strong typed delegate...we don't want to have to DynamicInvoke...that's slow
     return (Func<SqlDataReader, T>)d;
 }

Итак ... что касается вашего лямбда-компилятора / компилятора:

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

        /* so we end up with
         * T Read(SqlDataReader row)
         * {
         * var x = new T();
         * x.Prop1 = (cast)row["Prop1"]
         * x.Prop2 = (cast)row["Prop2"]
         * x.Prop3 = (cast)row["Prop3"]
         * x.Prop4 = (cast)row["Prop4"]
         * etc.
         * return x
         * }
         */
        var lambda = Expression.Lambda<Func<SqlDataReader, T>>(body, sqlDataReaderParameter);

        // cache me!
        return lambda.Compile();

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

Я не уверен, правильно ли я понял ваше предполагаемое использование ... это так?

...