web-dev-qa-db-fra.com

Le SQL en ligne est-il toujours considéré comme une mauvaise pratique maintenant que nous avons des micro ORM?

C'est un peu une question ouverte mais je voulais des opinions, car j'ai grandi dans un monde où les scripts SQL en ligne étaient la norme, puis nous avons tous été très conscients des problèmes basés sur l'injection SQL et de la fragilité du SQL quand faire des manipulations de cordes partout.

Puis vint l'aube de l'ORM où vous expliquiez la requête à l'ORM et le laissiez générer son propre SQL, ce qui dans de nombreux cas n'était pas optimal mais était sûr et facile. Une autre bonne chose au sujet des ORM ou des couches d'abstraction de base de données était que le SQL a été généré avec son moteur de base de données à l'esprit, donc je pouvais utiliser Hibernate/Nhibernate avec MSSQL, MYSQL et mon code n'a jamais changé, c'était juste un détail de configuration.

Maintenant, passons rapidement à la journée actuelle, où les Micro ORM semblent gagner plus de développeurs.Je me demandais pourquoi nous avions apparemment fait demi-tour sur l'ensemble du sujet SQL en ligne.

Je dois admettre que j'aime l'idée de ne pas avoir de fichiers de configuration ORM et de pouvoir écrire ma requête de manière plus optimale, mais j'ai l'impression de m'ouvrir aux anciennes vulnérabilités telles que l'injection SQL et je m'attache également à un moteur de base de données, donc si je veux que mon logiciel prenne en charge plusieurs moteurs de base de données, je devrais faire un peu plus de piratage de chaînes qui semble alors commencer à rendre le code illisible et plus fragile. (Juste avant que quelqu'un ne le mentionne, je sais que vous pouvez utiliser des arguments basés sur des paramètres avec la plupart des micro orms qui offrent une protection dans la plupart des cas contre l'injection SQL)

Alors, quelles sont les opinions des gens sur ce genre de chose? J'utilise Dapper comme Micro ORM dans ce cas et NHibernate comme ORM normal dans ce scénario, mais la plupart dans chaque domaine sont assez similaires.

Ce que j'appelle sql en ligne est une chaîne SQL dans le code source. Il y avait autrefois des débats sur la conception des chaînes SQL dans le code source, ce qui détournait l'intention fondamentale de la logique, c'est pourquoi les requêtes de style linq typées de manière statique sont devenues si populaires que ce n'est encore qu'un langage, mais avec disons C # et Sql sur une seule page, vous avez 2 langues entremêlées dans votre code source brut maintenant. Juste pour clarifier, l'injection SQL n'est qu'un des problèmes connus avec l'utilisation de chaînes sql, je mentionne déjà que vous pouvez empêcher cela de se produire avec des requêtes basées sur des paramètres, mais je souligne d'autres problèmes liés à l'intégration des requêtes SQL dans votre code source, telles que le manque d'abstraction du fournisseur de base de données ainsi que la perte de tout niveau de capture d'erreur de temps de compilation sur les requêtes basées sur des chaînes, ce sont tous des problèmes que nous avons réussi à contourner avec l'aube des ORM avec leur fonctionnalité de requête de niveau supérieur, comme HQL ou LINQ, etc. (pas tous les problèmes mais la plupart).

Je suis donc moins concentré sur les problèmes individuels mis en évidence et de plus en plus, il devient maintenant plus acceptable d'avoir à nouveau des chaînes SQL directement dans votre code source, car la plupart des Micro ORM utilisent ce mécanisme.

Voici une question similaire qui a quelques points de vue différents, mais qui concerne plus le sql en ligne sans le contexte micro orm:

https://stackoverflow.com/questions/5303746/is-inline-sql-hard-coding

27
Grofit

Ce que vous décrivez comme "Inline SQL" devrait vraiment être appelé "concaténation de chaînes sans paramétrage", et vous n'avez pas à le faire pour utiliser un Micro ORM en toute sécurité.

Considérez cet exemple Dapper:

string sql = "SELECT * from user_profile WHERE FirstName LIKE @name;";
var result = connection.Query<Profile>(sql, new {name = "%"+name+"%"});

Il est entièrement paramétré, même si la concaténation de chaînes a lieu. Voir le signe @?

Ou cet exemple:

var dog = connection.Query<Dog>("select Age = @Age, Id = @Id", 
          new { Age = (int?)null, Id = guid });

qui est à peu près équivalent au code ADO.NET suivant:

