web-dev-qa-db-fra.com

Est-il considéré comme un anti-modèle pour écrire du SQL dans le code source?

Est-il considéré comme un anti modèle pour coder en dur SQL dans une application comme celle-ci:

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

J'aurais normalement une couche de référentiel, etc., mais je l'ai exclue dans le code ci-dessus pour plus de simplicité.

J'ai récemment reçu des commentaires d'un collègue qui s'est plaint que SQL était écrit dans le code source. Je n'ai pas eu la chance de demander pourquoi et il est maintenant absent pendant deux semaines (peut-être plus). Je suppose qu'il voulait dire soit:

  1. Utilisez LINQ
    ou
  2. Utiliser des procédures stockées pour le SQL

Ai-je raison? Est-il considéré comme un anti-modèle pour écrire du SQL dans le code source? Nous sommes une petite équipe travaillant sur ce projet. Je pense que l'avantage des procédures stockées est que les développeurs SQL peuvent s'impliquer dans le processus de développement (écriture de procédures stockées, etc.).

Modifier Le lien suivant parle des instructions SQL codées en dur: https://docs.Microsoft.com/en-us/sql/odbc/reference/develop-app/hard-coded-sql-statement . Y a-t-il un avantage à préparer une instruction SQL?

88
w0051977

Vous avez exclu la partie cruciale pour la simplicité. Le référentiel est la couche d'abstraction pour la persistance. Nous séparons la persistance en sa propre couche afin de pouvoir changer la technologie de persistance plus facilement quand nous en avons besoin. Par conséquent, avoir SQL en dehors de la couche de persistance annule complètement l'effort d'avoir une couche de persistance séparée.

En conséquence: SQL va bien dans la couche de persistance qui est spécifique à une technologie SQL (par exemple, SQL va bien dans un SQLCustomerRepository mais pas dans un MongoCustomerRepository). En dehors de la couche de persistance, SQL rompt votre abstraction et donc est considéré comme une très mauvaise pratique (par moi).

Quant aux outils comme LINQ ou JPQL: ceux-ci peuvent simplement résumer les saveurs de SQL. Avoir des requêtes LINQ-Code ou JPQL en dehors d'un référentiel rompt l'abstraction de persistance autant que le SQL brut.


Un autre avantage énorme d'une couche de persistance distincte est qu'elle vous permet de tester votre code logique métier sans avoir à configurer un serveur de base de données.

Vous obtenez un profil de mémoire bas, des tests unitaires rapides avec des résultats reproductibles sur toutes les plateformes prises en charge par votre langue.

Dans une architecture de service MVC +, il s'agit d'une simple tâche de se moquer de l'instance de référentiel, de créer des données fictives en mémoire et de définir que le référentiel doit renvoyer ces données fictives lorsqu'un certain getter est appelé. Vous pouvez ensuite définir les données de test par unité et ne vous souciez plus de nettoyer la base de données par la suite.

Le test d'écriture dans la base de données est aussi simple: vérifiez que les méthodes de mise à jour pertinentes sur la couche de persistance ont été appelées et vérifiez que les entités étaient dans le bon état lorsque cela s'est produit.

111
marstato

La plupart des applications métier standard utilisent aujourd'hui différentes couches avec différentes responsabilités. Cependant, quelles couches que vous utilisez pour votre application, et quelle couche a quelle responsabilité dépend de vous et de votre équipe. Avant de pouvoir décider s'il est bon ou mauvais de placer SQL directement dans la fonction que vous nous avez montrée, vous devez savoir

  • quelle couche dans votre application a quelle responsabilité

  • de quelle couche provient la fonction ci-dessus

Il n'y a pas de solution unique à cela. Dans certaines applications, les concepteurs préfèrent utiliser un cadre ORM et laisser le cadre générer tout SQL. Dans certaines applications, les concepteurs préfèrent stocker ce SQL exclusivement dans des procédures stockées. Pour certaines applications, il existe une couche de persistance (ou référentiel) manuscrite dans laquelle réside le SQL, et pour d'autres applications, il est correct de définir des exceptions dans certaines circonstances pour placer strictement SQL dans cette couche de persistance.

