2009-03-27 12 views
8

Estoy volviendo a escribir el SQL en línea en mi clase de repositorio para usar en su lugar procedimientos almacenados (requisito de seguridad). Después de usar Fluent NHibernate y Linq2Sql en el pasado, me resulta extremadamente difícil de manejar y poco elegante.¿Cuál es la forma más elegante de usar procedimientos almacenados?

EDIT: Para aclarar, no estoy buscando una solución ORM que funcione con procs almacenados. Solo quiero algunos consejos sobre una buena forma de escribir el código a continuación.

¿Existen estrategias para hacer que este tipo de código sea lo más elegante posible?

 string commandText = "dbo.Save"; 

     using (SqlConnection sql = new SqlConnection(_connString.ConnectionString)) 
     using (SqlCommand cmd = sql.CreateCommand()) 
     { 
      cmd.CommandText = commandText; 
      cmd.CommandType = CommandType.StoredProcedure; 

      SqlParameter idParam = new SqlParameter("identity", item.Identity); 
      idParam.Direction = ParameterDirection.Input; 

      SqlParameter nameParam = new SqlParameter("name", item.Name); 
      nameParam.Direction = ParameterDirection.Input; 

      SqlParameter descParam = new SqlParameter("desc", item.Description); 
      descParam.Direction = ParameterDirection.Input; 

      SqlParameter titleParam = new SqlParameter("title", item.) 
      descParam.Direction = ParameterDirection.Input; 

      //SNIP More parameters 

      cmd.Parameters.Add(idParam); 
      cmd.Parameters.Add(descParam); 
      cmd.Parameters.Add(titleParam); 
      //SNIP etc 

      sql.Open(); 

      cmd.ExecuteNonQuery(); 

      //Get out parameters 
     } 

     return item; 

Respuesta

10

Dentro de nuestras aplicaciones internas generalmente utilizamos la clase SqlHelper que se puede encontrar en el siguiente enlace (descarga y descripción): http://www.microsoft.com/downloads/details.aspx?familyid=f63d1f0a-9877-4a7b-88ec-0426b48df275&displaylang=en

Esencialmente la clase SqlHelper le quita parte de la necesidad de declarar objetos de conexión, comandos, etc. y le permite llamar a métodos para devolver objetos como conjunto de datos

a continuación, puede utilizar SqlHelper como tal:

public static int UpdateItem(int parameter1, int parameter2, string parameter3) 
    { 
     SqlParameter[] arParam = new SqlParameter[3]; 
     arParam[0] = new SqlParameter("@Parameter1", lotId); 
     arParam[1] = new SqlParameter("@Parameter2", saleId); 
     arParam[2] = new SqlParameter("@Parameter3", lotNumber); 


     return int.Parse(SqlHelper.ExecuteScalar(connString, CommandType.StoredProcedure, "spName", arParam).ToString(), CultureInfo.InvariantCulture); 
    } 

Espero que esto ayude :)

+0

¡Ooh, gracias por la cabeza allá arriba! No había visto esto. –

+0

Se rumorea que EntLib se actualizará para .NET 3.5 para ser compatible con LINQ. –

+0

SqlHelper también almacenará en caché los parámetros en algunas de las sobrecargas, lo que guardará los viajes a la base de datos. En lo anterior, .NET realizará un viaje de ida y vuelta a la base de datos para descubrir el tipo de datos para @Parámetro1, etc. Asegúrese de leer sobre su uso. – Daniel

2

Usted podría utilizar SubSonic como una capa ORM entre su clase y los procedimientos almacenados. Aquí hay un example básico. Phil Haack tiene un buen article en él también.

Hay buena información en this other question.

EDITAR: Como ha actualizado su pregunta para indicar que no desea utilizar un ORM, SubSonic no es para usted. Sin embargo, dejaré la respuesta aquí para otras personas que usan procs almacenados. :) También deberías echar un vistazo si incluso hay una posibilidad de que puedas usarlo.

+0

Gracias. No he visto eso, sin embargo, se me aconseja no utilizar tecnología de terceros fuera de las herramientas existentes porque debe ser aprobado por el cliente y eso lleva mucho tiempo. –

+0

Así que no les digas que es "tecnología de terceros". También debe actualizar su pregunta con esta información. –

+0

