it-swarm.it

È considerato un modello anti per scrivere SQL nel codice sorgente?

È considerato un modello anti per hardcode SQL in un'applicazione come questa:

public List<int> getPersonIDs()
{    
    List<int> listPersonIDs = new List<int>();
    using (SqlConnection connection = new SqlConnection(
        ConfigurationManager.ConnectionStrings["Connection"].ConnectionString))
    using (SqlCommand command = new SqlCommand())
    {
        command.CommandText = "select id from Person";
        command.Connection = connection;
        connection.Open();
        SqlDataReader datareader = command.ExecuteReader();
        while (datareader.Read())
        {
            listPersonIDs.Add(Convert.ToInt32(datareader["ID"]));
        }
    }
    return listPersonIDs;
}

Normalmente avrei un livello repository ecc., Ma per semplicità l'ho escluso nel codice sopra.

Di recente ho ricevuto un feedback da un collega che si è lamentato del fatto che SQL fosse scritto nel codice sorgente. Non ho avuto la possibilità di chiedere perché e ora è via per due settimane (forse di più). Suppongo che intendesse:

  1. Usa LINQ
    o
  2. Utilizzare le stored procedure per SQL

Ho ragione? È considerato un modello anti per scrivere SQL nel codice sorgente? Siamo un piccolo team che lavora a questo progetto. Penso che il vantaggio delle procedure memorizzate sia che gli sviluppatori SQL possono essere coinvolti nel processo di sviluppo (scrittura di procedure memorizzate ecc.).

Modifica Il seguente link parla di istruzioni SQL codificate: https://docs.Microsoft.com/en-us/sql/odbc/di riferimento/sviluppo-app/hard-coded-SQL-dichiarazioni . C'è qualche vantaggio nel preparare un'istruzione SQL?

88
w0051977

Hai escluso la parte cruciale per semplicità. Il repository è lo strato di astrazione per la persistenza. Separiamo la persistenza nel suo livello in modo da poter cambiare la tecnologia di persistenza più facilmente quando è necessario. Pertanto, avere SQL al di fuori del livello di persistenza ostacola completamente lo sforzo di avere un livello di persistenza separato.

Di conseguenza: SQL va bene all'interno del livello di persistenza specifico di una tecnologia SQL (ad es. SQL va bene in un SQLCustomerRepository ma non in un MongoCustomerRepository). Al di fuori del livello di persistenza, SQL interrompe la tua astrazione e quindi è considerata una pessima pratica (da parte mia).

Per quanto riguarda strumenti come LINQ o JPQL: quelli possono semplicemente astrarre i sapori di SQL là fuori. Avere query LINQ-Code o JPQL al di fuori di un repository interrompe l'astrazione della persistenza proprio come farebbe SQL raw.


Un altro enorme vantaggio di un livello di persistenza separato è che consente di annullare il test del codice di logica aziendale senza dover configurare un server DB.

Ottieni test di unità a basso profilo di memoria e veloci con risultati riproducibili su tutte le piattaforme supportate dalla tua lingua.

In un'architettura di servizio MVC + questo è un semplice compito di deridere l'istanza del repository, creare alcuni dati di simulazione in memoria e definire che il repository dovrebbe restituire quei dati di simulazione quando viene chiamato un determinato getter. È quindi possibile definire i dati di test per unittest e non preoccuparsi di ripulire il DB in seguito.

Il test delle scritture nel DB è semplice: verificare che siano stati richiamati i metodi di aggiornamento rilevanti sul livello di persistenza e affermare che le entità erano nello stato corretto quando ciò è accaduto.

111
marstato

La maggior parte delle applicazioni aziendali standard oggi utilizza livelli diversi con responsabilità diverse. Tuttavia, quali livelli che usi per la tua applicazione e quale livello ha quale responsabilità spetta a te e al tuo team. Prima di poter decidere se è giusto o sbagliato posizionare SQL direttamente nella funzione che ci hai mostrato, devi sapere

  • quale livello nella tua applicazione ha quale responsabilità

  • da quale livello proviene la funzione sopra

