tags:

views:

81

answers:

4

I have the following method that is supposed to be a generic "Save to SQL" method for my application.

protected void EjecutarGuardar(string ProcedimientoAlmacenado, object[] Parametros)
        {
            SqlConnection Connection = new SqlConnection(ConfigurationManager.ConnectionStrings["ConnectionString"].ConnectionString);

            SqlCommand Command = Connection.CreateCommand();             
            Command.CommandType = CommandType.StoredProcedure;
            foreach (object X in Parametros)
            {
                Command.Parameters.Add(X);
            }            

            Connection.Open();
            Command.ExecuteNonQuery();
            Connection.Close();

            Connection.Dispose();
        }

I have to pass the NAME of the StoredProcedure and an Array filled with the parameters. I'm kind of lost at this point. Where should I use the NAME of the stored procedure "ProcedimientoAlmacenado"?

I'm thinking maybe Command.Command?????something somethign? But I'm lost there. Any help?

Edit: For simplicities sake let's say a I have a stored procedure called "ABC" in my database. How could I associate it to my SqlCommand "Command" in my code?

A: 
Command.ComandText = "storedProcName";
Esteban Araya
A: 

Command.CommandText = "ProcedimientoAlmacenado"; Before or after Command.CommandType = CommandType.StoredProcedure; This is not mandatory but that's the way I do it.

dr
+2  A: 

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.

Remus Rusanu
They are just generic object. My plan is to load a bunch of things (int, string, bool) into the object[] array and then use the foreach to load the parameters. This'll work right? :)
Sergio Tapia
No, you need to pass a dictionary of parameter name - values and assign the name for each parameter.
Remus Rusanu
Your stored procedure dictates what parameters you can add to the command object.
dr
A: 

Set the CommandText property of the SqlCommand object to the name of your stored procedure:

Command.CommandText = "MyStoredProcedureName";
Tim S. Van Haren