C # динамическое присваивание свойств в цикле - PullRequest
1 голос
/ 04 марта 2010

Я не совсем понял / увидел то, на что надеялся и, возможно, пропустил, так что я все равно спрошу снова. У меня есть класс с полями на нем. У меня есть таблица данных, из которой я запрашиваю те же столбцы и соответствующие типы данных. У меня есть строковый массив всех полей, которые меня беспокоят, и их конкретный порядок.

То, что я хочу сделать, - это циклически проходить по каждому элементу в массиве и помещать значение из строки данных набора результатов поиска в таблицу и заполнять его в поле структуры класса. Что-то вроде псевдокода ниже

String WorkWithThese[] = { "MyFld1", "AnotherFld", "ThirdFld" };
DataTable oTbl = GetMySQLQuery( "Select * from MyTable where ID = 1" );
DataRow oDR = oTbl.Rows[0];

MyOtherClass oMC = new MyOtherClass();


foreach( String s in WorkWithThese )
   // NOW, what I'm looking to do...
   oMC.<the field represented by 's'> = oDR[s];

Я знаю, что oDR [s] будет работать, так как это правильный столбец с тем же именем в наборе результатов. Моя проблема заключается в обновлении отдельного поля в oMC.

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

oMC.MyFld1 = oDR["MyFld1"];
oMC.MyFld1 = oDR["AnotherFld"];
oMC.MyFld1 = oDR["ThirdFld"];

когда может быть 50-60 полей для ссылки на таблицу

Ответы [ 6 ]

2 голосов
/ 07 марта 2010

Я придумала решение, которое позволило мне автоматизировать подход, но в то же время помогло повысить производительность ... Надеюсь, что техника может помочь и другим. Моя проблема была при использовании System.Data (через SqlCE, но применимо и для других баз данных). Каждый раз, когда я пытался создать объект команды SQL для выполнения вставки, обновления или чего-либо еще и добавлять «параметры» в объект sql, получать надлежащие типы данных и т. Д., Это снижало производительность. Итак, я сделал это для вставки / обновления. В моем классе менеджера данных (по одному на таблицу, с которой я работаю) я добавил объекты объектов IDbCommand, по одному для вставки / обновления соответственно. Во время конструктора я предварительно запрашивал таблицу, чтобы получить структуру конечного объекта строки, и предварительно собирал запрос и параметры (пропуская идентификатор первичного ключа) что-то вроде ...

private void BuildDefaultSQLInsert()
{

   // get instance to the object ONCE up front
   // This is a private property on the data manager class of IDbCommand type
   oSQLInsert = GetSQLCommand("");

   // pre-build respective insert statement and parameters ONCE. 
   // This way, when actually called, the object and their expected
   // parameter objects already in place.  We just need to update
   // the "Value" inside the parameter
   String SQLCommand = "INSERT INTO MySQLTable ( ";
   String InsertValues = "";

   // Now, build a string of the "insert" values to be paired, so
   // add appropriate columns to the string, and IMMEDIATELY add their
   // respective "Value" as a parameter
   DataTable MyTable = GetFromSQL( "Select * from MySQLTable where MyIDColumn = -1" );
   foreach (DataColumn oCol in MyTable.Columns)
   {
      // only add columns that ARE NOT The primary ID column
      if (!(oCol.ColumnName.ToUpper() == "MYIDCOLUMN" ))
      {
         // add all other columns comma seperated...
         SQLCommand += oCol.ColumnName + ",";

         InsertValues += "?,";
         // Ensure a place-holder for the parameters so they stay in synch 
         // with the string.  My AddDbParm() function would create the DbParameter
         // by the given column name and default value as previously detected
         // based on String, Int, DateTime, etc...
         oSQLInsert.Parameters.Add(AddDbParm(oCol.ColumnName, oCol.DefaultValue));
      }
   }

   // Strip the trailing comma from each element... command text, and its insert values
   SQLCommand = SQLCommand.Substring(0, SQLCommand.Length - 1);
   InsertValues = InsertValues.Substring(0, InsertValues.Length - 1);

   // Now, close the command text with ") VALUES ( " 
   // and add  the INSERT VALUES element parms
   SQLCommand += " ) values ( " + InsertValues + " )";

   // Update the final command text to the SQLInsert object 
   // and we're done with the prep ONCE
   oSQLInsert.CommandText = SQLCommand;

}

Далее, когда мне нужно на самом деле выполнить вставку для всех записей, я делаю это с помощью функции Add () и передаю экземпляр DataRow, над которым я работаю. Поскольку объект SQLInsert уже построен с соответствующими параметрами, я могу просто циклически проходить по строке данных того же типа, за который отвечает диспетчер данных, и просто обновлять объекты параметра текущими «значениями» строки данных