Non esiste una soluzione "unica per tutti". In alcune applicazioni i progettisti preferiscono utilizzare un framework ORM e lasciare che il framework generi tutto l'SQL. In alcune applicazioni i progettisti preferiscono archiviare tale SQL esclusivamente in stored procedure. Per alcune applicazioni esiste un livello di persistenza (o repository) scritto a mano in cui risiede l'SQL e per altre applicazioni è bene definire eccezioni in determinate circostanze dal rigoroso inserimento di SQL in quel livello di persistenza.

Quindi cosa devi pensare: quali livelli ti vuoi o bisogno nella tua particolare applicazione e come t vuoi le responsabilità? Hai scritto "Normalmente avrei un livello repository", ma quali sono le responsabilità esatte che vuoi avere all'interno di quel livello e quali responsabilità vuoi mettere altrove? Rispondi prima, quindi puoi rispondere alla tua domanda da solo.

55
Doc Brown

Marstato dà una buona risposta ma vorrei aggiungere qualche commento in più.

SQL nel sorgente NON è un modello anti-pattern ma può causare problemi. Ricordo quando in passato era necessario inserire query SQL nelle proprietà dei componenti rilasciati in ogni modulo. Ciò ha reso le cose davvero brutte molto velocemente e hai dovuto saltare attraverso i cerchi per individuare le query. Sono diventato un forte sostenitore della centralizzazione dell'accesso al database il più possibile entro i limiti delle lingue con cui stavo lavorando. Il tuo collega potrebbe ricevere flashback a questi giorni bui.

Ora, alcuni dei commenti parlano del blocco del fornitore come se fosse automaticamente una brutta cosa. Non lo è. Se firmo un assegno a sei cifre ogni anno per utilizzare Oracle, puoi scommettere che voglio che qualsiasi applicazione che accede a quel database utilizzi la sintassi Oracle aggiuntiva in modo appropriato ma al suo massimo. Non sarò contento se il mio brillante database è paralizzato dai programmatori che scrivono male Vanilla ANSI SQL quando esiste un "modo Oracle" di scrivere l'SQL che non paralizza il database. Sì, cambiare database sarà più difficile, ma l'ho visto solo in un sito di grandi clienti un paio di volte in oltre 20 anni e uno di questi casi si stava spostando da DB2 -> Oracle perché il mainframe che ospitava DB2 era obsoleto e veniva rimosso . Sì, questo è il blocco dei fornitori, ma per i clienti aziendali è effettivamente desiderabile pagare un RDBMS capace e costoso come Oracle o Microsoft SQL Server e quindi utilizzarlo nella misura massima. Hai un accordo di supporto come coperta di comfort. Se sto pagando per un database con un'implementazione di stored procedure avanzate, lo voglio utilizzato per quei casi in cui ha senso.

Questo porta al punto successivo, se stai scrivendo un'applicazione che accede a un database SQL devi imparare SQL così come l'altra lingua e per apprendere intendo anche l'ottimizzazione delle query; Mi arrabbierò con te se scrivi il codice di generazione SQL che scarica la cache SQL con una raffica di query quasi identiche quando avresti potuto usare una query abilmente parametrizzata.

Nessuna scusa, nessun nascondiglio dietro un muro di Hibernate. Gli ORM usati male possono veramente paralizzare le prestazioni delle applicazioni. Ricordo di aver visto una domanda su Stack Overflow qualche anno fa sulla falsariga di:

In Hibernate sto analizzando 250.000 record verificando i valori di un paio di proprietà e aggiornando oggetti che soddisfano determinate condizioni. Funziona un po 'lentamente, cosa posso fare per accelerarlo?

Che ne dici di "UPDATE table SET field1 = dove field2 è True e Field3> 100". La creazione e l'eliminazione di 250.000 oggetti potrebbe essere il tuo problema ...

cioè Ignora ibernazione quando non è appropriato usarlo. Comprendi il database.

