Czy wbudowany SQL nadal jest klasyfikowany jako zła praktyka, skoro mamy Micro ORM?

26

To trochę otwarte pytanie, ale chciałem mieć pewne opinie, ponieważ dorastałem w świecie, w którym wbudowane skrypty SQL były normą, wtedy wszyscy byliśmy bardzo świadomi problemów opartych na wstrzykiwaniu SQL i tego, jak delikatny był SQL, kiedy wykonując manipulacje strunami w dowolnym miejscu.

Potem przyszedł początek ORM, w którym wyjaśniłeś zapytanie ORM i pozwoliłeś mu wygenerować własny SQL, który w wielu przypadkach nie był optymalny, ale był bezpieczny i łatwy. Kolejną dobrą rzeczą dotyczącą ORM lub warstw abstrakcji bazy danych było to, że SQL został wygenerowany z myślą o jego silniku bazy danych, więc mogłem używać Hibernate / Nhibernate z MSSQL, MYSQL, a mój kod nigdy się nie zmieniał, to był tylko szczegół konfiguracji.

Teraz, szybko do przodu, do dnia dzisiejszego, w którym Micro ORM wydają się wygrywać z większą liczbą programistów. Zastanawiałem się, dlaczego najwyraźniej podjęliśmy zwrot w całej sprawie in-line sql.

Muszę przyznać, że podoba mi się pomysł braku plików konfiguracyjnych ORM i możliwość napisania zapytania w bardziej optymalny sposób, ale wydaje mi się, że otwieram się na stare luki, takie jak wstrzykiwanie SQL, a także przywiązuję się do jeden silnik bazy danych, więc jeśli chcę, aby moje oprogramowanie obsługiwało wiele mechanizmów baz danych, musiałbym zrobić trochę więcej hackerów łańcuchów, które wydają się wtedy powodować, że kod staje się nieczytelny i delikatniejszy. (Tuż przed tym, jak ktoś o tym wspomina, wiem, że możesz używać argumentów opartych na parametrach w większości mikro orms, które w większości przypadków zapewniają ochronę przed wstrzyknięciem sql)

Więc jakie są opinie ludzi na ten temat? W tym przypadku używam Dappera jako mojej Micro ORM, a NHibernate jako mojej zwykłej ORM w tym scenariuszu, jednak większość w każdym polu jest dość podobna.

Co nazywam inline sqlto ciągi SQL w kodzie źródłowym. Kiedyś debaty projektowe nad ciągami SQL w kodzie źródłowym naruszały podstawową intencję logiki, dlatego statycznie wpisywane zapytania w stylu linq stały się tak popularne, że wciąż mają tylko 1 język, ale powiedzmy C # i Sql na jednej stronie, którą masz 2 języki wplecione w twój surowy kod źródłowy. Aby wyjaśnić, wstrzyknięcie SQL jest tylko jednym ze znanych problemów z użyciem ciągów SQL, już wspominam, że można temu zapobiec przy zapytaniach opartych na parametrach, ale podkreślam inne problemy z zakodowaniem zapytań SQL w kodzie źródłowym, takie jak brak abstrakcji dostawcy DB oraz utrata jakiegokolwiek poziomu błędu kompilacji przechwytywania w zapytaniach opartych na łańcuchach, to wszystko problemy, które udało nam się przejść z początkiem ORM z ich funkcją zapytań wyższego poziomu,

Dlatego mniej skupiam się na poszczególnych wyróżnionych problemach, a im większy obraz, tym bardziej, że teraz bardziej akceptowalne jest ponowne umieszczanie ciągów SQL bezpośrednio w kodzie źródłowym, ponieważ większość Micro ORM używa tego mechanizmu.

Oto podobne pytanie, które ma kilka różnych punktów widzenia, chociaż bardziej dotyczy wbudowanego sql bez kontekstu mikro orm:

/programming/5303746/is-inline-sql-hard-coding