Alors, à quoi devez-vous penser: quelles couches voulez-vous vouloir ou besoin dans votre application particulière, et comment vous voulez-vous les responsabilités? Vous avez écrit "J'aurais normalement une couche de référentiel", mais quelles sont les responsabilités exactes que vous voulez avoir à l'intérieur de cette couche, et quelles responsabilités voulez-vous mettre ailleurs? Répondez d'abord à cela, puis vous pourrez répondre à votre question par vous-même.

55
Doc Brown

Marstato donne une bonne réponse mais j'aimerais ajouter quelques commentaires.

SQL dans la source n'est PAS un anti-modèle mais cela peut causer des problèmes. Je me souviens quand vous deviez placer des requêtes SQL dans les propriétés des composants déposés sur chaque formulaire. Cela a rendu les choses vraiment laides très rapidement et vous avez dû sauter à travers des cerceaux pour localiser les requêtes. Je suis devenu un ardent défenseur de la centralisation de l'accès à la base de données autant que possible dans les limites des langues avec lesquelles je travaillais. Votre collègue peut avoir des flashbacks sur ces jours sombres.

Maintenant, certains des commentaires parlent du verrouillage des fournisseurs comme si c'était automatiquement une mauvaise chose. Ça ne l'est pas. Si je signe chaque année un chèque à six chiffres pour utiliser Oracle, vous pouvez parier que je souhaite que toute application accédant à cette base de données utilise la syntaxe Oracle supplémentaire de manière appropriée mais au maximum. Je ne serai pas heureux si ma base de données brillante est paralysée par des codeurs qui écrivent mal Vanilla ANSI SQL lorsqu'il existe une "façon Oracle" d'écrire le SQL qui ne paralyse pas la base de données. Oui, la modification des bases de données sera plus difficile, mais je ne l'ai vu que sur un grand site client quelques fois en plus de 20 ans et l'un de ces cas a été transféré de DB2 -> Oracle parce que le mainframe qui hébergeait DB2 était obsolète et a été mis hors service . Oui, c'est le verrouillage du fournisseur, mais pour les entreprises, il est en fait souhaitable de payer pour un SGBDR capable coûteux comme Oracle ou Microsoft SQL Server, puis de l'utiliser au maximum. Vous avez un contrat d'assistance comme couverture de confort. Si je paie pour une base de données avec une implémentation de procédure stockée riche, je veux qu'elle soit utilisée dans les cas où cela a du sens.

Cela conduit au point suivant, si vous écrivez une application qui accède à une base de données SQL vous devez apprendre SQL ainsi que l'autre langue et par apprendre, je veux dire également l'optimisation des requêtes; Je serai en colère contre vous si vous écrivez du code de génération SQL qui vide le cache SQL avec un barrage de requêtes presque identiques alors que vous auriez pu utiliser une requête intelligemment paramétrée.

Pas d'excuses, pas de cachette derrière un mur d'Hibernate. Les ORM mal utilisés peuvent vraiment paralyser les performances des applications. Je me souviens avoir vu une question sur Stack Overflow il y a quelques années dans le sens de:

Dans Hibernate, je parcourt 250 000 enregistrements en vérifiant les valeurs de quelques propriétés et en mettant à jour des objets qui correspondent à certaines conditions. Il fonctionne un peu lentement, que puis-je faire pour l'accélérer?

Que diriez-vous de "UPDATE table SET field1 = où field2 est True et Field3> 100". La création et l'élimination de 250 000 objets pourraient bien être votre problème ...

c'est-à-dire Ignorer Hibernate lorsqu'il n'est pas approprié de l'utiliser. Comprenez la base de données.

