Skip to content
Advertisement

Implementing a query function with undeclared number of parameters?

I’m developing a server/client application in C#. In the earlier phases of development I was writing SQL codes each time and it caused spaghetti code. Now I’m trying to make it clean. My question is: How can I write a general query generator function with dynamic parameters?

private void button3_Click(object sender, EventArgs e)
    {
        try
        {
            SqlCommand cmd= new SqlCommand();
            cmd.CommandText = "INSERT INTO tbl(line, name, firstvalue, secondvalue)" +
                "VALUES(@line, @name, @value, @secondvalue)";

            cmd.Parameters.AddWithValue("@line", comboBox4.Text);
            cmd.Parameters.AddWithValue("@name", textBox2.Text);
            cmd.Parameters.AddWithValue("@value", comboBox5.Text);
            cmd.Parameters.AddWithValue("@secondvalue", comboBox6.Text);
            cmd.Connection = Db.Db;
            cmd.CommandType = CommandType.Text;
            SqlDataReader dr = cmd.ExecuteReader();
            MessageBox.Show("Saved");
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex);
        }
        finally
        {
            Db.Close();
        }
}

But I want to convert it into:

        public void query(string commandText, params string[] parameters)
    {
        SqlCommand command = new SqlCommand();
        command.CommandText = commandText;
        foreach (var parameter in parameters)
//In this part, there can be lots of different queries and how can assign each parameter to relevant values
//Is there any change to assign them in order.              
    }

Advertisement

Answer

Well, if you insist on implementing such a routine (usually we use ORM) you have to parse the commandText; the simplest (but not the best) implementation is regular expressions (we Match parameter name within commandText, then Zip it with its value from parameters):

using System.Linq;
using System.Text.RegularExpressions;

...

public void query(string commandText, params string[] parameters) {
  using (SqlCommand command = new SqlCommand()) {
    command.Connection = myConnection; //TODO: put the right connection here
    command.CommandText = commandText;

    var prms = Regex
      .Matches(commandText, @"b@[A-Za-z_][A-Za-z_0-9]*b") 
      .Cast<Match>()
      .Zip(parameters, (match, value) => new {
         name = match.Value,
         value
       });

    foreach(var prm in prms) 
      command.Parameters.AddWithValue(prm.name, prm.value);

    // Just execute; we have nothing to read (ExecuteReader)
    command.ExecuteNonQuery(); 
  }
}

Edit: If you want / ready to specify parameters’ names, not only values you can try Tuples: for c# 7.0+

public void query(string commandText, params (string, object)[] parameters) {
   ...
   foreach (var prm in parameters) 
     command.Parameters.AddWithValue(prm.Item1, prm.Item2);

   ...
}

usage

query(sql, 
  ("@line", comboBox4.Text),
  ("@name", textBox2.Text),
  ("@id", 123),             // please, note integer value
  ("@money", 456.78d), 
   ... 
);

for C# 6.0-:

public void query(string commandText, params Tuple<string, object>[] parameters) {
   ...
   foreach (var prm in parameters) 
     command.Parameters.AddWithValue(prm.Item1, prm.Item2);

   ...
}

...

query(sql, 
  Tuple.Create("@line", comboBox4.Text),
  Tuple.Create("@name", textBox2.Text),
   ... 
);
User contributions licensed under: CC BY-SA
7 People found this is helpful
Advertisement