Quindi, in sintesi, incorporare SQL nel codice può essere una cattiva pratica, ma ci sono cose molto peggiori che puoi finire cercando di evitare di incorporare SQL.

33
mcottle

Sì, la codifica hard delle stringhe SQL nel codice dell'applicazione è generalmente un anti-pattern.

Proviamo a mettere da parte la tolleranza che abbiamo sviluppato da anni vedendolo nel codice di produzione. Mischiare lingue completamente diverse con sintassi diverse nello stesso file non è generalmente una tecnica di sviluppo desiderabile. Questo è diverso dai linguaggi modello come Razor che sono progettati per dare un significato contestuale a più lingue. Come Sava B. menziona in un commento qui sotto, SQL nel tuo C # o altro linguaggio di applicazione (Python, C++, ecc.) È una stringa come qualsiasi altra ed è semanticamente insignificante. Lo stesso vale quando si mescolano più di una lingua nella maggior parte dei casi, anche se ci sono ovviamente situazioni in cui è accettabile farlo, come Assembly in linea in C, frammenti piccoli e comprensibili di CSS in HTML (notando che CSS è progettato per essere miscelato con HTML ), e altri.

Clean Code by Robert C. Martin, pg. 288 (Robert C. Martin su come mescolare le lingue, Clean Code , Capitolo 17, "Odori ed euristica del codice", pagina 288)

Per questa risposta, focalizzerò SQL (come richiesto nella domanda). I seguenti problemi possono verificarsi durante la memorizzazione di SQL come set à la carte di stringhe non associate:

  • La logica del database è difficile da individuare. Cosa cerchi per trovare tutte le tue istruzioni SQL? Stringhe con "SELECT", "UPDATE", "MERGE", ecc.?
  • Il refactoring degli usi dello stesso o simile SQL diventa difficile.
  • L'aggiunta del supporto per altri database è difficile. Come si potrebbe ottenere questo? Aggiungere if..quindi le istruzioni per ciascun database e archiviare tutte le query come stringhe nel metodo?
  • Gli sviluppatori leggono una dichiarazione in un'altra lingua e si distraggono dallo spostamento dell'attenzione dallo scopo del metodo ai dettagli di implementazione del metodo (come e da dove vengono recuperati i dati).
  • Mentre le battute singole potrebbero non rappresentare un problema eccessivo, le stringhe SQL incorporate iniziano a sfaldarsi man mano che le istruzioni diventano più complesse. Cosa fai con un'istruzione di riga 113? Inserisci tutte le 113 righe nel tuo metodo?
  • In che modo lo sviluppatore sposta in modo efficiente le query avanti e indietro tra il loro editor SQL (SSMS, SQL Developer, ecc.) E il loro codice sorgente? Il prefisso @ Di C # rende tutto più semplice, ma ho visto un sacco di codice che cita ogni riga SQL e sfugge alle nuove righe. "SELECT col1, col2...colN"\ "FROM painfulExample"\ "WHERE maintainability IS NULL"\ "AND modification.effort > @necessary"\
  • I caratteri di rientro utilizzati per allineare l'SQL con il codice dell'applicazione circostante vengono trasmessi sulla rete ad ogni esecuzione. Ciò è probabilmente insignificante per le applicazioni su piccola scala, ma può aumentare man mano che aumenta l'utilizzo del software.

Gli ORM completi (mappatori di oggetti relazionali come Entity Framework o Hibernate) possono eliminare SQL distribuiti casualmente nel codice dell'applicazione. Il mio uso di SQL e file di risorse non è che un esempio. ORM, classi di supporto, ecc. Possono tutti aiutare a raggiungere l'obiettivo di un codice più pulito.

Come ha detto Kevin in una risposta precedente, SQL nel codice può essere accettabile in piccoli progetti, ma i progetti di grandi dimensioni iniziano come piccoli progetti e la probabilità che la maggior parte dei team tornerà indietro e lo farà correttamente è spesso inversamente proporzionale alla dimensione del codice.