Donc, en résumé, l'incorporation de SQL dans le code peut être une mauvaise pratique, mais vous pouvez finir par faire des choses bien pires pour éviter d'incorporer SQL.

33
mcottle

Oui, le codage en dur des chaînes SQL en code d'application est généralement un anti-modèle.

Essayons de mettre de côté la tolérance que nous avons développée après des années à voir cela dans le code de production. Mélanger des langues complètement différentes avec une syntaxe différente dans le même fichier n'est généralement pas une technique de développement souhaitable. C'est différent des langages de modèles comme Razor qui sont conçus pour donner un sens contextuel à plusieurs langues. Comme Sava B. mentionne dans un commentaire ci-dessous, SQL dans votre C # ou autre langage d'application (Python, C++, etc.) est une chaîne comme les autres et n'a pas de sens sémantique. La même chose s'applique lors du mélange de plusieurs langues dans la plupart des cas, bien qu'il y ait évidemment des situations où cela est acceptable, comme l'assemblage en ligne en C, de petits extraits compréhensibles de CSS en HTML (notant que CSS est conçu pour être mélangé avec HTML ), et d'autres.

Clean Code by Robert C. Martin, pg. 288 (Robert C. Martin sur le mélange des langues, Clean Code , Chapter 17, "Code Smells and Heuristics", page 288)

Pour cette réponse, je vais me concentrer sur SQL (comme demandé dans la question). Les problèmes suivants peuvent se produire lors du stockage de SQL en tant qu'ensemble à la carte de chaînes dissociées:

  • La logique de la base de données est difficile à localiser. Que recherchez-vous pour trouver toutes vos instructions SQL? Des chaînes avec "SELECT", "UPDATE", "MERGE", etc.?
  • La refactorisation des utilisations du même SQL ou d'un langage SQL similaire devient difficile.
  • L'ajout de la prise en charge d'autres bases de données est difficile. Comment accomplirait-on cela? Ajouter des instructions if..then pour chaque base de données et stocker toutes les requêtes sous forme de chaînes dans la méthode?
  • Les développeurs lisent une déclaration dans une autre langue et se laissent distraire par le changement d'orientation de l'objectif de la méthode vers les détails d'implémentation de la méthode (comment et d'où les données sont récupérées).
  • Bien que les lignes simples ne soient pas trop problématiques, les chaînes SQL en ligne commencent à se désagréger à mesure que les instructions deviennent plus complexes. Que faites-vous avec un relevé de 113 lignes? Mettez les 113 lignes dans votre méthode?
  • Comment le développeur déplace-t-il efficacement les requêtes entre son éditeur SQL (SSMS, développeur SQL, etc.) et son code source? Le préfixe @ De C # facilite cela, mais j'ai vu beaucoup de code qui cite chaque ligne SQL et échappe aux sauts de ligne. "SELECT col1, col2...colN"\ "FROM painfulExample"\ "WHERE maintainability IS NULL"\ "AND modification.effort > @necessary"\
  • Les caractères d'indentation utilisés pour aligner le SQL avec le code d'application environnant sont transmis sur le réseau à chaque exécution. Ceci est probablement insignifiant pour les applications à petite échelle, mais il peut s'additionner à mesure que l'utilisation du logiciel augmente.

Les ORM complets (mappeurs objet-relationnels comme Entity Framework ou Hibernate) peuvent éliminer le SQL réparti de manière aléatoire dans le code d'application. Mon utilisation de SQL et de fichiers de ressources n'est qu'un exemple. Les ORM, les classes d'assistance, etc. peuvent tous aider à atteindre l'objectif d'un code plus propre.

Comme Kevin l'a dit dans une réponse précédente, SQL dans le code peut être acceptable dans les petits projets, mais les grands projets commencent comme de petits projets, et la probabilité que la plupart des équipes reviennent et le font correctement est souvent inversement proportionnelle à la taille du code.

