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 Tuple
s: 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), ... );