Является ли LinqToSQL достаточно мощным? Не является ли более мощный, но столь же удобный интерфейс, который легко построить?

В предыдущем вопросе я спросил, являются ли библиотеки ORM субоптимальными решениями и получили много отличных отзывов. Как я уже думал о проблеме, я пришел к выводу, что основное преимущество LinqToSQL (сейчас) и обещание LinqToEntities (в будущем) заключается в их способности сократить "несоответствие" между процедурным кодом и SQL. Учитывая ограничения LinqToSQL как полного языка запросов данных, я не склонен согласиться с тем, что его преимущества намного превосходят его, но я хотел бы узнать мнение других.

Чтобы начать, что я имею в виду под "несоответствием" между процедурным кодом и SQL? Если вы когда-либо разрабатывали приложение базы данных, то вы, вероятно, знакомы с (и устали) от шагов, необходимых для взаимодействия с базой данных: настройте все параметры, введите команду SQL, а затем вручную преобразуйте ожидаемые результаты обратно в переменные или экземпляры, используемые в коде.

LinqToSql делает это намного проще с помощью "плавных" интерфейсов, лямбда-выражений, методов расширения и т.д. Конечным результатом является то, что легко вытащить собственные скалярные типы С# в запрос поиска данных и легко автоматически генерировать собственные С#. Возьмите этот пример с веб-сайта MS:

var q =
   from c in db.Customers
   where c.City == "London"
   select c;
foreach (var cust in q)
   Console.WriteLine("id = {0}, City = {1}",cust.CustomerID, cust.City);

Очень круто!

Итак, в чем проблема?

Хорошо, как я указал в статье, связанной с наверху, есть ряд проблем. Самый большой шоу-стоппер для меня заключался в том, что любой, кто минимально компетентен в SQL, сразу же попадает в блокпосты. Дело не только в том, что многие из конструкций, созданных как альтернативы SQL, незнакомы или даже неуклюжи (Group By? Мы говорим Ugly в LinqToSql). Большая проблема заключается в том, что существует ряд общих конструкций, которые просто не поддерживаются изначально (например, DateDiff). В лучшем случае вы можете "выйти из" Linq и отправить код SQL в поток вызовов Linq.

Итак, не лучше ли просто встраивать SQL в С# (или VB) способами, которые позволяют свободно указывать ваш SQL, но также дают вам легкое вложение параметров и автоматическое преобразование в родные классы? Например, если мы реализуем только шесть функций в одном классе, мы можем написать что-то вроде этого (где qry - это экземпляр нашего класса запросов):

var ListOfEnrollees = 
           qry.Command("Select b.FirstName, b.LastName, b.ID From ClassEnroll a inner join Folder b on (a.ClientID = b.ClientID) ")
             .Where ("a.ClassID", classID)
             .AND()
             .Append("(DateDiff(d, a.ClassDate, @ClassDate) = 0) Order By LastName;")
             .ParamVal(classDate)
             .ReturnList<EnrollListMemberData>();

Команда просто заставляет нас не собирать инструкцию SQL, где начинается наше предложение SQL Where и вводится первый параметр, добавляет tacks на большее количество SQL, ParamVal просто вводит значение параметра в соответствие с параметром SQL, найденным в предыдущей строке, и ReturnList делает магию, необходимую для преобразования в класс. Обратите внимание, что это просто SQL: объединения тривиальны, поддерживается DateDiff (или любая другая конструкция SQL) и т.д. Мы можем протестировать его в окне SQL, а затем просто вырезать и вставлять в наш код, разбивая его по мере необходимости для ввода наши параметры. Это не может быть проще.

Теперь базовая конструкция, показанная выше, кажется мне противным, и она может обрабатывать любую конструкцию SQL, используя знания SQL, которые у меня уже есть. Мне пришлось использовать некоторое отражение и некоторые другие интересные новые конструкторы С# 3, чтобы сделать вызовы ReturnList (и подобных), но в целом это было довольно просто. Я также добавил несколько колоколов и свистов, чтобы сделать интерфейс более свободным. Итак, вот мой вопрос и где мне бы хотелось услышать комментарии сообщества:

Почему мне когда-либо понадобилось освоить тонкости LinqToEntities или даже нанести накладные расходы LinqToSql? Разве это в значительной степени не дает мне все, что LinqToSql дает мне и больше? Не покрывает ли он даже LinqToEntities, за исключением экзотических привязок к сопоставлениям объектов?

Обновить. Хэмиш Смит утверждает ниже, что LINQ может когда-нибудь быть полностью способным манипулировать данными, настолько мощным, что SQL даже не нужен. Это важный аргумент, который я не обсуждал, но с которым согласен. Суть моей позиции заключается в том, что, хотя у LinqToSql есть некоторые практические преимущества, она все еще далеко отстает от цели Хэмиша. Это очень реальный и существенный недостаток.