Il existe de nombreuses façons simples de conserver SQL dans un projet. L'une des méthodes que j'utilise souvent consiste à placer chaque instruction SQL dans un fichier de ressources Visual Studio, généralement nommé "sql". Un fichier texte, un document JSON ou une autre source de données peut être raisonnable selon vos outils. Dans certains cas, une classe distincte dédiée à la synchronisation des chaînes SQL peut être la meilleure option, mais pourrait présenter certains des problèmes décrits ci-dessus.

Exemple SQL: qui a l'air plus élégant?:

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

Devient

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

Le SQL est toujours dans un fichier facile à localiser ou un ensemble de fichiers groupés, chacun avec un nom descriptif qui décrit ce qu'il fait plutôt que comment il le fait, chacun avec un espace pour un commentaire cela n'interrompra pas le flux de code d'application:

SQL in a resource

Cette méthode simple exécute une requête solitaire. D'après mon expérience, les avantages évoluent à mesure que l'utilisation de la "langue étrangère" devient plus sophistiquée. Mon utilisation d'un fichier de ressources n'est qu'un exemple. Différentes méthodes peuvent être plus appropriées en fonction de la langue (SQL dans ce cas) et de la plateforme.

Cette méthode et d'autres résolvent la liste ci-dessus de la manière suivante:

  1. Le code de la base de données est facile à localiser car il est déjà centralisé. Dans les projets plus volumineux, groupez like-SQL dans des fichiers séparés, peut-être sous un dossier nommé SQL.
  2. La prise en charge d'une deuxième, troisième, etc. bases de données est plus facile. Créez une interface (ou une autre abstraction de langage) qui renvoie les instructions uniques de chaque base de données. L'implémentation de chaque base de données devient un peu plus que des instructions similaires à: return SqlResource.DoTheThing; Certes, ces implémentations peuvent ignorer la ressource et contenir le SQL dans une chaîne, mais certains (pas tous) problèmes ci-dessus feraient toujours surface.
  3. La refactorisation est simple - il suffit de réutiliser la même ressource. Vous pouvez même utiliser la même entrée de ressource pour différents systèmes SGBD la plupart du temps avec quelques instructions de format. Je le fais souvent.
  4. L'utilisation de la langue secondaire peut utiliser descriptif noms par exemple sql.GetOrdersForAccount Plutôt que plus obtus SELECT ... FROM ... WHERE...
  5. Les instructions SQL sont appelées sur une seule ligne, quelles que soient leur taille et leur complexité.
  6. SQL peut être copié et collé entre des outils de base de données comme SSMS et SQL Developer sans modification ni copie soigneuse. Pas de guillemets. Pas de barre oblique inverse à la fin. Dans le cas de l'éditeur de ressources Visual Studio en particulier, un clic met en surbrillance l'instruction SQL. CTRL + C, puis collez-le dans l'éditeur SQL.

La création de SQL dans une ressource est rapide, il y a donc peu d'impulsion pour mélanger l'utilisation des ressources avec SQL en code.

Quelle que soit la méthode choisie, j'ai constaté que le mélange des langues réduit généralement la qualité du code. J'espère que certains problèmes et solutions décrits ici aident les développeurs à éliminer cette odeur de code le cas échéant.

14
Charles Burns

Ça dépend. Il existe un certain nombre d'approches différentes qui peuvent fonctionner:

  1. Modularisez le SQL et isolez-le dans un ensemble distinct de classes, de fonctions ou de toute unité d'abstraction utilisée par votre paradigme, puis appelez-le avec la logique d'application.
  2. Déplacez tous les SQL complexes dans des vues, puis ne faites que du SQL très simple dans la logique d'application, de sorte que vous n'avez rien à modulariser.
  3. Utilisez une bibliothèque de mappage relationnel-objet.
  4. YAGNI, il suffit d'écrire SQL directement dans la logique d'application.

Comme c'est souvent le cas, si votre projet a déjà choisi l'une de ces techniques, vous devez être cohérent avec le reste du projet.