Realmente no puedo salirse con la tuya. Lo veré para ver cuál es la implicación, lo almacenaré en el banco para su uso posterior si no. –

2

Puede reducir a la mitad el conteo de líneas derivando su propio InputSqlParameter de SqlParameter y estableciendo la dirección en Input en el constructor.

que le permiten escribir

cmd.Parameters.Add(new InputSqlParameter("title", item.title)); 
    cmd.Parameters.Add(new InputSqlParameter("property", item.property)); 

Esto muestra un patrón, y que permite la configuración de una lista de nombres de parámetros y campos de elementos y hacer añadiendo el parámetro en un para bucle.

1

Mantenga cada parámetro para un procedimiento almacenado dado en una "clase de datos". Use anotaciones para especificar cosas como parámetros de sproc "in" o "out".

Luego puede cargar la clase desde el código del cliente, luego usar la reflexión para construir todos los parámetros para el sproc, ejecutar el sproc y cargar los parámetros de salida nuevamente dentro de la clase de datos.

Un poco más limpio: tiene una clase para las entradas y otra para las salidas (incluso si algunas están dentro/fuera). De esta forma, es claro (para el código del cliente) qué parámetros deben completarse en el camino de entrada y cuáles devueltos. También esto obvia la necesidad de esas anotaciones.

+0

Interesante ... Voy a tener una jugada con eso –

-1
using (var conn = new SqlConnection(ConnectionString)) 
using (var cmd = conn.CreateCommand()) 
{ 
    cmd.CommandText = "[dbo].[Save]"; 
    cmd.CommandType = CommandType.StoredProcedure; 

    cmd.Parameters.Add(new SqlParameter(
     "Identity", SqlDbType.Int) { Value = item.Identity }); 

    cmd.Parameters.Add(new SqlParameter(
     "Name", SqlDbType.NVarChar, 50) { Value = item.Name }); 

    cmd.Parameters.Add(new SqlParameter(
     "Title", SqlDbType.NVarChar, 100) { Value = item.Title }); 

    conn.Open(); 
    cmd.ExecuteNonQuery(); 
} 

Aquí es cómo podría parecerse con Ent Lib:

// Note, that you don't need to specify connection string here, 
// it will be automatically taken from a configuration file 
var db = DatabaseFactory.CreateDatabase(); 

using (var cmd = db.GetStoredProcCommand("[dbo].[Save]")) 
{ 
    db.AddInParameter(cmd, "Identity", DbType.Int32, item.Identity); 
    db.AddInParameter(cmd, "Name", DbType.String, item.Name); 
    db.AddInParameter(cmd, "Title", DbType.String, item.Title); 
    db.ExecuteNonQuery(cmd); 
} 

También puede utilizar el método SqlHelper de Enterprise Library para simplificar esta sintaxis.

SqlHelper.ExecuteNonQuery(connectinString, 
    CommandType.StoredProcedure, "[dbo].[Save]", new SqlParameter[] 
     { 
      new SqlParameter("Identity", item.Identity), 
      new SqlParameter("Name", item.Name), 
      new SqlParameter("Title", item.Title) 
     }); 
+0

No es necesario que intente finalmente si usa "usar" las llamadas descartar automáticamente que a su vez se llama cerrar –

6

hacerse con una copia de Enterprise Library. Es una linda envoltura alrededor de ADO. Por ejemplo:

using System.Data.Common; 
using System.Globalization; 
using Microsoft.Practices.EnterpriseLibrary.Data; 

Database db = DatabaseFactory.CreateDatabase(DatabaseType.MyDatabase.ToString()); 

using (DbCommand dbCommand = db.GetStoredProcCommand("dbo.MyStoredProc")) { 
    db.AddInParameter(dbCommand, "identity", DbType.Int32, item.Identity); 
    db.AddInParameter(dbCommand, "name", DbType.String, item.Name); 
    db.AddInParameter(dbCommand, "desc", DbType.String, item.Description); 
    db.AddInParameter(dbCommand, "title", DbType.String, item.Title); 

    db.ExecuteNonQuery(dbCommand); 
} // using dbCommand 
2

Recomendaría utilizar el objeto SqlHelper de Microsoft Application Blocks.

Para una declaración como la que mencioné anteriormente, puedo hacer lo siguiente.

SqlHelper.ExecuteNonQuery(_connectionString, "MyProcName", 1, "NameValue", "Description", "Title"); 

