На сколько "продвинут"?
Требуется инструмент для "кросдатабазной" генереции запросов.
Есть ли / можно ли самому добавить поддержку WITH например и оконных функций (с тем, что бы можно было бы определить что определённый провайдер эту возможность не поддерживает)?
Можно ли сериализовать объект-запрос? Анализировать его (есть ли визитор)?
На сколько вообще разумно использование BLToolkit в таком ракурсе? Может, генерилка sql в нём делалась сторого для нужд ОРМ и всякие SQL-изыски там категорически не к чему?
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>На сколько "продвинут"? _FR>Требуется инструмент для "кросдатабазной" генереции запросов.
Если имеется ввиду возможность указывать имя базы при указании таблицы, то в наличии соответствующие атрибуты для классов и метод DatabaseName в linq запросе.
_FR>Есть ли / можно ли самому добавить поддержку WITH например и оконных функций (с тем, что бы можно было бы определить что определённый провайдер эту возможность не поддерживает)?
Не понял.
_FR>Можно ли сериализовать объект-запрос? Анализировать его (есть ли визитор)?
Можно. Linq over WCF именно такой фигнёй и занимается.
_FR>На сколько вообще разумно использование BLToolkit в таком ракурсе? Может, генерилка sql в нём делалась сторого для нужд ОРМ и всякие SQL-изыски там категорически не к чему?
SQL изыски планируется только расширять.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
_FR>>На сколько "продвинут"? _FR>>Требуется инструмент для "кросдатабазной" генереции запросов. IT>Если имеется ввиду возможность указывать имя базы при указании таблицы, то в наличии соответствующие атрибуты для классов и метод DatabaseName в linq запросе.
Имеется в виду возможность такого псевдокода:
var query = new SelectBuilder();
query.From = new Table("Orders");
query.AddColumn("OrderID");
var commandText = query.ToString(Db.MsSql);
SELECT [OrderID]
FROM [Orders]
То есть создание некоего объекта (по сути AST) и дальнейшая возможность трансляции его в SQL для различных СУБД.
_FR>>Есть ли / можно ли самому добавить поддержку WITH например и оконных функций (с тем, что бы можно было бы определить что определённый провайдер эту возможность не поддерживает)? IT>Не понял.
Только помимо простейших и наиболее общих вещей нужна широкая поддержка sql:
var cte = new SelectBuilder();
cte.From = new Table("Order Details");
cte.AddColumns("OrderID", "ProductID");
var column = cte.AddColumn("UnitPrice");
query /* из предыдущего */.AddWith(cte);
query.AddJoin(cte, query["OrderID"], Sql.Equals, cte["OrderID"]);
query.AddColumn(column);
WITH [CTE Order Details] ([OrderID], [ProductID], [UnitPrice]) AS (
SELECT [OrderID], [ProductID], [UnitPrice]
FROM [Order Details]
)
SELECT [O].[OrderID], [D].[UnitPrice]
FROM [Orders] [O]
JOIN [CTE Order Details] [D] ON [O].[OrderID] = [D].[OrderID];
То есть объектная модель должна поддерживать common table expressions / оконные функции (с атрибутами PARTITION BY / ORDER BY) и прочие "изыски". Быть удобной для сложных манипуляций при построении и изменении объекта-запроса.
_FR>>На сколько вообще разумно использование BLToolkit в таком ракурсе? Может, генерилка sql в нём делалась сторого для нужд ОРМ и всякие SQL-изыски там категорически не к чему? IT>SQL изыски планируется только расширять.
Ага, вот меня по примерам и подкупила возможность в пользовательском коде вносить расширения (ну так мне по примерам показалось что такая возможность есть для функций / подвыражений).
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>Имеется в виду возможность такого псевдокода:
Это Sql Builder pattern. В BLToolkit этот класс называется SqlQuery.
_FR>То есть создание некоего объекта (по сути AST) и дальнейшая возможность трансляции его в SQL для различных СУБД.
Потом он скармливается конкретному SqlProvider и тот строит по нему запрос.
_FR>То есть объектная модель должна поддерживать common table expressions / оконные функции (с атрибутами PARTITION BY / ORDER BY) и прочие "изыски".
Прямой поддержки WITH нет, хотя некоторые провайдеры строят SQL с использованием WITH для некоторых конструкций, например, для пейджинга.
_FR>Быть удобной для сложных манипуляций при построении и изменении объекта-запроса.
Удобным для сложных манипуляций может быть только паттерн-матчинг. Всё остальное — закат солнца в ручную.
_FR>Ага, вот меня по примерам и подкупила возможность в пользовательском коде вносить расширения (ну так мне по примерам показалось что такая возможность есть для функций / подвыражений).
На уровне выражений уже сейчас можно расширяться куда угодно. Сейчас переписывается парсер линк выражений и тогда появится возможность расширяться на уровне цепочки методов, например, добавлять свои методы вроде LeftJoin и т.п.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
_FR>>Быть удобной для сложных манипуляций при построении и изменении объекта-запроса. IT>Удобным для сложных манипуляций может быть только паттерн-матчинг. Всё остальное — закат солнца в ручную.
Нет, мне достаточно суметь, например, имея какой-то SqlQuery
SELECT X1, X2 FROM X
заиспользовать его в качестве FROM в другом SELECT SqlQuery,
SELECT X1 + X2, X1 - X2
FROM (SELECT X1, X2 FROM X) T
и потом результат указать как источник для INSERT, то есть что бы из трёх независимых кусочков собралось бы что-то вроде
INSERT Y (Y1, Y2)
SELECT X1 + X2, X1 - X2
FROM (SELECT X1, X2 FROM X) T
А после этого вдруг дабавит в самый внутренний SELECT какой-нить WHERE.
С анализом/распознованием будем пока закатыванием заниматься
В общем, буду пробывать
Help will always be given at Hogwarts to those who ask for it.
Здравствуйте, _FRED_, Вы писали:
_FR>А после этого вдруг дабавит в самый внутренний SELECT какой-нить WHERE.
Хотел бы сказать, что тема топика для меня тоже очень насущная (если я правильно понял суть вопроса).
Где-то год назад я начинал рабочий проект, в котором даже хотел использовать BLT, но меня смутила на тот момент связность сборок BLT с System.Web, System.Windows.Forms, OracleClient и прочими специфичностями. Показалось что всё это как-то сыровато пока и решил ограничиться пробой Linq-подхода в проекте. Т.е. казалось очень логичным забирать только нужные данные из базы.
Однако, на практике выяснилось, что это удобно только когда пишешь запрос ручками. Когда же надо написать что-то автоматическое, то всё сразу превращается в кошмар — в необходимость перестраивать деревья, понимать как все эти Expression стыкуются между собой.
Сейчас в фоновом режиме, не торопясь, пишу свой DataAccess, в котором предусматриваю три уровня работы с БД:
— конструирование запросов на низшем уровне (работа с элементами sql, гибче всего, но и сложнее, всё нетипизировано)
— конструктор среднего уровня (проще составлять простые запросы, типизированный и нетипизированный варианты)
— ORM для типичных сущностей, может быть тут же место и Linq-образным запросам.
на низком уровне конструирование выглядит так (должны поддерживаться все возможности sql — ранжирование, cte. Расширяются специфичными для субд элементами (на данный момент сиквенсы), обобщаются, где возможно):
[TestMethod()]
public void RangeAggregateTest() {
var q = new Query();
var my_table = new QSrcWithAlias(new TableSource("my_table"), new Alias("tbl"));
var clmn1 = new SourceColumn(my_table, "id");
var clmn2 = new SourceColumn(my_table, "value");
var clmn3 = new SourceColumn(my_table, "purchase_date");
q.Select.Add(new QExWithAlias(new RangeFunction(new RAggRank(), new List<SourceColumn>(), new List<SourceColumn>() { clmn3, clmn1 }), new Alias("rnk")));
q.Select.Add(new JustQEx(clmn1));
q.From = my_table;
ILowTranslator translator = new DummyLowTranslator();
var pq = translator.Translate(q);
var sb = new StringBuilder();
sb.AppendLine("select rank() over (order by tbl.purchase_date, tbl.id) rnk, tbl.id");
sb.AppendLine("from my_table tbl");
Shared.AreSqlEqual(sb.ToString(), pq.SqlQuery);
Assert.AreEqual(0, pq.Parameters.Count());
}
на среднем (планирую этот уровень сделать независимым от субд, пока не знаю как получится):
[TestMethod()]
public void MixedGeneralTest() {
var decl = new MidDeclarator(new FlatClassMetaInformationExtractor());
var qb = decl.AQuery<result_data>();
var tbl1 = decl.Table<my_table>("t");
qb.Select(tbl1.Clmn(c => c.id), tbl1.Clmn(c => c.name).As(qb.Clmn(c => c.als_name)));
qb.From(tbl1);
var subQuery = GetUntypedSubQuery(2);
qb.LeftOuterJoin(subQuery).On(tbl1.Clmn(c => c.ref_id).EqualsTo(subQuery["id"]));
qb.Select(subQuery["sub_name"]);
IMidTranslator translator = new DummyMidTranslator();
var pq = translator.Translate(qb);
var sb = new StringBuilder();
sb.AppendLine("select t.id, t.name als_name, t1.sub_name");
sb.AppendLine("from my_table t");
sb.AppendLine("left outer join");
sb.AppendLine(" (");
sb.AppendLine(" select t1.id, t1.name sub_name");
sb.AppendLine(" from ref_table t1");
sb.AppendLine(" where");
sb.AppendLine(" t1.state = $state");
sb.AppendLine(" and t1.enabled = TRUE");
sb.AppendLine(" ) t1 on (t.ref_id = t1.id)");
Shared.AreSqlEqual(sb.ToString(), pq.SqlQuery);
Assert.AreEqual(1, pq.Parameters.Count());
Assert.AreEqual("state", pq.Parameters.Single().ParameterName);
}
private IMidSourceEntity GetUntypedSubQuery(int param) {
var decl = new MidDeclarator();
var qb = decl.AQuery();
var tbl1 = decl.Table("ref_table");
qb.Select(tbl1["id"], tbl1["name"].As("sub_name"));
qb.From(tbl1);
qb.Where(decl.And(tbl1["state"].EqualsToParam(param), tbl1["enabled"].EqualsToConstant(true)));
return qb;
}
вся типизация сводится к тому, чтобы использовать типы-таблицы для извлечения информации об именах таблиц, полей и т.п. Маппер (которого пока нет) будет должен использовать накопленную информацию, чтобы по сути сделать обратную операцию — текстовые имена колонок сопоставить со свойствами типов-таблиц.
но всё это в зачаточной стадии и не знаю, дозреет ли до чего-то серьёзного. Просто хотел присоединиться к тому, что необходимость SelectBuilder'ов (и не только Select) есть и было бы заманчиво увидеть это в BLT. И вообще узнать бы, движется ли поезд вообще в этом направлении, либо есть фундаментальные нестыковки?
На BLToolkit это будет выглядеть примерно следующим образом:
var myTable = new SqlTable
{
Name = "my_table",
Fields =
{
new SqlField { Name = "id" },
new SqlField { Name = "value" },
new SqlField { Name = "purchase_date" }
}
};
var q = new SqlQuery();
q
.Select
.Expr (new SqlFunction("MyFunction", myTable["purchase_date"], myTable["id"]))
.Field(myTable["id"])
.From
.Table(myTable)
;
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>На BLToolkit это будет выглядеть примерно следующим образом:
Спасибо за ответ!
Тогда ещё несколько вопросов, пока вы добрый.
1. у меня лежит скачанная версия 3.2 с середины прошлого года. А сейчас финальная опубликованная версия — 3.1. Проект жив?
2. я правильно понял, что Linq AST рендерится в SqlQuery, а потом уже он рендерится в настоящий Sql запрос?
3. счас обнаружил, что SqlQuery существует разный в папках Data\Sql и DataAccess. И они оба (точнее используемые ими SqlQueryBase и разные Clause'ы соот-но) занимаются генерацией запросов. Что-то из них устаревшее или оба в силе?
4. Можно ли где почитать про концептуальное устройство библиотеки? Т.е. там же всё разделено вроде бы — сбор информации о запросе, трансформации, генерация sql, выполнение запроса, получение результатов, маппинг. К тому же по приведённому вами примеру я так понимаю, что в основе лежит нетипизированый доступ к данным, а уже на уровне выше (ближе к юзеру) он может типизироваться. Но читая код я вижу, что маппинг, чтение метаданных и генерация запроса идут как будто в одном месте:
protected SqlQueryInfo CreateInsertSqlText(DbManager db, Type type, int nParameter)
{
TypeExtension typeExt = TypeExtension.GetTypeExtension(type, Extensions);
ObjectMapper om = db.MappingSchema.GetObjectMapper(type);
List<MemberMapper> list = new List<MemberMapper>();
StringBuilder sb = new StringBuilder();
SqlQueryInfo query = new SqlQueryInfo(om);
MetadataProviderBase mp = MappingSchema.MetadataProvider;
sb.Append("INSERT INTO ");
AppendTableName(sb, db, type);
sb.Append(" (\n");
foreach (MemberMapper mm in GetFieldList(om))
{
// IT: This works incorrectly for complex mappers.
//
// [2009-03-24] ili: use mm.MemberAccessor instead of mm.ComplexMemberAccessor
// as in CreateUpdateSqlText
//bool isSet;
if (mp.GetNonUpdatableAttribute(type, typeExt, mm.MapMemberInfo.MemberAccessor, out isSet) == null || !isSet)
{
sb.AppendFormat("\t{0},\n",
db.DataProvider.Convert(mm.Name, ConvertType.NameToQueryField));
list.Add(mm);
}
}
это сильно сбивает с толку (вероятно, в большей степени из-за того, что ожидаю увидеть так как сам себе это представлял). Хотелось бы понять концепции — ключевые ответственности, интерфейсы, классы; как они между собой взаимосвязаны. Чтобы когда уже полез в код, не удивляться.
5. Есть ли в планах растащить всё по разным сборкам? Чтобы было проще понимать и использовать только то, что надо? Например, мне нравится модель маппирования BLT и я хочу её использовать, но то, что надо тащить ещё целый паровозик доступа к данным — это пугает. Хотя я подозреваю, что люди делятся на два типа — кому нравится иметь всё в разных сборках и кто любит всё в одном, поэтому может это и спорное решение, но конкретные планы этого проекта всё же интересны.
Здравствуйте, Neco, Вы писали:
N>1. у меня лежит скачанная версия 3.2 с середины прошлого года. А сейчас финальная опубликованная версия — 3.1. Проект жив?
Проект живее всех живых. Сейчас идёт переработка Linq-парсера. Далее в планах добавление ещё одного DataProvider для одной встраиваемой БД. Есть идеи по вынесению Linq в отдельный проект. Дальше посмотрим.
N>2. я правильно понял, что Linq AST рендерится в SqlQuery, а потом уже он рендерится в настоящий Sql запрос?
Правильно.
N>3. счас обнаружил, что SqlQuery существует разный в папках Data\Sql и DataAccess. И они оба (точнее используемые ими SqlQueryBase и разные Clause'ы соот-но) занимаются генерацией запросов. Что-то из них устаревшее или оба в силе?
В DataAccess старый вариант, который не используется Linq и который не использует SqlProviders.
N>4. Можно ли где почитать про концептуальное устройство библиотеки?
К сожалению, только в коде
N>Т.е. там же всё разделено вроде бы — сбор информации о запросе, трансформации, генерация sql, выполнение запроса, получение результатов, маппинг. К тому же по приведённому вами примеру я так понимаю, что в основе лежит нетипизированый доступ к данным, а уже на уровне выше (ближе к юзеру) он может типизироваться. Но читая код я вижу, что маппинг, чтение метаданных и генерация запроса идут как будто в одном месте:
Это DataAccess из другой оперы ещё до линковских времён. На самом деле библиотека уже слегка обросла рудиментами, но по понятным причинам от них не получается избавиться.
N>5. Есть ли в планах растащить всё по разным сборкам? Чтобы было проще понимать и использовать только то, что надо? Например, мне нравится модель маппирования BLT и я хочу её использовать, но то, что надо тащить ещё целый паровозик доступа к данным — это пугает. Хотя я подозреваю, что люди делятся на два типа — кому нравится иметь всё в разных сборках и кто любит всё в одном, поэтому может это и спорное решение, но конкретные планы этого проекта всё же интересны.
Есть идея вынести всё, что касается Linq в отдельный проект и развивать его в сторону легковесного ORM.
Если нам не помогут, то мы тоже никого не пощадим.