views:

295

answers:

5

Hi all,

I didn't quite get / see what I was hoping for and maybe missed, so I'll ask again anyhow. I have a class with fields on it. I have a data table that I'm querying from with the same columns and corresponding data types. I have a string array of all fields I am concerned about, and their specific order.

What I want to do is cycle through each element in the array and put the value from the data row of the retrieve data table resul set and populate into the field of the class structure. Something like the pseudo-code below

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];

I know the oDR[s] will work as it is a valid column by same name in the result set. My problem is the updating the individual field in the oMC.

This is intended use on a handheld device, so memory restrictions need best performance, yet dynamic for maintainability in a variety of other areas too... A gentle blend of both. I was hoping to avoid doing

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

when there could be 50-60 fields per table reference

A: 

Haven't tried on CompactFramework, but you could try to use reflection to find the property to set my name (=> the column name from the database). You could then set the property using reflection.

This, however, is the least performant way...

Thorsten Dittmar
A: 

It is possible to do it using reflection but it is extremely slow. I would recommend either:

  • to fill fields manually (no that much work)
  • use codegeneration to write that code for you
  • use ORM (best solution!)
Andrey
Just out of interest, how do you think an ORM will discover and populate the fields? Advising against reflection and then promoting it in the same answer is surprising.
Greg Beech
let me explain. orms solve it either by codegeneration or by reflection (right!) but they do it very smart way and usually only once, at start-up, so it doesn't affect overall performance.
Andrey
A: 

Agree with the others about the speed of using reflection but something like this might work:

        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)
            }
        }
openshac
A: 

Using relection is certainly not the way to go when you are looking for performance in handheld device. Although there are some ORM tools out there like EntitySpaces but i am sure you would get better performance by following manual approach may be with the help of some Code generation tool.

Nikhil Vaghela
That's kinda the answer I was expecting... Thanks to all to confirm my gut feelings...
DRapp
A: 

I've come up with a solution that allowed me an automated approach, yet helped improve performance at the same time... Hope the technique can help others too. My issue was when using System.Data (via SqlCE, but applicable for other database backends too). Each time I tried to create the SQL command object to perform insert, update, or whatever, and add the "parameters" to the sql object, get proper data types, etc was killing performance. So, I did this for the Insert/Update. On my data manager class (one per table I work with), I added to objects of IDbCommand objects, one for Insert/Update respectively. During the constructor, I would pre-query the table to get a structure of the final row object and pre-build the query and parameters (skipping over the primary key ID) something like...

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;

}

Next, When I need to actually perform the inserts for all records as I go through, I do that via my Add() function and pass in an instance of the DataRow I am working on. Since the SQLInsert object is already built with respective parameters, I can just cycle through the data row of same type as the data manager is responsible for, and just update the parameter objects with the data row's current "values"

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

With some timed-trials against the handheld device, the before and after times to run / validate about 20 queries and 10 inserts from about 10 seconds down to 2.5 seconds. The technique was similar for that of doing SQLUpdate, but forcing the WHERE clause to the primary ID column of the table at the end of the building string / object cycle. It works great. Now, if I ever need to expand the structure or column sequences of the table, I dont have to change ANY code for the insert, update processes.

DRapp