Básicamente SQL Helper toma algunos parámetros.

  1. la cadena de conexión para conectarse a la db
  2. El nombre del procedimiento almacenado
  3. Una matriz de valores de parámetros, en el orden en el que aparecen en el procedimiento almacenado.

Hay un inconveniente de rendimiento MUY leve con este método sobre la creación explícita de cada parámetro, pero el ahorro de tiempo generalmente lo supera, ya que es muy pequeño.

+0

¿Existe realmente un inconveniente de rendimiento? ¿El compilador no se ocupa de eso? –

+0

De hecho, acabo de terminar ejecutando un montón de pruebas. con un procedimiento almacenado de 5 parámetros existe una diferencia del 7% cuando se emiten entre 1 y 1000 declaraciones. Después de 1000, baja un poco a aproximadamente 4% más o menos. Una vez que llegué a unos 20000 insertos, no hubo diferencia. Parece estar vinculado al almacenamiento en caché. –

+0

Pero realmente teniendo en cuenta los tiempos involucrados, la diferencia de rendimiento es apenas algo que notaría una diferencia de .0003 segundos para entradas individuales. –

3

normalmente utilizo alguna variación del ejemplo siguiente, en función del entorno, por supuesto:

Mis métodos básicos de ayuda que yo llamo lo largo de mi código de

public static SqlCommand CreateStoredProcCmd(string name, SqlConnection con) 
{ 
    var cmd = new SqlCommand(name, con); 
    cmd.CommandType = CommandType.StoredProcedure; 
    return cmd; 
} 

public static void AddParams(this SqlCommand cmdObject, Params SqlParameter[] parameters) 
{ 
    foreach(SqlParameter param in parameters) 
    { 
    cmdObject.Parameters.add(param); 
    } 
} 

/* Any overloaded methods to create params receiving my param definitions 
in any manner that the usual new SqlParameter() constructor doesn't handle */ 
public static SqlParameter CreateSqlParam(string ParamName, 
              SqlDbType ParamType, 
              object value) 
{ 
    return CreateSqlParam(ParamName, ParamType, ParameterDirection.Input, value); 
} 

public static SqlParameter CreateSqlParam(string ParamName, 
              SqlDbType ParamType, 
              ParameterDirection ParamDir) 
{ 
    return CreateSqlParam(ParamName, ParamType, ParamDir, null; 
}       

public static SqlParameter CreateSqlParam(string ParamName, 
              SqlDbType ParamType, 
              ParameterDirection ParamDir, 
              object value) 
{ 
    var parm = new SqlParameter(ParamName, ParamType); 
    parm.Direction = ParamDir; 
    parm.Value = value; 
    return parm; 
} 

Ahora aquí es como me puse mis procesos almacenados y agregar todos mis parámetros elegantemente

public static string DoStuff() 
{ 
    using (var oCon = new SqlConnection("MyConnectionString")) 
    { 
     oCon.Open(); 
     var oCmd = CreateStoredProcCmd("sp_Name", oCon).AddParams(
      CreateSqlParam("Param1", SqlDBType.Int, 3), 
      CreateSqlParam("Param2", SqlDBType.VarChar, "Hello World"), 
      CreateSqlParam("Param3", SqlDBType.VarChar, ParameterDirection.Output) 
     ); 
     oCmd.Prepare(); 
     oCmd.ExecuteNonQuery(); 
     object outVal = oCmd.Parameters["Param3"]; 
     return null != outVal ? outVal.ToString() : String.Empty; 
    } 
} 
2

Para que el código un poco menos detallados parámetros siempre he agregado empleando

cmd.Parameters.AddWithValue("name", item.Name); 
cmd.Parameters.AddWithValue("title", item.Title); 
// and so on 
2

de entrada es la dirección por defecto y se puede acortar el parámetro de la adición y probablemente quiere declarar las SqlDBTypes así ...

cmd.Parameters.Add("identity", SqlDBType.???).Value = item.Identity; 
cmd.Parameters.Add("desc", SqlDbType.???, ?size?).Value = item.Description; 
cmd.Parameters.Add("title", SqlDBType.???, ?size?).Value = item.Title; 

//Output params generally don't need a value so... 
cmd.Parameters.Add("someOut", SqlDBType.???).Direction = ParameterDirection.Output; 
Cuestiones relacionadas