Чи вбудований SQL досі вважається поганою практикою зараз, коли ми маємо мікро ORM?


26

Це дещо відкрите запитання, але я хотів отримати деякі думки, оскільки я виріс у світі, де вбудовані сценарії SQL були нормою, то всі ми були дуже обізнані про проблеми, що базуються на інжекції SQL, і наскільки крихким було sql, коли робити всюди стринг-маніпуляції.

Потім настала світанок ORM, де ви пояснювали запит до ORM і дозволяли йому генерувати свій власний SQL, який у багатьох випадках не був оптимальним, але був безпечним і легким. Ще одна гарна річ щодо шарів ORM або абстракції баз даних полягала в тому, що SQL генерувався з урахуванням його двигуна бази даних, тому я міг використовувати Hibernate / Nhibernate з MSSQL, MYSQL, і мій код ніколи не змінювався, це була лише деталь конфігурації.

Зараз швидко вперед до поточного дня, коли Micro ORM, здається, перемагає більше розробників, мені було цікаво, чому ми, здавалося б, взяли поворот на всю лінію-тематику sql.

Я мушу визнати, що мені подобається ідея відсутності файлів конфігурації ORM і можливість написати запит більш оптимальним чином, але відчувається, що я відкриваю себе назад до старих уразливостей, таких як інжекція SQL, і я також прив'язуюсь до один двигун бази даних, тому якщо я хочу, щоб моє програмне забезпечення підтримувало декілька двигунів баз даних, мені потрібно було б зробити ще кілька рядкових хакерів, які, здається, потім почнуть робити код нечитабельним і більш крихким. (Перед тим, як хтось це згадає, я знаю, що ви можете використовувати аргументи на основі параметрів з більшістю мікроорганізмів, що забезпечує захист у більшості випадків від sql введення)

То які думки народів щодо такого роду? Я використовую Dapper як мій Micro ORM в цьому випадку і NHibernate як мій звичайний ORM в цьому сценарії, проте більшість у кожній галузі досить схожі.

Що я називаю вбудованим sql- це рядки SQL у вихідному коді. Раніше дизайнерські дебати над рядками SQL у вихідному коді відштовхувались від основного наміру логіки, саме тому статично набрані запити стилю linq стали настільки популярними, як і раніше, лише 1 мова, але з можливістю сказати C # і Sql на одній сторінці Зараз у вашому вихідному вихідному коді 2 мови змішані. Для уточнення, ін'єкція SQL - це лише одна з відомих проблем використання рядків sql, я вже згадую, що ви можете зупинити це з запитами на основі параметрів, однак я виділяю інші проблеми із вбудованим SQL-запитом у вихідний код, наприклад відсутність абстракції постачальника баз даних, а також втрата будь-якого рівня помилки часу компіляції під час запиту на основі рядкових запитів, це все питання, яким нам вдалося перейти до світанку ORM з їх функціональністю запитів вищого рівня,

Тому я менш зосереджений на окремих висвітлених питаннях, і тим більше уявлення про те, що тепер стає більш прийнятним знову мати рядки SQL безпосередньо у своєму вихідному коді, оскільки більшість Micro ORM використовують цей механізм.

Ось подібне питання, яке має кілька різних точок зору, хоча йдеться більше про вбудований sql без контексту мікроорганізмів:

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


1
Як ви думаєте, ви могли б переформулювати питання таким чином, щоб воно не запитувало думку? Опитування думок не вписується добре у формат Q&A Stack Exchange.

Ви завжди можете абстрагувати запити "Micro ORM" в окремому класі, де за необхідності ви поміняєте місцями запити, що виконуються під час зміни вашої СУБД.
CodeCaster

Ви не чули терміна "Micro ORM". Ви маєте на увазі ОРМ, які не намагаються повністю перейняти SQL, чи це щось інше?
Хав'єр

ніколи не забудьте, після невеликого гуглювання, здається, це річ .net.
Хав’єр

1
@GrandmasterB: Поясніть, чому відповідь. Я досить сильно обійшов цю проблему у своїй відповіді, оскільки вважаю, що це питання компромісів.
Роберт Харві

Відповіді:


31

Те, що ви описуєте як "вбудований SQL", справді слід називати "конкатенацією рядків без параметризації", і вам не потрібно робити це, щоб безпечно використовувати Micro ORM.

Розглянемо цей приклад Dapper:

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

Він повністю параметризований, навіть незважаючи на те, що відбувається конкатенація рядків. Бачите знак @?

Або цей приклад:

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

що приблизно еквівалентно наступному коду 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 });
        }
    }
}

Якщо вам потрібна більша гнучкість, ніж ця, Dapper надає шаблони SQL і AddDynamicParms()функцію. Всі безпечні ін'єкції SQL.

То чому б в першу чергу використовувати рядки SQL?

Ну, з тих же причин ви б використовували власні SQL в будь-якому іншому ORM. Можливо, ORM генерує код суб-оптимальний SQL, і вам потрібно його оптимізувати. Можливо, ви хочете зробити щось, що важко зробити в ОРМ споконвічно, як, наприклад, UNION. Або, можливо, ви просто хочете уникнути складності створення всіх цих проксі-класів.

Якщо ви дійсно не хочете писати рядок SQL для кожного методу CRUD на Dapper (хто це робить?), Ви можете використовувати цю бібліотеку:

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