Хэмиш также правильно утверждает, что я все еще работаю со встроенным SQL - я не "решил" проблему. Для меня, однако, это особенность, а не ошибка. SQL по-прежнему гораздо лучше выбирает и обрабатывает реляционные данные (а это, в конце концов, то, с чем мы работаем), которые я хочу использовать для создания моего решения. Я утверждаю, что встроенный SQL не является проблемой, так как импедансное несоответствие между ним и С#. Однако, используя новые, созданные Linq функции С# 3, я могу в значительной степени устранить это "несоответствие импеданса" и получить лучшее из обоих миров.

Ответ 1

Итак, не лучше ли просто вставлять SQL в С# (или VB) способами, которые позволяют вы свободно указываете свой SQL, но также дают вам легкое вложение параметров и автоматическое преобразование в классы?

То, что я обычно делаю, очень близко к этому. Я пишу SQL в хранимой процедуре, поэтому я получаю свободу реального SQL. В С# я использую Linq DBML для естественного доступа к хранимым процедурам. Это похоже на лучшее из обоих миров.

Реальное решение выглядит еще проще для меня: внедряйте SQL в код С# и создайте компилятор сильных классов. Если Visual Studio поддерживает это, я сомневаюсь, что кто-то будет использовать что-нибудь еще.

(Всегда есть люди, которые пытаются создать Entity Frameworks и "архитектуру" из-за необходимости SQL. Насколько мне известно, это никогда не работало, хотя бы потому, что результат значительно сложнее поддерживать и замедлять работу.)

Ответ 2

Я думаю, что это слишком много, как LinqToSQL, так и LinqToEntities. Я принял минималистский подход. Я разработал DAL, который использует DTO и отражает его свойства для генерации SQL "на лету". Он работает в 95% случаев, когда мне не нужен, и я не хочу думать о SQL. Для всех остальных случаев мой DAL позволяет мне написать любой SQL, который я хочу, и возвращает либо DataSet, либо если он предоставлен DTO, он заполнит его результатами. Он также позволяет вызывать хранимые процедуры с той же концепцией, возвращая DataSet или DTO. Это работает очень хорошо для меня, и мне не нужно видеть SQL, если я не хочу.

Ответ 3

linq2sql имеет встроенную поддержку подкачки и строго типизирован.

Вы можете избежать использования отслеживания объектов, если вы его не используете.

Вы можете вызвать функции sql, переведя его в метод.

Если вы используете отслеживание объектов, вы можете легко выполнить несколько вызовов обработки данных (делает это автоматически, таким образом, вызов SubmitChanges). Таким образом, вы не попадаете в базу данных 15 раз, чтобы сделать 5 вставок и 10 обновлений.

Это также удобно, когда вы хотите получить структурированную информацию, например:

var someInfo = from s in Context.Somethings
               select new SomeInfo
               {
                   AField = s.SomeColumn,
                   RelatedStuff = s.RelatedInfo.Where(someconditionhere)
               };

Я бы сказал, что это более чем того стоит. Также обратите внимание, если вам действительно нужно получить максимальную производительность, вы можете скомпилировать выражения, и это будет соответствовать скорости datareader.

Ответ 4

Что вы предлагаете в своем классе запросов, так это то, что вы все равно вставляете SQL непосредственно в код приложения. Что вы получаете с решением LINQ + ORM, так это то, что код приложения никогда не содержит SQL. У вас все еще есть несоответствие и отвлечение от работы над кодом. Вы упомянули об этом в своем сообщении:

Мы можем проверить это в окне SQL и затем просто вырезать и вставлять в наш код, разбив его по мере необходимости, чтобы ввести наши параметры. Это не может быть проще.

Синтаксис LINQ пытается дать нам запросы в качестве первоклассных граждан в коде. Да, некоторые вещи неуклюжи, некоторые вещи, возможно, не совсем правильные, но заявленная цель LINQ - объединить код запроса и приложения в точку, где нет непересекающегося. Похоже, это не то, что вам нужно, поэтому, возможно, встраивание SQL в строковые литералы, непрозрачные для компилятора, и время выполнения будет работать лучше для вас. Мы знаем, что его можно заставить работать, мы это делали годами.
Другое преимущество заключается в том, что оператор LINQ не будет подвергаться конкретным изменениям поставщика (что проблема поставщика LINQ). Вы можете получить строку SQL, которую вы передаете qry.Command(), привязанной к конкретной базе данных.

Ответ 5

var q = DataContext.ExecuteQuery <Etc> ( "SELECT * FROM и т.д." ) обычно выполняет довольно хорошую работу.

Ответ 6

На данный момент Я не поклонник Entity Framework, но 4.0 выглядит значительно улучшенным. Я не уверен, что это так же сильно изменилось, как Vista против Windows 7, но определенно лучше.

В настоящее время я нахожу, что LINQ-to-SQL охватывает большинство вещей, которые я регулярно нуждаюсь. Heck, даже если я просто использую его для подключения хранимых процедур без необходимости писать код параметра, что в моих интересах. Я нахожу, что материал CRUD отлично подходит для простой обработки ортогональных данных. Для более сложных сценариев мне нравятся UDF и т.д., Которые LINQ-to-SQL отлично поддерживают.

Пока вы скрываете его в репозитории, вы даже не рисуете себя в углу.

Re датифик - посмотрели ли вы на SqlMethods?