Grofit
źródło
1
Czy uważasz, że możesz przeformułować pytanie w taki sposób, aby nie wymagało opinii? Sondowanie opinii nie pasuje dobrze do formatu pytań i odpowiedzi Stack Exchange.
Zawsze możesz wyodrębnić swoje zapytania „Micro ORM” w osobnej klasie, gdzie w razie potrzeby zamieniasz wykonywane zapytania podczas zmiany DBMS.
CodeCaster,
nie słyszałem terminu „Micro ORM”. masz na myśli ORM, które nie próbują całkowicie przejąć SQL, czy jest to coś innego?
Javier
nieważne, po małym googlowaniu wydaje się, że jest to rzecz .net.
Javier
1
@GrandmasterB: Wyjaśnij, dlaczego w odpowiedzi. Prawie omijałem tę kwestię w mojej odpowiedzi, ponieważ uważam, że jest to kwestia kompromisów.
Robert Harvey

Odpowiedzi:

31

To, co opisujesz jako „Inline SQL”, powinno naprawdę nazywać się „konkatenacją łańcucha bez parametryzacji” i nie musisz tego robić, aby bezpiecznie używać Micro ORM.

Rozważ ten przykład Dappera:

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

Jest w pełni sparametryzowany, nawet jeśli ma miejsce konkatenacja łańcucha. Widzisz znak @?

Lub ten przykład:

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

co odpowiada mniej więcej następującemu kodowi ADO.NET:

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

Jeśli potrzebujesz większej elastyczności, Dapper zapewnia szablony SQL i AddDynamicParms()funkcję. Wszystkie SQL Injection są bezpieczne.

Po co więc używać ciągów SQL?

Z tych samych powodów użyjesz niestandardowego SQL w dowolnym innym ORM. Być może ORM generuje kod nieoptymalny SQL i trzeba go zoptymalizować. Być może chcesz zrobić coś, co jest trudne do zrobienia w ORM, np. UNION. A może po prostu chcesz uniknąć złożoności generowania wszystkich tych klas proxy.

Jeśli naprawdę nie chcesz pisać łańcucha SQL dla każdej metody CRUD w Dapper (kto to robi?), Możesz użyć tej biblioteki:

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

Dzięki temu uzyskasz wyjątkowo prostą i prostą CRUD, a jednocześnie zapewni elastyczność odręcznych instrukcji SQL. Pamiętaj, że powyższy przykład ADO.NET był sposobem, w jaki wszyscy to zrobili, zanim pojawił się ORM; Dapper to po prostu cienki fornir.

Robert Harvey
źródło
Dobre przykłady, już wspomniałem, że można ominąć wstrzyknięcie SQL za pomocą parametrów, ponieważ jest to tylko jeden z większych pytań, dodałem również edycję wyjaśniającą, co mam na myśli, gdy używam terminu inline sql .
Grofit
Dodałem trochę szczegółów do mojej odpowiedzi.
Robert Harvey
2
+1 za informację o SimpleCRUD nie wiedziałem, że to istnieje.
Grofit,
W Javie znajdziesz Mybatis, który jest ligther niż Hibernate lub EclipseLink i. Jego drogą są predefiniowane zdania w XML (zamiast kodowania na stałe w kodzie). Dodaje także kilka interesujących funkcji jako warunek wstępny do ulepszenia końcowego SQL. Niedawno wykorzystaliśmy interfejs API, który ma dość wysoką współbieżność i nieskomplikowany biznes i działał świetnie.
Laiv
2

Uwielbiam sql i nie mogłem znieść widoku, jak jest posiekany w literały łańcuchowe, więc napisałem małe rozszerzenie VS, aby umożliwić ci pracę z prawdziwymi plikami sql w projektach c #. Edycja sql we własnym pliku zapewnia inteligencję dla kolumn i tabel, sprawdzanie poprawności składni, wykonywanie testów, plany wykonania i resztę. Kiedy zapisujesz plik, moje rozszerzenie generuje klasy otoki c #, więc nigdy nie piszesz wiersza kodu połączenia, kodu polecenia ani parametru lub kodu czytnika. Twoje zapytania są sparametryzowane, ponieważ nie ma innego wyjścia, a Ty wygenerowałeś repozytoria i POCO do testów jednostkowych, z inteligencją dla parametrów wejściowych i wyników.

