Здравствуйте, _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. И вообще узнать бы, движется ли поезд вообще в этом направлении, либо есть фундаментальные нестыковки?