Esistono molti modi semplici per mantenere SQL in un progetto. Uno dei metodi che utilizzo spesso è quello di inserire ogni istruzione SQL in un file di risorse di Visual Studio, generalmente denominato "sql". Un file di testo, un documento JSON o un'altra origine dati può essere ragionevole a seconda degli strumenti. In alcuni casi, una classe separata dedicata alla creazione di stringhe SQL può essere l'opzione migliore, ma potrebbe presentare alcuni dei problemi sopra descritti.

Esempio SQL: quale sembra più elegante?:

using(DbConnection connection = Database.SystemConnection()) {
    var eyesoreSql = @"
    SELECT
        Viewable.ViewId,
        Viewable.HelpText,
        PageSize.Width,
        PageSize.Height,
        Layout.CSSClass,
        PaginationType.GroupingText
    FROM Viewable
    LEFT JOIN PageSize
        ON PageSize.Id = Viewable.PageSizeId
    LEFT JOIN Layout
        ON Layout.Id = Viewable.LayoutId
    LEFT JOIN Theme
        ON Theme.Id = Viewable.ThemeId
    LEFT JOIN PaginationType
        ON PaginationType.Id = Viewable.PaginationTypeId
    LEFT JOIN PaginationMenu
        ON PaginationMenu.Id = Viewable.PaginationMenuId
    WHERE Viewable.Id = @Id
    ";
    var results = connection.Query<int>(eyesoreSql, new { Id });
}

diventa

using(DbConnection connection = Database.SystemConnection()) {
    var results = connection.Query<int>(sql.GetViewable, new { Id });
}

L'SQL è sempre in un file facile da individuare o in un gruppo raggruppato di file, ognuno con un nome descrittivo che descrive ciò che fa piuttosto che come lo fa, ciascuno con spazio per un commento che non interromperà il flusso del codice dell'applicazione:

SQL in a resource

Questo semplice metodo esegue una query solitaria. Nella mia esperienza, il vantaggio aumenta man mano che l'uso della "lingua straniera" diventa più sofisticato. Il mio uso di un file di risorse è solo un esempio. Diversi metodi possono essere più appropriati a seconda della lingua (in questo caso SQL) e della piattaforma.

Questo e altri metodi risolvono l'elenco sopra nel modo seguente:

  1. Il codice del database è facile da individuare perché è già centralizzato. Nei progetti più grandi, raggruppa like-SQL in file separati, magari in una cartella denominata SQL.
  2. Il supporto per un secondo, terzo, ecc. Database è più semplice. Creare un'interfaccia (o astrazione in un'altra lingua) che restituisca le dichiarazioni univoche di ciascun database. L'implementazione per ogni database diventa poco più di istruzioni simili a: return SqlResource.DoTheThing; È vero, queste implementazioni possono saltare la risorsa e contenere l'SQL in una stringa, ma alcuni problemi (non tutti) sopra potrebbero ancora emergere.
  3. Il refactoring è semplice: basta riutilizzare la stessa risorsa. È anche possibile utilizzare la stessa voce di risorsa per sistemi DBMS diversi per la maggior parte del tempo con poche istruzioni di formato. Lo faccio spesso.
  4. L'uso della lingua secondaria può usare descrittivo nomi, ad es. sql.GetOrdersForAccount Piuttosto che più ottuso SELECT ... FROM ... WHERE...
  5. Le istruzioni SQL vengono convocate con una riga indipendentemente dalle loro dimensioni e complessità.
  6. SQL può essere copiato e incollato tra strumenti di database come SSMS e SQL Developer senza modifiche o copie accurate. Nessuna virgoletta. Nessuna barra rovesciata finale. Nel caso specifico dell'editor di risorse di Visual Studio, un clic evidenzia l'istruzione SQL. CTRL + C e quindi incollalo nell'editor SQL.

La creazione di SQL in una risorsa è rapida, quindi c'è poco slancio per mescolare l'utilizzo delle risorse con SQL-in-code.

Indipendentemente dal metodo scelto, ho scoperto che la miscelazione delle lingue di solito riduce la qualità del codice. Spero che alcuni problemi e soluzioni qui descritti aiutino gli sviluppatori a eliminare questo odore di codice quando appropriato.