I wrzuciłem bezpłatne noże do steków ... Kiedy ekspert musi przyjść i przerobić sql twojego dewelopera, patrzy na prawdziwy plik sql i nie musi dotykać żadnego C #. Kiedy wraca i porządkuje DB, usuwając niektóre kolumny, odniesienia do brakujących kolumn odskakują jak błędy kompilacji w c #. Baza danych staje się jak inny projekt w twoim rozwiązaniu, do którego możesz się włamać, jest to interfejs wykrywalny w kodzie. Jeśli rozwiązanie się kompiluje, wiesz, że wszystkie Twoje zapytania działają. Nie ma błędów środowiska wykonawczego z powodu nieprawidłowych rzutowań, niepoprawnych nazw kolumn lub nieprawidłowych zapytań.

Patrząc wstecz na elegancki, którego wciąż używamy w pracy, nie mogę uwierzyć, że w 2016 roku jest to najfajniejsza rzecz w dostępie do danych. Generowanie msil w czasie wykonywania jest sprytne, ale wszystkie błędy są błędami w czasie wykonywania, a manipulowanie ciągiem, podobnie jak manipulowanie ciągiem w innym celu, jest czasochłonne, kruche i podatne na błędy. I jak może być Dry, aby powtarzać nazwy kolumn w POCO, które musisz pisać i utrzymywać ręcznie.

Więc proszę bardzo. Jeśli chcesz spojrzeć na nieznaną technologię dostępu do danych od niespotykanego dewelopera pracującego w wolnym czasie (z małym dzieckiem i wieloma innymi hobby), to tutaj .

bbsimonbb
źródło
Wydaje się, że dużo myślisz o swojej małej „innowacji”, ale czym to się różni od, powiedzmy, ExecuteStoreQuery lub ExecuteStoreCommand w Entity Framework?
Robert Harvey
Nie wchodzę w debatę na temat EF ani SQL. Moja sprawa jest dla ludzi, którzy chcą używać SQL. Więc jako sposób na wykonanie SQL, ExecuteStoreQuery () wypełni twoje POCO dla Ciebie, ale nadal musisz zdefiniować swoje POCO na podstawie wyglądu? I nie robi nic, aby pomóc Ci umieścić SQL w pliku lub utworzyć parametry. Twoje zapytanie nie jest wykrywalne w kodzie ani testowalne. Usunięcie kolumn nie spowoduje błędów kompilacji w aplikacji. Mógłbym kontynuować, ale obawiam się, że się powtarzam :-) Być może zajmie Ci to 3 minuty na obejrzenie filmu na youtube. Jest po francusku, ścisz dźwięk! Angielski nadchodzi.
bbsimonbb
EF jest w stanie automatycznie generować wszystkie POCO. Czego tu brakuje? Prawdziwą innowacją Dapper i Massive jest to, że wcale nie potrzebujesz POCO; możesz po prostu użyć dynamic.
Robert Harvey
Nie wiem prawie nic o EF. ExecuteStoreQuery () wypełni jednostkę. Wygeneruje podmiot z kwerendy . Jednostki są generowane z obiektów DB (lub odwrotnie), a nie z zapytań. Jeśli twoje zapytanie nie odpowiada istniejącemu podmiotowi, musisz napisać swój POCO, nie?
bbsimonbb
1
:-) muszę powiedzieć, że jestem wrażliwy na opłaty reklamowe, kiedy swobodnie udostępniam swój najlepszy pomysł. Oto moja najnowsza agresywna komercja. Po 3 minutach powinieneś wiedzieć, czy coś lubię.
bbsimonbb
1

Sparametryzowane zapytania, a także wzorzec projektu repozytorium dają pełną kontrolę nad tym, co wchodzi w skład zapytania, aby zapobiec atakom wstrzykiwania. Inline SQL w tym przypadku nie jest problemem innym niż czytelność w przypadku dużych i złożonych zapytań, które i tak powinny znajdować się w procedurach przechowywanych.

Kyle JV
źródło