0

I have to do a lot of SQL inserts without using stored procedures.

For big classes, the insert strings get huge so I was thinking of building a generalized insert function to handle it when passing in an object. What I've written below works but it's not ideal because (1) I have to specify all possible data types and (2) I have to convert all values back to strings to build the insert string, I'd rather be using parameters with values.

I just want a function where I pass it an object class and the function inserts the object's values into the database (given all the column names in the table matches the property names of the object)

Any ideas would be greatly appreciated, thanks.

public static IEnumerable<KeyValuePair<string, T>> PropertiesOfType<T>(object obj)
{
    return from p in obj.GetType().GetProperties()
           where p.PropertyType == typeof(T)
           select new KeyValuePair<string, T>(p.Name, (T)p.GetValue(obj, null));
}

public string InsertString(string _table, object _class)
{
    Dictionary<string, string> returnDict = new Dictionary<string, string>();
    StringBuilder sb = new StringBuilder();
    foreach (var property in PropertiesOfType<DateTime>(_class))
        returnDict.Add(property.Key, property.Value.ToString("yyyy-MM-dd HH:mm:ss"));
    foreach (var property in PropertiesOfType<string>(_class))
        returnDict.Add(property.Key, property.Value);
    foreach (var property in PropertiesOfType<int>(_class))
    {
        if (property.Key == "Key")
            continue;
        returnDict.Add(property.Key, property.Value.ToString());
    }            
    foreach (var property in PropertiesOfType<bool>(_class))
    {
        if (property.Value)
            returnDict.Add(property.Key, "1");
        else
            returnDict.Add(property.Key, "0");
    }
    foreach (var property in PropertiesOfType<decimal>(_class))
        returnDict.Add(property.Key, property.Value.ToString());
    foreach (var property in PropertiesOfType<long>(_class))
        returnDict.Add(property.Key, property.Value.ToString());
    if (returnDict.Count == 1)
    {
        sb.Append(string.Format("INSERT INTO [{0}] ({1}) VALUES ('{2}')", _table, returnDict.ElementAt(0).Key, returnDict.ElementAt(0).Value));
    }
    else
    {
        for (int i = 0; i < returnDict.Count; i++)
        {
            if (i == 0)
                sb.Append(string.Format("INSERT INTO [{0}] ({1}, ", _table, returnDict.ElementAt(i).Key));
            else if (i == returnDict.Count - 1)
                sb.Append(string.Format("{0}) ", returnDict.ElementAt(i).Key));
            else
                sb.Append(string.Format("{0}, ", returnDict.ElementAt(i).Key));
        }
        for (int i = 0; i < returnDict.Count; i++)
        {
            if (i == 0)
                sb.Append(string.Format("VALUES ('{0}', ", returnDict.ElementAt(i).Value));
            else if (i == returnDict.Count - 1)
                sb.Append(string.Format("'{0}')", returnDict.ElementAt(i).Value));
            else
                sb.Append(string.Format("'{0}', ", returnDict.ElementAt(i).Value));
        }
    }        
    return sb.ToString();
}



string query = InsertString(_table, _obj);
user3769327
  • 93
  • 10
  • Its unclear what you'd like us to do to help you... – Taryn East Nov 10 '14 at 03:47
  • 1
    Have you considered using a MicroORM such as Dapper.Net? Then you just pass POCO objects into a generic insert function and it does all the work for you... – Greg the Incredulous Nov 10 '14 at 03:59
  • Sorry, I'll edit the post. I just want a function where I pass it an object class and the function inserts the object's values into the database (given all the column names in the table matches the property names of the object). – user3769327 Nov 10 '14 at 04:04
  • @user3769327 that is exactly what Dapper.Net is for - as long as your object's properties map to the underlying field names in your table (and have same data types) it all just works. – Greg the Incredulous Nov 10 '14 at 04:29
  • If You return dataset then it will be very easy case for you @user3769327 – Hardik Parmar Nov 10 '14 at 04:59
  • @HardikParmar Sorry, where should I return a DataSet from? Thank you. – user3769327 Nov 10 '14 at 06:01
  • @Greg Hi Greg, Dapper.Net looks very powerful but unfortunately I'm not very good at c# to implement all that yet. I was just looking for a simpler way to make this easier. Thank you for your suggestion. – user3769327 Nov 10 '14 at 06:02
  • Side note: I don't think you are building paramatrizied query based on quick overview of code. I assume you know that it is bad idea, so it is somewhat strange that you want spread it across your code... – Alexei Levenkov Nov 10 '14 at 06:08
  • Instead of appending values to sb.Append append that value to dataset & pass table valued fucntion to the sql – Hardik Parmar Nov 10 '14 at 06:11
  • @AlexeiLevenkov Hi, yes I know it's a bad idea which is why I'm trying to find a way to add the values as parameters instead of building everything as one insert string. Thanks. – user3769327 Nov 10 '14 at 07:09
  • @HardikParmar Hi, sorry I'm still a little confused. Can you please show me this method in code? Sounds like exactly what I'm looking for. Thank you very much. – user3769327 Nov 10 '14 at 07:10