14
Charles Burns

Dipende. Esistono diversi approcci che possono funzionare:

  1. Modularizza l'SQL e isolalo in un insieme separato di classi, funzioni o qualsiasi unità di astrazione utilizzata dal tuo paradigma, quindi chiamalo con la logica dell'applicazione.
  2. Sposta tutti gli SQL complessi nelle viste, quindi esegui solo SQL molto semplici nella logica dell'applicazione, in modo da non dover modulare nulla.
  3. Utilizzare una libreria di mapping relazionale ad oggetto.
  4. YAGNI, basta scrivere SQL direttamente nella logica dell'applicazione.

Come spesso accade, se il tuo progetto ha già scelto una di queste tecniche, dovresti essere coerente con il resto del progetto.

(1) e (3) sono entrambi relativamente bravi a mantenere l'indipendenza tra la logica dell'applicazione e il database, nel senso che l'applicazione continuerà a compilare e superare test di fumo di base se si sostituisce il database con un altro fornitore. Tuttavia, la maggior parte dei fornitori non è completamente conforme allo standard SQL, quindi è probabile che la sostituzione di qualsiasi fornitore con qualsiasi altro fornitore richieda test approfonditi e ricerca dei bug, indipendentemente dalla tecnica utilizzata. Sono scettico sul fatto che questo sia un grosso problema come lo fanno le persone. La modifica dei database è fondamentalmente l'ultima risorsa quando non è possibile ottenere il database corrente per soddisfare le proprie esigenze. Se ciò accade, probabilmente hai scelto male il database.

La scelta tra (1) e (3) dipende principalmente da quanto ti piacciono gli ORM. Secondo me sono abusati. Sono una scarsa rappresentazione del modello di dati relazionali, poiché le righe non hanno identità nel modo in cui gli oggetti hanno identità. È probabile che si verifichino punti deboli attorno a vincoli, join univoci e si potrebbe avere difficoltà a esprimere alcune query più complicate a seconda della potenza dell'ORM. D'altra parte, (1) probabilmente richiederà molto più codice di un ORM.

(2) è raramente visto, nella mia esperienza. Il problema è che molti negozi proibiscono agli SWE di modificare direttamente lo schema del database (perché "questo è il lavoro del DBA"). Questa non è necessariamente una cosa negativa in sé e per sé; le modifiche allo schema hanno un potenziale significativo per la rottura delle cose e potrebbe essere necessario implementarle con cura. Tuttavia, affinché (2) funzioni, gli SWE dovrebbero almeno essere in grado di introdurre nuove viste e modificare le query di supporto delle viste esistenti con una burocrazia minima o nulla. Se non è così nel tuo luogo di lavoro, (2) probabilmente non funzionerà per te.

D'altra parte, se riesci a far funzionare (2), è molto meglio della maggior parte delle altre soluzioni perché mantiene la logica relazionale in SQL anziché nel codice dell'applicazione. A differenza dei linguaggi di programmazione per scopi generici, SQL è specificamente progettato per il modello di dati relazionali ed è di conseguenza migliore nell'esprimere query e trasformazioni di dati complessi. Le viste possono anche essere trasferite insieme al resto dello schema quando si modificano i database, ma renderanno tali spostamenti più complicati.

Per le letture, le procedure memorizzate sono fondamentalmente solo una versione più scadente di (2). Non li consiglio a tale titolo, ma potresti comunque volerli per le scritture, se il tuo database non supporta viste aggiornabili o se devi fare qualcosa di più complesso dell'inserimento o dell'aggiornamento di un riga singola alla volta (ad es. transazioni, lettura/scrittura, ecc.). Puoi associare la procedura memorizzata a una vista utilizzando un trigger (ovvero CREATE TRIGGER trigger_name INSTEAD OF INSERT ON view_name FOR EACH ROW EXECUTE PROCEDURE procedure_name;), ma le opinioni variano considerevolmente sul fatto che si tratti effettivamente di una buona idea. I sostenitori ti diranno che mantiene l'SQL che l'applicazione esegue nel modo più semplice possibile. I detrattori ti diranno che questo è un livello inaccettabile di "magia" e che dovresti semplicemente eseguire la procedura direttamente dalla tua applicazione. Direi che questa è un'idea migliore se la procedura memorizzata assomiglia o si comporta in modo molto simile a un INSERT, UPDATE o DELETE e un'idea peggiore se lo sta facendo qualcos'altro. Alla fine dovrai decidere tu stesso quale stile ha più senso.