Це дозволить отримати надзвичайно простий і простий CRUD, при цьому надаючи вам гнучкість рукописних заяв SQL. Пам'ятайте, приклад ADO.NET, описаний вище, - це те, як це робили всі до того, як ORM з'явився; Dapper - це лише тонкий шпон.


Хороші приклади, я вже згадував, що ви можете обійти SQL-ін'єкцію за допомогою параметрів, оскільки це лише одна деталь більшого питання, також додав редагування, що пояснює, що я маю на увазі, коли я використовую термін вбудований sql .
Грофіт

Я доклав трохи деталей до своєї відповіді.
Роберт Харві

2
+1 для інформації про SimpleCRUD не знав, що це існує.
Грофіт

На Java ви знайдете Mybatis, який довший, ніж сплячий або EclipseLink та. Її шлях - заздалегідь визначені речення в XML (замість жорсткого кодування на код). Він також додає деякі цікаві функції як передумови для покращення кінцевого SQL. Нещодавно ми використовували в Інтернеті API, який має досить високу конкурентоспроможність та не складний бізнес, і він чудово працював.
Лаїв

2

Мені подобається sql , і я не витримав, побачивши, як він порубаний рядковими літералами, тому я написав невелике розширення VS, щоб ви могли працювати з реальними файлами sql в c # проектах. Редагування sql у власному файлі дає вам інтелігенцію для стовпців і таблиць, перевірку синтаксису, тестове виконання, плани виконання та інше. Коли ви зберігаєте файл, моє розширення генерує c # обгорткові класи, тому ви ніколи не пишете рядок коду підключення, або командний код, або параметр чи код зчитування. Усі ваші запити параметризовані, оскільки іншого способу немає, і ви створили сховища та POCO для тестування одиниць, з інтелігенцією для вхідних параметрів та результатів.

І я кинув у вільні стейкові ножі ... Коли експерт повинен прийти і переробити sql вашого розробника, вона переглядає справжній файл sql, і не потрібно торкатися жодного C #. Коли вона повертається назад і прибирає БД, видаляючи деякі стовпці, посилання на відсутні стовпці вискакують прямо як помилки компіляції в c #. База даних стає подібно до іншого проекту у вашому рішенні, який ви можете зламати, це інтерфейс, який можна знайти в коді. Якщо рішення компілюється, ви знаєте, що всі ваші запити працюють. Немає помилок під час виконання через невірні касти або неправильні назви стовпців або недійсні запити.

Оглянувшись на Dapper, який ми все ще використовуємо на роботі, я не можу повірити, що в 2016 році це найкрутіша річ у доступі до даних. Генерація msil у процесі виконання розумна, але всі помилки - це помилки під час виконання, а маніпуляція з рядками, як і маніпуляція з рядками для будь-яких інших цілей, забирає багато часу, неміцна та схильна до помилок. І як можна сушити повторювати назви своїх стовпців у своєму POCO, які ви повинні писати та підтримувати вручну.

Так що ви йдете. Якщо ви хочете подивитися на невідому технологію доступу до даних від нечуваного розробника, який працює у вільний час (з маленькою дитиною та безліччю інших захоплень), тут все .


Ви, здається, вважаєте багато своїх маленьких "нововведень", але чим це відрізняється від, скажімо, ExecuteStoreQuery або ExecuteStoreCommand в Entity Framework?
Роберт Харві

Я не вступаю в дискусії щодо EF або SQL. Моя річ - для людей, які хочуть використовувати SQL. Отже, як спосіб виконання SQL, ExecuteStoreQuery () заповнить ваші POCO, але ви все ще повинні визначити свої POCO, за зовнішнім виглядом речей? І це нічого не допоможе вам поставити sql у файл або створити параметри. Ваш запит не можна знайти в коді або перевірити. Видалення стовпців не призведе до помилок компіляції у вашій програмі. Я міг би продовжувати, але я боюся, що я повторююсь :-) Можливо, ви могли б взяти 3 хвилини, щоб переглянути відео на YouTube. Це французькою мовою, зменшіть звук! Англійська йде.
bbsimonbb

EF цілком здатний генерувати всі POCO автоматично. Що я тут пропускаю? Справжня інновація Dapper і Massive полягає в тому, що вам взагалі не потрібні POCO; ви можете просто використовувати dynamic.
Роберт Харві

Я майже нічого не знаю про EF. ExecuteStoreQuery () заповнить об'єкт. Чи це генерує сутність із запиту . Суб'єкти генеруються з об'єктів БД (або навпаки), а не з запитів. Якщо ваш запит не відповідає існуючому об'єкту, ви повинні написати свій POCO, ні?
bbsimonbb

1
:-) повинен сказати, що я ставлюсь чутливим до витрат на рекламу, коли я вільно викладаю свою найкращу ідею. Ось мій останній шматочок агресивного комерціалізму. До 3-хвилинної позначки ви повинні знати, чи я на щось, чи ні.
bbsimonbb

1

Параметризовані запити, а також модель дизайну сховища дають вам повний контроль над тим, що входить у запит, щоб запобігти ін'єкційним атакам. Вбудований SQL в цьому випадку не є іншим питанням, ніж читабельність для великих і складних запитів, які так чи інакше повинні бути в збережених процедурах.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.