2009-03-27 8 views
8

Sto riscrivendo l'SQL inline nella mia classe di repository per utilizzare invece stored procedure (requisiti di sicurezza). Dopo aver usato Fluent NHibernate e Linq2Sql in passato, trovo che sia estremamente ingombrante e poco elegante.Qual è il modo più elegante di utilizzare le stored procedure?

EDIT: Per chiarire, non sto cercando una soluzione ORM che funzioni con i proc memorizzati. Voglio solo qualche consiglio su un bel modo di scrivere il codice qui sotto.

Esistono strategie per rendere questo tipo di codice il più elegante possibile?

 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; 

risposta

10

All'interno delle nostre applicazioni interne generalmente usiamo la classe SqlHelper che si trova al seguente link (download e la descrizione): http://www.microsoft.com/downloads/details.aspx?familyid=f63d1f0a-9877-4a7b-88ec-0426b48df275&displaylang=en

In sostanza la classe SqlHelper toglie un po 'della necessità di dichiarare gli oggetti di connessione, comandi ecc e permette di chiamare i metodi per restituire oggetti, come DataSet

si potrebbe quindi utilizzare SqlHelper come tale:

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); 
    } 

Spero che questo aiuti :)

+0

Ooh grazie per le teste lassù! Non l'avevo visto. –

+0

Si dice che EntLib sarà aggiornato per .NET 3.5 per supportare LINQ. –

+0

SqlHelper memorizza anche i parametri in _some_ dei sovraccarichi, il che salverà i viaggi nel db. In quanto sopra, .NET eseguirà il round trip del db per scoprire il tipo di dati per @ Parameter1, ecc. Assicurati di leggere sul suo utilizzo. – Daniel

2

Si potrebbe utilizzare SubSonic come strato ORM tra la vostra classe e le stored procedure. Ecco un numero base example. Anche Phil Haack ha un buon article.

Ci sono alcune buone informazioni in this other question.

MODIFICA: poiché hai aggiornato la domanda per indicare che non vuoi utilizzare un ORM, SubSonic non fa per te. Tuttavia, lascerò qui la risposta per gli altri utenti che usano proc memorizzati. :) Dovresti anche dare un'occhiata se c'è anche una possibilità che tu possa usarlo.

+0

Grazie non l'ho visto, tuttavia mi viene consigliato di non utilizzare la tecnologia di terze parti al di fuori degli strumenti esistenti perché deve essere approvata dal cliente e ciò richiede anni. –

+0

Quindi non dire loro che è "tecnologia di terze parti". Dovresti anche aggiornare la tua domanda con queste informazioni. –

+0

Non posso davvero farla franca. Lo guarderò per vedere qual è l'implicazione, lo conserverò in banca per utilizzarlo in seguito, se no. –

2

Si può dimezzare il linecount derivando il proprio InputSqlParameter da SqlParameter e impostando la direzione su Input nel costruttore.

modo che permettesse di scrivere

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

Questo dimostra un modello, e si imposta lascia un elenco dei nomi dei parametri e campi di voce e fare il parametro aggiungendo in un per ciclo.

1

Conservare ciascun parametro per una determinata stored procedure in una "classe dati". Utilizzare le annotazioni per specificare elementi come i parametri sproc "in" o "out".

Quindi è possibile caricare la classe dal codice client, quindi utilizzare reflection per creare tutti i parametri per lo sproc, eseguire lo sproc e caricare nuovamente i parametri di output nella classe dati.

Leggermente più pulito: avere una classe per gli ingressi e un'altra per le uscite (anche se alcuni sono in/out). In questo modo è chiaro (al codice client) quali parametri devono essere compilati durante il percorso e quali vengono restituiti. Anche questo ovvia alla necessità di quelle annotazioni.

+0

Interessante ... Avrò un gioco con quello –

-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(); 
} 

Ecco come potrebbe sembrare 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); 
} 

È inoltre possibile utilizzare il metodo SqlHelper da Enterprise Library per semplificare questa sintassi.

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

Non c'è bisogno di provare, infine, se si utilizza "usa" chiama chiamate automaticamente che si chiama close –

6

prendere una copia di Enterprise Library. È un bel wrapper intorno ADO. Ad esempio:

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

Si consiglia di utilizzare l'oggetto SqlHelper di Microsoft Application Blocks.

Per una dichiarazione come quella che ho elencato sopra, posso fare quanto segue.

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

Fondamentalmente SQL Helper richiede alcuni parametri.

  1. la stringa di connessione per il collegamento al db
  2. Il nome della stored procedure
  3. Un array di valori di parametri, nell'ordine in cui compaiono nella stored procedure.

C'è un lieve limite di prestazioni con questo metodo rispetto alla creazione esplicita di ciascun parametro, ma il risparmio di tempo di solito lo sbilancia poiché è così piccolo.

+0

C'è davvero un limite di prestazioni? Il compilatore non si occupa di questo? –

+0

In realtà ho appena finito di fare un sacco di test. con una procedura memorizzata con 5 parametri c'è una differenza del 7% quando si emette tra 1 e 1000 istruzioni. Dopo il 1000 scende un po 'a circa il 4% circa. Una volta che ho colpito circa 20000 inserti non c'era differenza. Appare collegato al caching. –

+0

Ma date le circostanze, le differenze nelle prestazioni non sono certo qualcosa che si noterà una differenza di .0003 secondi per le singole voci. –

3

Io di solito uso qualche variazione del seguente esempio, a seconda dell'ambiente di corso:

miei metodi di supporto di base che io chiamo tutto il mio codice

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; 
} 

Ora ecco come ho impostato il mio stored procedure e aggiungere elegantemente tutti i miei parametri

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

Per rendere il codice un po 'meno verbose parametri ho sempre aggiunto utilizzando

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

ingresso è la direzione di default ed è possibile ridurre il parametro di aggiunta e probabilmente vuole dichiarare le SqlDBTypes così ...

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; 
Problemi correlati