(4) è la non soluzione. Potrebbe valerne la pena per piccoli progetti o grandi che interagiscono solo sporadicamente con il database. Ma per i progetti con un sacco di SQL, non è una buona idea perché potresti avere duplicati o variazioni della stessa query sparpagliati casualmente nell'applicazione, il che interferisce con la leggibilità e il refactoring.

13
Kevin

È considerato un anti-pattern per scrivere SQL nel codice sorgente?

Non necessariamente. Se leggi tutti i commenti qui troverai argomenti validi per le istruzioni SQL hardcoding nel codice sorgente.

Il problema si presenta con la posizione delle istruzioni . Se posizioni le istruzioni SQL in tutto il progetto, ovunque, probabilmente ignorerai alcuni dei principi SOLID che di solito ci impegniamo a seguire.

supponiamo che intendesse; o:

1) Utilizzare LINQ

o

2) Utilizzare le stored procedure per SQL

Non possiamo dire cosa intendesse dire. Tuttavia, possiamo indovinare. Ad esempio, il primo che mi viene in mente è blocco del fornitore . Le istruzioni SQL di hardcoding possono indurre ad associare strettamente l'applicazione al motore DB. Ad esempio, utilizzando funzioni specifiche del fornitore che non sono conformi ANSI.

Questo non è necessariamente sbagliato né cattivo. Sto solo indicando il fatto.

Ignorare SOLID e i blocchi dei fornitori possono avere conseguenze negative che potresti ignorare. Ecco perché di solito è bene sedersi con il team ed esporre i tuoi dubbi.

Il vantaggio delle procedure memorizzate credo sia che gli sviluppatori SQL possano essere coinvolti nel processo di sviluppo (scrittura di procedure memorizzate ecc.)

Penso che non abbia nulla a che fare con i vantaggi delle stored procedure. Inoltre, se al tuo collega non piace l'SQL codificato, è probabile che spostare l'attività nelle procedure memorizzate non piacerà anche a lui.

Modifica: il seguente link parla di istruzioni SQL codificate: https://docs.Microsoft.com/en-us/sql/odbc/reference/develop-app/hard-coded-sql-statements =. C'è qualche vantaggio nel preparare un'istruzione SQL?

Sì. Il post enumera i vantaggi di istruzioni preparate . È una sorta di modello SQL. Più sicuro della concatenazione di stringhe. Ma il post non ti incoraggia ad andare in questo modo né a confermare che hai ragione. Spiega semplicemente come possiamo usare SQL hardcoded in modo sicuro ed efficiente.

Riassumendo, prova prima a chiedere al tuo collega. Invia una mail, chiamalo, ... Sia che risponda o meno, siediti con la squadra ed esponi i tuoi dubbi. Trova la soluzione più adatta alle tue esigenze. Non fare false assunzioni basate su ciò che leggi là fuori.

8
Laiv

Penso che sia una cattiva pratica, sì. Altri hanno sottolineato i vantaggi di mantenere tutto il codice di accesso ai dati nel proprio livello. Non devi cercarlo, è più facile da ottimizzare e testare ... Ma anche all'interno di quel livello, hai alcune scelte: usare un ORM, usare sprocs o incorporare query SQL come stringhe. Direi che le stringhe di query SQL sono di gran lunga l'opzione peggiore.

