Command.CommandText
= ProcedimientoAlmacenado
The parameters must have names too. Does the Parametros array contains SqlParameter objects or generic C# objects?
If the parameters are generic C# objects, is better to pass in a dictionary of names and values:
protected void EjecutarGuardar(string ProcedimientoAlmacenado,
Dictionary<string, object> Parametros)
{
using (SqlConnection Connection = new SqlConnection(...))
{
Connection.Open();
SqlCommand Command = Connection.CreateCommand()
Command.CommandText = ProcedimientoAlmacenado;
Command.Connection = Connection;
Command.CommandType = CommandType.StoredProcedure;
foreach (string name in Parametros.Keys)
{
Command.Parameters.AddWithValue(name, Parametros[name] ?? DBNull.Value);
}
Command.ExecuteNonQuery();
}
}
This is a quick and dirty approach. Note that this approach usually has problems because AddWithValue will pass in a parameter of type NVARCHAR for a string, not VARCHAR, and with ad-hoc SQL this can cause index SARG-ability problems on VARCHAR columns (because the conversion will be always from VARCHAR to NVARCHAR and not vice-versa). However with stored procedures is not such a problem because procedures have types parameters and thus a force coercion happens to VARCHAR if the procedure was created with parameter type VARCHAR.
You will also have problems around passing NULL parameters, so you'll need to do something like, the parameter has to be DBNull.Value
not null
:
Command.Parameters.AddWithValue(name, Parametros[name] ?? DBNull.Value);
On high performance systems this approach also pollutes the execution cache unnecessarily because the AddWithValue will pass parameters of type NVARCHAR(<exact length of the string>)
, not NVARCHAR(<length of the database type>)
. So Paramaters.AddWithValue("@name", "John")
and Parameters.AddwithValue("@name", "Doe")
will create two distinct plans in the cache because one is invoked with a parameter of type NVARCHAR(4), the other with a parameter NVARCHAR(3) and they are seen by the SQL plan cache as different types. This is not a problem on simple projects, but on more complex and high performance ones it is recommended to set the parameter types explicitly.
My recommendation would be to avoid this kind of generic one-size-fits-all procedures and instead write a data access layer with explicit C# wrapper for each database procedure, with properly types parameters. A strongly typed dataset can actually do this, the other alternative (my favorite and what I always use) is to generate the entire data acces slayer from an XML file using an XSLT stylesheet that creates the C# wrappers. The source XML is, of course, extracted from the database meta data itself.