public Boolean AddMyRecord(DataRow oDR)
{
   // the parameter name was set based on the name of the column, 
   // so I KNOW there will be a match, and same data type
   foreach (IDbDataParameter oDBP in oSQLInsert.Parameters)
      oDBP.Value = oDR[oDBP.ParameterName];

   ExecuteMySQLCommand( oSQLInsert );
}

С некоторыми временными испытаниями на портативном устройстве время до и после запуска / проверки около 20 запросов и 10 вставок от 10 секунд до 2,5 секунд. Техника была аналогична методике выполнения SQLUpdate, но вынуждала предложение WHERE к первичному столбцу ID таблицы в конце строительного цикла / цикла объекта. Работает отлично. Теперь, если мне когда-либо понадобится развернуть структуру или последовательность столбцов таблицы, мне не нужно менять ЛЮБОЙ код для вставки, процессы обновления.

1 голос
/ 04 марта 2010

Согласитесь с остальными относительно скорости использования отражения, но что-то вроде этого может сработать:

        public static void SetProperty(object myObject, string name, string valueString)
        {
            try
            {
                Type type = myObject.GetType();
                PropertyInfo property = type.GetProperty(name);

                if (property != null && property.CanWrite)
                {
                    object value = null;
                    if (property.PropertyType == typeof(double))
                        value = Convert.ToDouble(valueString);
                    else if (property.PropertyType == typeof(int))
                        value = Convert.ToInt32(valueString);
                    else if (property.PropertyType == typeof(bool))
                        value = Convert.ToBoolean(valueString);
                    else if (property.PropertyType == typeof(DateTime))
                        value = DateTime.Parse(valueString);
                    ...
                    else
                        Debug.Assert(false, property.PropertyType.AssemblyQualifiedName + " not handled");

                    property.SetValue(myObject, value, null);
                }
            }
            catch (FormatException)
            {
                //Unable to set the property '{0}' to '{1}', name, valueString
            }
            catch (NullReferenceException)
            {
                //Property not defined (or even deprecated)
            }
        }
0 голосов
/ 13 февраля 2013

Вот мое решение, использующее рефлексию, я не знаю, подходит ли оно по производительности, но вы можете решить любые проблемы одним кликом ...

    /// <summary>
    /// Riempie una Entità con i valori presenti in un DataRow automaticamente.
    /// L'automatismo funzione solo se i nomi delle colonne (campi del DataBase) corrispondono ai nomi 
    /// delle properties se una Property non ha colonna del DataRow semplicemente è valorizzata al Default
    /// (come dopo una istanzizione dell'oggetto mediante "new"). Se una colonna esiste ma non c'è la 
    /// corrispondente proprietà la segnalazione dell'eccezione dipenderà dal flag: columnCanNotCorrespond
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="dr"></param>
    /// <param name="columnCanNotCorrespond">Se true, non scatta eccezione se c'è una colonna che non è corrispondente ad una Property</param>
    /// <returns></returns>
    public static T GetEntity<T>(DataRow dr, bool columnCanNotCorrespond)
    {
        Type entityType = typeof(T);
        T entity = (T)entityType.Assembly.CreateInstance(entityType.FullName);

        if (columnCanNotCorrespond)
        {
            foreach (DataColumn dc in dr.Table.Columns)
            {
                object columnValue = dr[dc.ColumnName];                 
                if (entity.GetType().GetProperty(dc.ColumnName) != null) //La Property Esiste?
                    entity.GetType().GetProperty(dc.ColumnName).SetValue(entity, columnValue, null);

            }
        }
        else //Scatterà eccezione se la Property non corrisponde alla colonna!
        {
            foreach (DataColumn dc in dr.Table.Columns)
            {
                object columnValue = dr[dc.ColumnName];                 
                entity.GetType().GetProperty(dc.ColumnName).SetValue(entity, columnValue, null);
            }

        }
        return (T)entity;
    }
0 голосов
/ 04 марта 2010

Использование переизбрания, безусловно, не тот путь, когда вы ищете производительность в портативном устройстве. Хотя есть некоторые инструменты ORM, такие как EntitySpaces, но я уверен, что вы достигнете лучшей производительности, следуя ручному подходу, возможно, с помощью некоторого инструмента генерации кода.

0 голосов
/ 04 марта 2010

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

  • заполнить поля вручную (не так уж много работы)
  • используйте генерацию кода, чтобы написать этот код для вас
  • используйте ORM (лучшее решение!)
0 голосов
/ 04 марта 2010

Не пробовал на CompactFramework, но вы можете попробовать использовать отражение, чтобы найти свойство для установки моего имени (=> имя столбца из базы данных). Затем вы можете установить свойство с помощью отражения.

Это, однако, наименее эффективный способ ...

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...