Con un ORM, lo sviluppo diventa molto più semplice e meno soggetto a errori. Con EF, definisci il tuo schema semplicemente creando le classi del tuo modello (avresti comunque dovuto creare queste classi). Interrogare con LINQ è un gioco da ragazzi: spesso ti allontani con 2 righe di c # dove altrimenti dovresti scrivere e mantenere uno sproc. IMO, questo ha un enorme vantaggio in termini di produttività e manutenibilità: meno codice, meno problemi. Ma c'è un sovraccarico di prestazioni, anche se sai cosa stai facendo.

Sprocs (o funzioni) sono l'altra opzione. Qui, scrivi le tue query SQL manualmente. Ma almeno hai qualche garanzia che siano corretti. Se lavori in .NET, Visual Studio genererà anche errori del compilatore se SQL non è valido. Questo è fantastico Se modifichi o rimuovi alcune colonne e alcune delle tue query diventano non valide, almeno è probabile che le scoprirai durante la compilazione. È anche molto più semplice mantenere gli sprocs nei propri file: probabilmente otterrai l'evidenziazione della sintassi, completamento automatico ... ecc.

Se le query vengono archiviate come sprocs, è anche possibile modificarle senza ricompilare e ridistribuire l'applicazione. Se noti che qualcosa nel tuo SQL è rotto, un DBA può semplicemente risolverlo senza bisogno di avere accesso al codice dell'app. In generale, se le tue query sono in letterali stringa, non puoi dbas fare altrettanto facilmente.

Le query SQL come valori letterali delle stringhe renderanno anche meno leggibile il codice c # dell'accesso ai dati.

Proprio come una regola empirica, le costanti magiche sono cattive. Ciò include i letterali stringa.

3
Sava B.