(1) et (3) sont tous deux relativement bons pour maintenir l'indépendance entre la logique d'application et la base de données, dans le sens où l'application continuera à compiler et à passer des tests de fumée de base si vous remplacez la base de données par un autre fournisseur. Cependant, la plupart des fournisseurs ne sont pas entièrement conformes à la norme SQL, donc le remplacement d'un fournisseur par un autre fournisseur nécessitera probablement des tests approfondis et une recherche de bogues, quelle que soit la technique que vous utilisez. Je suis sceptique que ce soit aussi important que les gens le prétendent. La modification des bases de données est essentiellement un dernier recours lorsque vous ne pouvez pas obtenir la base de données actuelle pour répondre à vos besoins. Si cela se produit, vous avez probablement mal choisi la base de données.

Le choix entre (1) et (3) est principalement une question de combien vous aimez les ORM. À mon avis, ils sont surutilisés. Ils sont une mauvaise représentation du modèle de données relationnel, car les lignes n'ont pas d'identité de la même manière que les objets ont une identité. Vous risquez de rencontrer des points faibles autour de contraintes uniques, de jointures et vous pouvez avoir des difficultés à exprimer des requêtes plus compliquées en fonction de la puissance de l'ORM. D'un autre côté, (1) nécessitera probablement beaucoup plus de code qu'un ORM.

(2) est rarement vu, selon mon expérience. Le problème est que de nombreux magasins interdisent aux SWE de modifier directement le schéma de la base de données (car "c'est le travail du DBA"). Ce n'est pas nécessairement une mauvaise chose en soi; les modifications de schéma ont un potentiel significatif de rupture et peuvent devoir être déployées avec soin. Cependant, pour que (2) fonctionne, les SWE devraient au moins être en mesure d'introduire de nouvelles vues et de modifier les requêtes de support des vues existantes avec une bureaucratie minimale ou nulle. Si ce n'est pas le cas sur votre lieu de travail, (2) ne fonctionnera probablement pas pour vous.

D'un autre côté, si vous pouvez faire fonctionner (2), c'est beaucoup mieux que la plupart des autres solutions car il conserve la logique relationnelle en SQL au lieu du code d'application. Contrairement aux langages de programmation à usage général, SQL est spécialement conçu pour le modèle de données relationnel et est donc mieux à même d'exprimer des requêtes et des transformations de données complexes. Les vues peuvent également être portées avec le reste de votre schéma lors du changement de bases de données, mais elles rendront ces mouvements plus compliqués.

Pour les lectures, les procédures stockées ne sont fondamentalement qu'une version plus folle de (2). Je ne les recommande pas à ce titre, mais vous pouvez toujours les vouloir pour les écritures, si votre base de données ne prend pas en charge vues pouvant être mises à jour , ou si vous devez faire quelque chose de plus complexe que d'insérer ou de mettre à jour un une seule ligne à la fois (par exemple transactions, lecture-écriture, etc.). Vous pouvez coupler votre procédure stockée à une vue à l'aide d'un déclencheur (c'est-à-dire CREATE TRIGGER trigger_name INSTEAD OF INSERT ON view_name FOR EACH ROW EXECUTE PROCEDURE procedure_name;), mais les opinions varient considérablement quant à savoir s'il s'agit réellement d'une bonne idée. Les promoteurs vous diront qu'il maintient le SQL que votre application exécute aussi simple que possible. Les détracteurs vous diront qu'il s'agit d'un niveau inacceptable de "magie" et que vous devez simplement exécuter la procédure directement à partir de votre application. Je dirais que c'est une meilleure idée si votre procédure stockée ressemble ou agit beaucoup comme un INSERT, UPDATE ou DELETE, et une pire idée si elle le fait autre chose. En fin de compte, vous devrez décider par vous-même quel style a le plus de sens.

(4) est la non-solution. Cela peut valoir la peine pour les petits projets ou les grands qui n'interagissent que sporadiquement avec la base de données. Mais pour les projets avec beaucoup de SQL, ce n'est pas une bonne idée car vous pouvez avoir des doublons ou des variations de la même requête dispersés au hasard dans votre application, ce qui nuit à la lisibilité et au refactoring.

13
Kevin

Est-il considéré comme un anti-modèle pour écrire du SQL dans le code source?

Pas nécessairement. Si vous lisez tous les commentaires ici, vous trouverez des arguments valides pour les instructions SQL de codage en dur dans le code source.

Le problème vient de l'endroit où vous placez les instructions . Si vous placez les instructions SQL partout dans le projet, partout, vous ignorerez probablement certains des principes SOLID que nous nous efforçons généralement de suivre.

supposons qu'il voulait dire; Soit:

1) Utilisez LINQ

