Сравнение Controllable Query с Dapper
каждый параметр упоминается дваждыдля не динамических запросов это не требуется:
new {Age = 1.Param Id = Guid.NewGuid.Param}.Invoke(
p => "select Age = @Age, Id = @Id".Query<Dog>(p.Execute(connection);
но сложности, генерирующей запутанность, нетЕсли ты так говоришь, не могу спорить.
Хотя для меня первая строчка выглядит логично, а вот вторая — извращенно.
Первую строчку я могу прочитать, как если бы это было на естественном языке:
"у connection-а вызвать query, возвращающий Dog, с SQL телом <SQL> и параметрами {Age=1, Id = Guid.NewGuid}"
Вторая строчка:
"Взять объект с полями {Age = 1 как параметр, Id = Guid.NewGuid как параметр} , затем у него вызвать Invoke с делегатом, возвращающим строчку <SQL>, ой нет, не строчку, а ее extension-метод, превращающий ее в Query, возвращающую Dog, с параметром {Age=Age, Id = Id}, получится какая-то исполняемая хреновина, которую затем надо исполнить над connection-ом."
То есть надо читать не слева направо, не справа налево, а как-то шиворот-навыворот и кверху ногами: тело запроса в середине, значения параметров в начале, а connection в конце.
Кстати, не совсем понял, у тебя что, привносится extension-method object.Invoke( ... ) ?
То есть надо читать не слева направо, не справа налево, а как-то шиворот-навыворот и кверху ногами: тело запроса в середине, значения параметров в начале, а connection в конце.Слушай, круто ты всё расписал. Подскажи котанам, а как ты это интерпретируешь?
oreach (var record in QExecutor.MaterializeReader(
command => new {
BillOfMaterialsHeaderId = command.Param => int.Parse(billOfMaterialsHeaderId
ModelPublicationId = command.Param => int.Parse(modelPublicationId
ServicePartsPageId = command.Param => int.Parse(servicePartsPageId
redCondition,
redOn = Func.Invoke(
=> {
if (redCondition)
{
Console.Write("LanguageCode=");
var languageCode = Console.ReadLine;
Console.Write("ModelId=");
var modelId = Console.ReadLine;
Console.Write("BlueCondition=");
var blueCondition = bool.Parse(Console.ReadLine;
return new {
languageCode = command.Param(languageCode
modelId = command.Param => int.Parse(modelId
blueCondition
}.AsOption;
}
else
return new {
languageCode = default(IParam<string>
modelId = default(IParam<int>
blueCondition = default(bool)
}.Nothing;
})
},
p => {
var q = new Q(@"
FROM BillOfMaterialsLine
WHERE BillOfMaterialsHeaderId = ")._(p.BillOfMaterialsHeaderId)._(@"
AND ( ( BillOfMaterialsLine.MainLinkExcludedFrom = 0
AND EXISTS ( SELECT 1
FROM MainPartLink
WHERE MainPartLink.PartNumber = BillOfMaterialsLine.PartNumber
AND MainPartLink.ModelPublicationId = ")._(p.ModelPublicationId)._(@"
AND MainPartLink.ServicePartsPageId <> ")._(p.ServicePartsPageId)._(@" )
)");
p.redOn.Some(
@on => {
q._(@"
OR ( BillOfMaterialsLine.InnerLinkExcludedFrom = 0
AND EXISTS ( SELECT 1
FROM InnerPartLink
INNER JOIN Model
ON InnerPartLink.ModelCode = Model.ModelCode
AND InnerPartLink.ModelSequence = Model.ModelSequence");
if (on.blueCondition)
q._(@"
INNER JOIN Publication
ON InnerPartLink.PublicationId = Publication.PublicationId");
q._(@"
INNER JOIN LocalizedPublication
ON InnerPartLink.PublicationId = LocalizedPublication.PublicationId
AND LocalizedPublication.LanguageCode = ")._(on.languageCode)._(@"
WHERE InnerPartLink.PartNumber = BillOfMaterialsLine.PartNumber
AND Model.ModelId = ")._(on.modelId);
if (on.blueCondition)
q._(@"
AND Publication.FollowingPublicationId IS NULL");
q._(@" )
)");
});
return q._(@"
)").Select(
_ => new {
BillOfMaterialsLineId = _._<int>
PartNumber = _._<string>
});
}
Console.WriteLine("BillOfMaterialsLineId = {0}, PartNumber = {1}",
record.BillOfMaterialsLineId,
record.PartNumber);
боюсь, если начну интерпретировать, то перейду на темную сторону.
боюсь, если начну интерпретировать, то перейду на темную сторону.Да ладно, разве тёмная сторона - это сильно большая плата за автоматическую проверку, find usages и простой рефакторинг базы?
Рассмотри и другие плюсы, например, ты сможешь делать вот так:
По поводу естественного языка дам ссылку на статью Дейкстры On the foolishness of "natural language programming".
Вот так выглядит функция Invoke:
public static TResult Invoke<T1, TResult>(this T1 arg, Func<T1, TResult> func)
{
return func(arg);
}
Алгоритмы Controllable Query будут работать и в таком варианте:
new {connection, Age = 1.Param Id = Guid.NewGuid.Param}.Invoke(
p => p.connection.Query<Dog>("select Age = @Age, Id = @Id", new {p.Age, p.Id};
или с не анонимным методом:
DogQuery(connection, 1.Param Guid.NewGuid.Param;
static IEnumerable<Dog> DogQuery(IDbConnection connection, IParam<int> age, IParam<Guid> id)
{
return connection.Query<Dog>("select Age = @age, Id = @id", new {age, id});
}
Легко видеть, что вариант ControllableQuery отличается от варианта Dapper всего лишь на extract method плюс вызовы Param. Это утверждение является более формализованной формулировкой моего предыдущего утверждения “сложности, генерирующей запутанность, нет”.
Причем, если такой extract method не сделать или не вызвать Param, то query checker найдет запрос и сообщит нам о невозможности проверить запрос.
По поводу естественного языка дам ссылку на статью Дейкстры On the foolishness of "natural language programming".Ну да, Дейкстра пишет, что естественные языки слишком неоднозначны и неформализованы, чтобы на них программировать. Из этого же не следует, что я не должен хотеть читать выражения слева направо, а не "задом наперед, совсем наоборот".
К твоему фреймворку можно привыкнуть, конечно. Вот моя мама, к примеру, пишет на MUMPS и не жалуется:
PF S %H=$H D YMD S %(9)=X,X=%DT["F"*2-1 I @("%I(1)*100+%I(2)"_$E("> <",X+2)_"$E(%(94,7)") S %I(3)=%I(3)+X
Q
TT D 7 S %I(1)=%M,%I(2)=%D,%I(3)=%Y K %M,%D,%Y Q
NOW S %H=$H,%H=$S($P(%H,",",2):%H,1:%H-1)
D TT S %=$P(%H,",",2) D S S %=X_$S(%:%,1:.24) Q
DMW S %=$S(X?1.N1"D":+X,X?1.N1"W":X*7,X?1.N1"M":X*30,+X=X:X,1:0)
Как его не причесывай, твой код выглядит все равно уродливее твоего же примера на Dapper. Не могу судить, насколько пресловутое удобство рефакторинга уравновешивает уродство.
new {Age = 1.Param Id = Guid.NewGuid.Param}.Invoke(это же вроде можно свести до:
6 p => "select Age = @Age, Id = @Id".Query<Dog>(new {p.Age, p.Id}.Execute(connection);
connection
.Prepare(p => "select Age = @Age, Id = @Id".Query<Dog>(p
.Execute(new {Age = 1.Param Id=Guid.NewGuid.Param
который более наглядный.
почему так не сделать?
это же вроде можно свести до:да, вроде можно. Ты думаешь это не назовут "шиворот-навыворот и кверху ногами"? Существует какой-нибудь формализованный критерий наглядности, который можно было бы аргументировано отстаивать при возникновении возражений?
code:--------------------------------------------------------------------------------
connection
.Prepare(p => "select Age = @Age, Id = @Id".Query<Dog>(p
.Execute(new {Age = 1.Param Id=Guid.NewGuid.Param
--------------------------------------------------------------------------------
который более наглядный.
Обобщение на динамический случай:
connection
.Prepare(p => "select Age = @Age" + flag?"+1":"" + ", Id = @Id".Query<Dog>(new {p.Age, p.Id}
.Execute(new {flag, Age = 1.Param Id=Guid.NewGuid.Param
Это нормально?
Query<Dog>(new {p.Age, p.Id})почему здесь нельзя просто написать p? зачем явное указание полей? о них же и так уже известно из строки запроса
ты предлагаешь парсить запрос?
Существует какой-нибудь формализованный критерий наглядности, который можно было бы аргументировано отстаивать при возникновении возражений?код должен читаться слева направо, сверху вниз.
весь процесс должен подчиняться одной метафоре (например, выполняем операции над множеством записей).
после каждой "элементарной" операции должно быть внятное понимание, что есть "на руках" (отфильтрованное множество записей, упорядоченное множество и т.д.)
зы
примеры даны на примере linq
почему здесь нельзя просто написать p? зачем явное указание полей? о них же и так уже известно из строки запросапотом список параметров в общем случае не линеен, а дерево. Например, если передаем не bool, а Option.
код должен читаться слева направо, сверху вниз.Вызов функций этому подчиняется?
var a = a;
var b = b(a);
var c = c(b);
а можно так
c(b(a
Было abc стало cba.
Вызов функций этому подчиняется?вложенные вызовы функций этому не соответствуют. Поэтому я не советую использовать длинные последовательности вложенных вызовов функций.
c(b(a
var a = a;
var b = b(a);
var c = c(b);
это читается намного легче
ты предлагаешь парсить запрос?можно парсить и тогда будет доп. проверка, причем это можно делать на этапе разработки.
можно тупо брать из p все поля, которые типа Param (или как они там у тебя называются)
вложенные вызовы функций этому не соответствуют. Поэтому я не советую использовать длинные последовательности вложенных вызовов функций.
если и есть такая проблема, то IDE остроту этой проблемы снижает. Вообще, отношение странное, выбрали язык, так принимайте его правила игры. А то путь в этом направлении такой как в той истории, когда программиста на фортране посадили на плюсы, а он взял и переопределил все ключевые слова, чтобы программа выглядела привычнее ему.
взял и переопределил все ключевые словаво, со стороны кажется что именно этим ты и занимаешься с Controllable Query
можно тупо брать из p все поля, которые типа Param (или как они там у тебя называются)тип IParam может быть где-нибудь в середине дерева, например, IOption<IParam<int>>
код должен читаться слева направо, сверху вниз.Мой взгляд на это такой. В первую очередь должны быть соблюдены правила хорошего стиля кодирования: решение поставленной задачи, DRY, не злоупотребление mutable состоянием и т.п. Множество решений, удовлетворяющих этим условиям, как правило, образуют класс изоморфности. В том смысле, что решения из этого класса переводятся друг в друга формальным рефакторингом. Опытный программист должен понимать эту изоморфность и не делать особых предпочтений какому-либо элементу из класса изоморфности. Например, вот кода изоморфны.
весь процесс должен подчиняться одной метафоре (например, выполняем операции над множеством записей).
после каждой "элементарной" операции должно быть внятное понимание, что есть "на руках" (отфильтрованное множество записей, упорядоченное множество и т.д.)
примеры даны на примере linq
linq to sql это представление кода в виде data structures (раздел 4.6 C# Language Specification). Если не знать этого, а обманываться "выполняем операции над множеством записей", то далеко не продвинешься.
Опытный программист должен понимать эту изоморфность и не делать особых предпочтений какому-либо элементу из класса изоморфности.Например, код до обфускации эквивалентен коду после обфускации. Значит ли это, что опытный программист не должен особо предпочитать один вид кода другому?
Ты думаешь это не назовут "шиворот-навыворот и кверху ногами"?ты странный.
ты странный.т.е тебе этот вариант нравится? Если, да, то это хорошо.
подробнее отпишусь позже.
В первую очередь должны быть соблюдены правила хорошего стиля кодирования (...). Множество решений, удовлетворяющих этим условиям, как правило, образуют класс изоморфности. В том смысле, что решения из этого класса переводятся друг в друга формальным рефакторингом.Вроде как, из твоих слов получается, что "формальный рефакторинг" не должен нас выводить за пределы "класса изоморфности".
Я тебе привел извращенный пример формального рефакторинга: обфускация. Этот контрпример показывает, что всего лишь переименованием идентификаторов и добавлением/удалением пробельных символов можно вывести код за пределы класса "хороших, годных решений задачи".
А значит, не всякий рефакторинг сохраняет "хорошесть, годность" кода. Множество хороших решений задачи не замкнуто относительно рефакторинга.
Следовательно, ты пока не доказал, что твои кадавры для опытных программистов должны быть приемлемы.
Your move.
Следовательно, ты пока не доказал, что твои кадавры для опытных программистов должны быть приемлемы.Круто, на форумлокал занимаются формальным доказательством того, что гавнокод на самом деле является гавнокодом.
Интересно, будет ли Шурик в этот раз
Кстати, а что мы будем делать, если он формально докажет, что его гавнокод не гавнокод?
Я предлагаю в этом случае отправить его на костёр, а то потом он примется доказывать, что Земля - это шар!11
Круто, на форумлокал занимаются формальным доказательством того, что гавнокод на самом деле является гавнокодом.[зануда-mode] Я не доказываю ничего, а лишь указываю на ошибку в доказательстве противоположного факта.
Мои слова соответствуют следующей картинке:А слова читателей раздела - этой:
(*1) можно рассмотреть еще ряд подобных библиотек
Множество решений, удовлетворяющих этим условиям, как правило, образуют класс изоморфности. В том смысле, что решения из этого класса переводятся друг в друга формальным рефакторингом.Изоморфность всегда определяется, исходя из каких-то критериев. И то, что изоморфно исходя из одних критериев, будет не изоморфным исходя из других критериев.
В частности, человек (в отличии от машины) очень "не любит" операцию переопределения символов, хотя проще операции и быть не может.
Первую строчку я могу прочитать, как если бы это было на естественном языке:
"у connection-а вызвать query, возвращающий Dog, с SQL телом <SQL> и параметрами {Age=1, Id = Guid.NewGuid}"
...
Как его не причесывай, твой код выглядит все равно уродливее твоего же примера на Dapper. Не могу судить, насколько пресловутое удобство рефакторинга уравновешивает уродство.
Вот такая форма вроде нормально читается на естественном языке:
//ControllableQuery
connection.Execute(p => "select Age = @Age, Id = @Id".ToQuery<Dog>(p new { Age = 1.Param Id = Guid.NewGuid.Param });
//Dapper
connection.Query<Dog>("select Age = @Age, Id = @Id", new { Age = 1, Id = Guid.NewGuid });
"На connection-е выполнить запрос, получаемый как результат анонимной функции, с параметрами {Age=1, Id = Guid.NewGuid}, запрос создается из sql-строки и указанных параметров, приходящих в аргументе анонимной функции."
Update: переставил аргументы в конец. Спасибо, .
сначала хочется видеть что делаем, а потом уже понятно что это за параметры, и чему они равны
параметры лучше в конец переставить.по этому вопросу я метаюсь из стороны в сторону .
сначала хочется видеть что делаем, а потом уже понятно что это за параметры, и чему они равны
С одной стороны, да, твой аргумент имеет смысл. Да и запись будет более похожа на Dapper.
С другой стороны, когда я начал писать интерпретацию на естественном языке, предложение плохо строилось, поскольку "p" используется для построения запроса. Год назад Майк кричал, что параметры вторым аргументов это ужасно. Автокомплишен иногда сбивается, поскольку выражение еще не полностью написано и вывести тип трудно.
Вот такая форма вроде нормально читается на естественном языке:Да, такая лучше.
connection.Execute("select Age = @Age, Id = @Id".ToQuery<Dog>(new { Age = 1, Id = Guid.NewGuid };
или лучше так
"select Age = @Age, Id = @Id".ToQuery<Dog>(new { Age = 1, Id = Guid.NewGuid }).Execute(connection);
поскольку часто управление connection-ом будет вынесено, и будет просто .Execute или даже просто .ToList
В случае динамического sql-я появление лямбды “p =>” обусловлено самым базовым принципом работы алгоритма “статической типизации” . Этот алгоритм основан на выделение из всей программы фрагментов, которые можно вызвать автоматически, перебирая все возможные комбинации параметров. Лямбда и является таким фрагментом.
Появление Param тоже имеет чёткое объяснение (зачем пишется эта закорючка) в рамках парадигмы статической типизации. Алгоритм попытается для всех параметров перебрать все значения. Для многих типов (string, int) он не сможет этого сделать и сообщит об этом. Эти параметры должны транзитом проскочить исполняемый метод и уйти параметрами в sql сервер. Для таких транзитных параметров была введена специальная обертка IParam. Исполняемый метод не сможет для своей логики использовать значение, спрятанное под этой оберткой. Алгоритм может спокойно создать IParam, который пройдет через исполняемый метод.
То есть всё в точности так же, как при общении человека с компилятором.
Оставить комментарий
6yrop
В соседнем треде сравнение Controllable Query с ADO.NET вызывает массу не относящихся к делу моментов. Поэтому предлагаю для сравнения использовать уже “рафинированный” ADO.NET, т.е. проект Dapper (ближайший аналог для Java DbUtils). Dapper имеет авторитет, поскольку на нем работает stackoverflow.comПолучаем лишние слова Param, Invoke, Execute и каждый параметр упоминается дважды. Замечу, что упоминание параметров дважды является будничным делом, вы такое проделываете при объявлении/вызове любого метода. Вопрос: это сильно большая плата за автоматическую проверку, find usages и простой рефакторинг базы? Где здесь можно запутаться? Оверхед по количеству букв есть, но сложности, генерирующей запутанность, нет.