Non è un anti-schema (questa risposta è pericolosamente vicina all'opinione). Ma la formattazione del codice è importante e la stringa SQL deve essere formattata in modo che sia chiaramente separata dal codice che la utilizza. Per esempio

    string query = 
    @"SELECT foo, bar
      FROM table
      WHERE id = @tn";
2
rleir

Molte grandi risposte qui. A parte ciò che è già stato detto, vorrei aggiungere un'altra cosa.

Non considero l'uso di SQL inline un anti-pattern. Quello che faccio, tuttavia, considero un anti-schema è che ogni programmatore fa le proprie cose. Devi decidere come una squadra su uno standard comune. Se tutti usano linq, allora usi linq, se tutti usano viste e stored procedure, lo fai.

Tieni sempre una mente aperta e non innamorarti dei dogmi. Se il tuo negozio è un negozio "linq", lo usi. Tranne quello in cui Linq non funziona assolutamente. (Ma non dovresti il ​​99,9% delle volte.)

Quindi quello che vorrei fare è ricercare il codice nella base di codice e controllare come funzionano i tuoi colleghi.

1
Pieter B

Non posso dirti cosa intendesse il tuo collega. Ma posso rispondere a questo:

C'è qualche vantaggio nel preparare un'istruzione SQL?

[~ ~ #] si [~ ~ #] . L'uso di istruzioni preparate con variabili associate è la difesa raccomandata contro SQL injection , che rimane il il più grande rischio per la sicurezza delle applicazioni Web per oltre un decennio . Questo attacco è così comune che è stato descritto in fumetti web quasi dieci anni fa, ed è giunto il momento di implementare difese efficaci.

... e la difesa più efficace alla cattiva concatenazione delle stringhe di query non è rappresentando le query come stringhe, ma utilizzare un'API di query sicura per i tipi per creare query. Ad esempio, ecco come scriverei la tua query in Java con QueryDSL:

List<UUID> ids = select(person.id).from(person).fetch();

Come puoi vedere, non c'è una sola stringa letterale qui, rendendo quasi impossibile l'iniezione di SQL. Inoltre, ho avuto il completamento del codice durante la scrittura di questo, e il mio IDE può refactoring se dovessi scegliere di rinominare mai la colonna id. Inoltre, posso facilmente trovare tutte le query per la tabella delle persone chiedendo IDE dove si accede alla variabile person. Oh, e hai notato che è un po 'più breve e più facile da vedere rispetto al tuo codice?

Posso solo supporre che qualcosa di simile sia disponibile anche per C #. Ad esempio, sento grandi cose su LINQ.

In sintesi, rappresentare le query come stringhe SQL rende difficile per IDE assistere in modo significativo nella scrittura e nel refactoring delle query, difende il rilevamento della sintassi e digita gli errori dal tempo di compilazione al tempo di esecuzione ed è un contributo causa di vulnerabilità nell'iniezione SQL [1]. Quindi sì, ci sono validi motivi per cui non si potrebbero desiderare stringhe SQL nel codice sorgente.

[1]: Sì, l'uso corretto delle istruzioni preparate impedisce anche l'iniezione di SQL. Ma che un'altra risposta in questo thread è stata vulnerabile a un'iniezione SQL fino a quando un commentatore non l'ha sottolineato non ispira fiducia nei programmatori junior che li usano correttamente ogni volta che è necessario ...

1
meriton

Per molte organizzazioni moderne con pipeline di distribuzione rapida in cui le modifiche al codice possono essere compilate, testate e portate alla produzione in pochi minuti, ha perfettamente senso incorporare le istruzioni SQL nel codice dell'applicazione. Questo non è necessariamente un anti-schema a seconda di come lo fai.

Un caso valido per l'utilizzo di procedure memorizzate oggi è nelle organizzazioni in cui vi è un sacco di processo attorno alle distribuzioni di produzione che può comportare settimane di cicli di controllo qualità ecc. In questo scenario il team DBA può risolvere i problemi di prestazioni che emergono solo dopo la distribuzione di produzione iniziale ottimizzando le query nelle stored procedure.

A meno che non ti trovi in ​​questa situazione, ti consiglio di incorporare il tuo SQL nel codice dell'applicazione. Leggi le altre risposte in questo thread per consigli sul modo migliore per procedere.


Testo originale di seguito per il contesto

Il vantaggio principale delle stored procedure è che è possibile ottimizzare le prestazioni del database senza ricompilare e ridistribuire l'applicazione.

In molte organizzazioni il codice può essere trasferito alla produzione solo dopo un ciclo di controllo qualità ecc. Che può richiedere giorni o settimane. Se il sistema sviluppa un problema di prestazioni del database a causa della modifica dei modelli di utilizzo o dell'aumento delle dimensioni della tabella ecc., Può essere piuttosto difficile da rintracciare il problema con query codificate, mentre il database avrà strumenti/report, ecc. che identificheranno le procedure memorizzate. Con le procedure memorizzate le soluzioni possono essere testate/confrontate nella produzione e il problema può essere risolto rapidamente senza dover spingere un nuova versione del software applicativo.

Se questo problema non è importante nel tuo sistema, allora è una decisione perfettamente valida inserire codice SQL nell'applicazione.

0
bikeman868

Il codice è simile al livello di interazione del database che si trova tra il database e il resto del codice. In tal caso, questo è no anti-pattern.

Questo metodo IS la parte del livello, anche se OP la pensa diversamente (accesso al database semplice, nessuna miscelazione con nient'altro). Forse è semplicemente collocato male nel codice. Questo metodo appartiene al classe separata, "livello di interfacciamento del database". Sarebbe anti-modello posizionarlo all'interno della classe ordinaria accanto ai metodi che implementano attività non di database.

L'anti-pattern sarebbe quello di chiamare SQL da qualche altra posizione casuale di codice. È necessario definire un livello tra il database e il resto del codice, ma non è assolutamente necessario utilizzare alcuni strumenti popolari che potrebbero esservi utili.

0
h22

Secondo me no non è un anti pattern ma ti troverai a occuparti della spaziatura di formattazione delle stringhe, specialmente per le grandi query che non possono rientrare in una riga.

un altro vantaggio è che diventa molto più difficile quando si gestiscono query dinamiche che dovrebbero essere costruite in base a determinate condizioni.

var query = "select * from accounts";

if(users.IsInRole('admin'))
{
   query += " join secret_balances";
}

Suggerisco di usare un sql query builder

0
amd