ou

2) Utilisez des procédures stockées pour le SQL

Nous ne pouvons pas dire ce qu'il voulait dire. Cependant, nous pouvons deviner. Par exemple, le premier qui me vient à l'esprit est verrouillage du fournisseur . Le codage en dur des instructions SQL peut vous amener à coupler étroitement votre application au moteur de base de données. Par exemple, utiliser des fonctions spécifiques du fournisseur qui ne sont pas conformes à ANSI.

Ce n'est pas nécessairement mauvais ni mauvais. Je souligne simplement le fait.

Ignorer SOLID et les verrous des fournisseurs peuvent avoir des conséquences négatives que vous pourriez ignorer. C'est pourquoi il est généralement bon de s'asseoir avec l'équipe et d'exposer vos doutes.

L'avantage des procédures stockées est, je pense, que les développeurs SQL peuvent s'impliquer dans le processus de développement (écriture de procédures stockées, etc.)

Je pense que cela n'a rien à voir avec les avantages des procédures stockées. De plus, si votre collègue n'aime pas le SQL codé en dur, il est probable que le déplacement de l'entreprise vers des procédures stockées ne lui plaira pas non plus.

Edit: Le lien suivant parle des instructions SQL codées en dur: https://docs.Microsoft.com/en-us/sql/odbc/reference/develop-app/hard-coded-sql-statements =. Y a-t-il un avantage à préparer une instruction SQL?

Oui. La publication énumère les avantages des déclarations préparées . C'est une sorte de modèle SQL. Plus sûr que la concaténation de chaînes. Mais le message ne vous encourage pas à suivre cette voie ni ne confirme que vous avez raison. Il explique simplement comment utiliser SQL codé en dur de manière sûre et efficace.

Pour résumer, essayez d'abord de demander à votre collègue. Envoyez-lui un mail, téléphonez-lui, ... Qu'il réponde ou non, asseyez-vous avec l'équipe et exposez vos doutes. Trouvez la solution qui correspond le mieux à vos besoins. Ne faites pas de fausses hypothèses sur la base de ce que vous lisez.

8
Laiv

Je pense que c'est une mauvaise pratique, oui. D'autres ont souligné les avantages de conserver tous vos codes d'accès aux données dans sa propre couche. Vous n'avez pas à chercher pour cela, il est plus facile d'optimiser et de tester ... Mais même au sein de cette couche, vous avez quelques choix: utiliser un ORM, utiliser des sprocs ou incorporer des requêtes SQL en tant que chaînes. Je dirais que les chaînes de requête SQL sont de loin la pire option.

Avec un ORM, le développement devient beaucoup plus facile et moins sujet aux erreurs. Avec EF, vous définissez votre schéma simplement en créant vos classes de modèle (vous auriez quand même eu besoin de créer ces classes). L'interrogation avec LINQ est un jeu d'enfant - vous vous en sortez souvent avec 2 lignes de c # où vous auriez autrement besoin d'écrire et de maintenir un sproc. OMI, cela a un énorme avantage en termes de productivité et de maintenabilité - moins de code, moins de problèmes. Mais il y a une surcharge de performances, même si vous savez ce que vous faites.