List<Dog> dog = new List<Dog>();
using(var cmd = connection.CreateCommand()) {
    cmd.CommandText = "select Age = @Age, Id = @Id";
    cmd.Parameters.AddWithValue("Age", DBNull.Value);
    cmd.Parameters.AddWithValue("Id", guid);
    using(var reader = cmd.ExecuteReader()) {
        while(reader.Read()) {
            int age = reader.ReadInt32("Age");
            int id = reader.ReadInt32("Id");
            dog.Add(new Dog { Age = age, Id = id });
        }
    }
}

Si vous avez besoin de plus de flexibilité que cela, Dapper fournit des modèles SQL et une fonction AddDynamicParms(). Tous les injections SQL sécurisées.

Alors pourquoi utiliser des chaînes SQL en premier lieu?

Eh bien, pour les mêmes raisons, vous utiliseriez du SQL personnalisé dans n'importe quel autre ORM. L'ORM génère peut-être du code SQL sous-optimal et vous devez l'optimiser. Peut-être que vous voulez faire quelque chose de difficile à faire nativement dans l'ORM, comme les UNION. Ou, vous voulez peut-être simplement éviter la complexité de générer toutes ces classes proxy.

Si vous ne voulez vraiment pas écrire une chaîne SQL pour chaque méthode CRUD dans Dapper (qui le fait?), Vous pouvez utiliser cette bibliothèque:

https://github.com/ericdc1/Dapper.SimpleCRUD/

Cela vous permettra d'obtenir un CRUD extrêmement simple et direct, tout en vous offrant la flexibilité des instructions SQL manuscrites. Rappelez-vous, l'exemple ADO.NET ci-dessus était la façon dont tout le monde le faisait avant l'arrivée des ORM; Dapper n'est qu'un mince placage par-dessus.

31
Robert Harvey

J'adore sql , et je ne pouvais pas supporter de le voir découpé en chaînes de caractères, j'ai donc écrit une petite extension VS pour vous permettre de travailler avec de vrais fichiers sql dans les projets c #. La modification de SQL dans son propre fichier vous donne Intellisense pour les colonnes et les tables, la validation de la syntaxe, l'exécution des tests, les plans d'exécution et le reste. Lorsque vous enregistrez le fichier, mon extension génère des classes wrapper c #, de sorte que vous n'écrivez jamais une ligne de code de connexion, ou code de commande, ou paramètre ou code de lecteur. Vos requêtes sont toutes paramétrisées car il n'y a pas d'autre moyen, et vous avez généré des référentiels et des POCO pour les tests unitaires, avec intellisense pour vos paramètres d'entrée et vos résultats.

Et j'ai jeté des couteaux à steak gratuits ... Quand un expert doit venir retravailler le sql de votre développeur, elle regarde un vrai fichier sql, et n'a pas besoin de toucher à n'importe quel C #. Quand elle revient en arrière et nettoie la base de données, en supprimant certaines colonnes, les références aux colonnes manquantes sautent directement sous la forme d'erreurs de compilation dans le c #. La base de données devient tout comme un autre projet de votre solution que vous pouvez pirater, c'est une interface détectable dans le code. Si la solution se compile, vous savez que toutes vos requêtes fonctionnent. Il n'y a aucune erreur d'exécution due à des transtypages non valides, à des noms de colonnes non valides ou à des requêtes non valides.

En repensant à Dapper, que nous utilisons toujours au travail, je ne peux pas croire qu'en 2016, c'est la chose la plus cool en matière d'accès aux données. La génération de msil d'exécution est astucieuse, mais toutes les erreurs sont des erreurs d'exécution et la manipulation de chaînes, comme la manipulation de chaînes à d'autres fins, prend du temps, est fragile et sujette aux erreurs. Et comment peut-il être sec d'avoir à répéter les noms de vos colonnes dans votre POCO, que vous devez écrire et maintenir à la main.

Alors voilà. Si vous voulez regarder une technologie d'accès aux données inconnue d'un développeur inconnu travaillant dans ses temps libres (avec un jeune enfant et beaucoup d'autres passe-temps), c'est ici.

2
bbsimonbb

Les requêtes paramétrées ainsi que le modèle de conception du référentiel vous donnent un contrôle total sur ce qui est inclus dans la requête pour empêcher les attaques par injection. Inline SQL dans ce cas n'est pas un problème autre que la lisibilité pour les requêtes volumineuses et complexes, qui devraient de toute façon être dans les procédures stockées.

1
Kyle J V