1 Answers1

1

I've managed to find a way to do this that I'm pretty happy about that doesn't require any external libraries or frameworks.

Basing on @HardikParmar's suggestion I built a new process on converting a class object into a datatable, this will then store all the relevant datatypes as columns.

Then add a row into the structured datatable using the class object.

Now what you have a datatable with one row of values.

Then I create a PARAMATERIZED insert statement. Then in my command text I add the values to the parameters.

Almost clean, always room for improvement.

//this function creates the datatable from a specified class type, you may exclude properties such as primary keys
public DataTable ClassToDataTable<T>(List<string> _excludeList = null)
{
    Type classType = typeof(T);
    List<PropertyInfo> propertyList = classType.GetProperties().ToList();

    DataTable result = new DataTable(classType.Name);

    foreach (PropertyInfo prop in propertyList)
    {
        if (_excludeList != null)
        {
            bool toContinue = false;
            foreach (string excludeName in _excludeList)
            {
                if (excludeName == prop.Name)
                {
                    toContinue = true;
                    break;
                }
            }
            if (toContinue)
                continue;
        }
        result.Columns.Add(prop.Name, prop.PropertyType);
    }
    return result;
}
//add data to the table
public void AddRow(ref DataTable table, object data)
{
    Type classType = data.GetType();
    string className = classType.Name;

    if (!table.TableName.Equals(className))
    {
        throw new Exception("DataTableConverter.AddRow: " +
                            "TableName not equal to className.");
    }
    DataRow row = table.NewRow();
    List<PropertyInfo> propertyList = classType.GetProperties().ToList();
    foreach (PropertyInfo prop in propertyList)
    {            
        foreach (DataColumn col in table.Columns)
        {
            if (col.ColumnName == prop.Name)
            {
                if (table.Columns[prop.Name] == null)
                {
                    throw new Exception("DataTableConverter.AddRow: " +
                                        "Column name does not exist: " + prop.Name);
                }
                row[prop.Name] = prop.GetValue(data, null);
            }
        }                        
    }
    table.Rows.Add(row);
}
//creates the insert string
public string MakeInsertParamString(string _tableName, DataTable _dt, string _condition=null)
{
    StringBuilder sb = new StringBuilder();
    sb.Append(string.Format("INSERT INTO [{0}] (", _tableName));
    for (int i = 0; i < _dt.Columns.Count; i++)
    {
        sb.Append(string.Format("{0}", _dt.Columns[i].ColumnName));
        if (i < _dt.Columns.Count - 1)
            sb.Append(", ");                
    }
    sb.Append(") VALUES (");
    for (int i = 0; i < _dt.Columns.Count; i++)
    {
        sb.Append(string.Format("@{0}", _dt.Columns[i].ColumnName));
        if (i < _dt.Columns.Count - 1)
            sb.Append(", ");
    }
    sb.Append(")");
    if (!string.IsNullOrEmpty(_condition))
        sb.Append(" WHERE " + _condition);
    return sb.ToString();
}
//inserts into the database
public string InsertUsingDataRow(string _tableName, DataTable _dt, string _condition = null)
{
    try
    {
        using (SQLiteConnection conn = new SQLiteConnection(_dbPath))
        {
            string query = MakeInsertParamString(_tableName, _dt, _condition);
            SQLiteCommand cmd = new SQLiteCommand(query, conn);
            foreach (DataColumn col in _dt.Columns)
            {
                var objectValue = _dt.Rows[0][col.ColumnName];                    
                cmd.Parameters.AddWithValue("@" + col.ColumnName, objectValue);
            }
            conn.Open();
            cmd.ExecuteNonQuery();
            conn.Close();                
        }
        //return MakeInsertParamString(_tableName, _dt, _condition);
        return "Success";
    }
    catch (Exception ex) { return ex.Message; }
}
user3769327
  • 93
  • 10