Les sprocs (ou fonctions) sont l'autre option. Ici, vous écrivez vos requêtes SQL manuellement. Mais au moins, vous obtenez une garantie qu'ils sont corrects. Si vous travaillez dans .NET, Visual Studio générera même des erreurs de compilation si le SQL n'est pas valide. C'est bien. Si vous modifiez ou supprimez une colonne et que certaines de vos requêtes deviennent invalides, au moins vous êtes susceptible de le découvrir lors de la compilation. Il est également beaucoup plus facile de conserver les sprocs dans leurs propres fichiers - vous obtiendrez probablement la mise en évidence de la syntaxe, la saisie semi-automatique..etc.

Si vos requêtes sont stockées en tant que sprocs, vous pouvez également les modifier sans recompiler ni redéployer l'application. Si vous remarquez que quelque chose dans votre SQL est cassé, un DBA peut simplement le réparer sans avoir besoin d'avoir accès à votre code d'application. En général, si vos requêtes sont en littéraux de chaîne, vous ne pouvez pas faire leur travail presque aussi facilement.

Les requêtes SQL en tant que littéraux de chaîne rendront également votre code c # d'accès aux données moins lisible.

En règle générale, les constantes magiques sont mauvaises. Cela inclut les littéraux de chaîne.

3
Sava B.

Ce n'est pas un anti-pattern (cette réponse est dangereusement proche de l'opinion). Mais le formatage du code est important, et la chaîne SQL doit être formatée de manière à être clairement distincte du code qui l'utilise. Par exemple

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

Beaucoup de bonnes réponses ici. Outre ce qui a déjà été dit, je voudrais ajouter une autre chose.

Je ne considère pas l'utilisation de SQL en ligne comme un anti-modèle. Ce que je considère cependant comme un anti-pattern, c'est que chaque programmeur fait sa propre chose. Vous devez décider en équipe d'une norme commune. Si tout le monde utilise linq, alors vous utilisez linq, si tout le monde utilise des vues et des procédures stockées, vous le faites.

Gardez toujours un esprit ouvert et ne tombez pas dans les dogmes. Si votre boutique est une boutique "linq", vous l'utilisez. Sauf pour cet endroit où linq ne fonctionne absolument pas. (Mais vous ne devriez pas 99,9% du temps.)

Donc, ce que je ferais, c'est rechercher le code dans la base de code et vérifier comment fonctionnent vos collègues.

1
Pieter B

Je ne peux pas vous dire ce que votre collègue voulait dire. Mais je peux répondre à ceci:

Y a-t-il un avantage à préparer une instruction SQL?

[~ # ~] oui [~ # ~] . L'utilisation d'instructions préparées avec des variables liées est la défense recommandée contre injection SQL , qui reste le risque de sécurité le plus important pour les applications Web depuis plus d'une décennie . Cette attaque est si courante qu'elle figurait dans bandes dessinées Web il y a près de dix ans, et il est grand temps de déployer des défenses efficaces.

... et la défense la plus efficace contre une mauvaise concaténation de chaînes de requête n'est pas de représenter les requêtes en tant que chaînes en premier lieu, mais d'utiliser une API de requête sécurisée pour créer des requêtes. Par exemple, voici comment j'écrirais votre requête en Java avec QueryDSL:

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

Comme vous pouvez le voir, il n'y a pas un seul littéral de chaîne ici, ce qui rend impossible l'injection SQL. De plus, le code a été complété lors de l'écriture, et mon IDE peut le refactoriser si je choisis de renommer la colonne id. De plus, je peux facilement trouver toutes les requêtes pour la table person en demandant à mon IDE où l'on accède à la variable person. Oh, et avez-vous remarqué qu'elle est un peu plus courte et plus agréable à regarder que votre code?

Je peux seulement supposer que quelque chose comme ça est également disponible pour C #. Par exemple, j'entends beaucoup de choses sur LINQ.

En résumé, la représentation des requêtes sous forme de chaînes SQL complique la tâche de IDE pour aider de manière significative à l'écriture et à la refactorisation des requêtes, diffère la détection des erreurs de syntaxe et de type de la compilation à l'exécution, et contribue à cause des vulnérabilités d'injection SQL [1]. Donc oui, il y a des raisons valables pour lesquelles on peut ne pas vouloir de chaînes SQL dans le code source.

[1]: Oui, l'utilisation correcte des instructions préparées empêche également l'injection SQL. Mais qu'une autre réponse dans ce fil était vulnérable à une injection SQL jusqu'à ce qu'un commentateur souligne que cela n'inspire pas confiance aux programmeurs juniors qui les utilisent correctement chaque fois que nécessaire ...

1
meriton

Pour de nombreuses organisations modernes dotées de pipelines de déploiement rapide où les modifications de code peuvent être compilées, testées et mises en production en quelques minutes, il est tout à fait logique d'incorporer des instructions SQL dans le code d'application. Ce n'est pas nécessairement un anti-modèle selon la façon dont vous vous y prenez.

Un cas valable pour l'utilisation de procédures stockées aujourd'hui est dans les organisations où il y a beaucoup de processus autour des déploiements de production qui peuvent impliquer des semaines de cycles d'assurance qualité, etc. Dans ce scénario, l'équipe DBA peut résoudre les problèmes de performances qui n'apparaissent qu'après le déploiement de production initial en optimisant les requêtes dans les procédures stockées.

À moins que vous ne soyez dans cette situation, je vous recommande d'incorporer votre SQL dans votre code d'application. Lisez les autres réponses de ce fil pour obtenir des conseils sur la meilleure façon de procéder.


Texte original ci-dessous pour le contexte

Le principal avantage des procédures stockées est que vous pouvez optimiser les performances de la base de données sans recompiler ni redéployer votre application.

Dans de nombreuses organisations, le code ne peut être poussé en production qu'après un cycle d'assurance qualité, etc., ce qui peut prendre des jours ou des semaines. Si votre système développe un problème de performances de base de données en raison de la modification des modèles d'utilisation ou de l'augmentation de la taille des tables, etc., cela peut être assez difficile de localiser le problème avec les requêtes codées en dur, alors que la base de données aura des outils/rapports, etc. qui identifieront les procédures stockées problématiques. Avec les procédures stockées, les solutions peuvent être testées/comparées en production et le problème peut être résolu rapidement sans avoir à pousser un nouvelle version du logiciel d'application.

Si ce problème n'est pas important dans votre système, c'est une décision parfaitement valide de coder en dur les instructions SQL dans l'application.

0
bikeman868

Le code ressemble à la couche d'interaction avec la base de données qui se trouve entre la base de données et le reste du code. Si c'est vraiment le cas, c'est pas anti-pattern.

Cette méthode IS la partie de la couche, même si OP pense différemment (accès simple à la base de données, pas de mélange avec autre chose). Elle est peut-être juste mal placée dans le code. Cette méthode appartient à la classe distincte, "couche d'interfaçage de base de données". Il serait anti-modèle de le placer dans la classe ordinaire à côté des méthodes qui implémentent des activités non liées à la base de données.

L'anti-modèle serait d'appeler SQL à partir d'un autre endroit aléatoire de code. Vous devez définir une couche entre la base de données et le reste du code, mais ce n'est pas que vous devez absolument utiliser un outil populaire qui pourrait vous y aider.

0
h22

À mon avis non n'est pas un anti-modèle mais vous vous retrouverez face à l'espacement de formatage des chaînes, en particulier pour les grandes requêtes qui ne peuvent pas tenir sur une seule ligne.

un autre avantage est qu'il devient beaucoup plus difficile de traiter des requêtes dynamiques qui doivent être construites selon certaines conditions.

var query = "select * from accounts";

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

Je suggère d'utiliser un générateur de requêtes SQL

0
amd