Аннотация:
Сегодня Linq уже не нуждается в особом представлении и рекомендациях. Эта технология с каждым днём получает всё больше распространение и всё сильнее завоёвывает признание разработчиков. BLToolkit так же не остался в стороне и предлагает свой Linq-provider для доступа к базам данным. Целью данного документа как раз является предоставление информации о поддержки Linq в BLToolkit.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, ili, Вы писали:
ili>отчего бы не вынести данное узнавание в метадата провайдер, в таком случае, кому надо, тот сможет припаивать свой код на этот предмет.
А оно разве сейчас не там?
... << RSDN@Home 1.2.0 alpha 4 rev. 1464 on Windows 7 6.1.7600.0>>
Здравствуйте, MozgC, Вы писали:
MC>Тут выпадает исключение "'c.ID' cannot be converted to SQL."
Это скорее всего бага.
MC>Если избавиться от let, т.е. написать например так: MC>
var q = from c in db.GetTable<Customer>()
MC> select new Pair(c.Name, MySql.StoredFunction<double, int>("GetCustomerBalance", c.ID));
MC>То получаем такой SQL: MC>
SELECT
MC> c.Name,
MC> 'GetCustomerBalance'(c.ID) as c1
MC>FROM
MC> customers c
MC>ну и соответственно ошибку в синтаксисе запроса.
Да, точно. Строка будет рассмотрена как значение и заключена в кавычки. Тогда только наследование от SqlExpressionAttribute с перекрытием метода GetExpression.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Это всё элементы Entity Service (в частности это — Change Tracking), которых в BLT нет. Пока.
А может и не надо? Или только где-нибудь с боку, чтобы можно было выкинуть...
Здравствуйте, Игорь Ткачёв, Вы писали:
ИТ>Аннотация: ИТ>Сегодня Linq уже не нуждается в особом представлении и рекомендациях. Эта технология с каждым днём получает всё больше распространение и всё сильнее завоёвывает признание разработчиков. BLToolkit так же не остался в стороне и предлагает свой Linq-provider для доступа к базам данным. Целью данного документа как раз является предоставление информации о поддержки Linq в BLToolkit.
Спасибо, про CompiledQuery не знал, хорошая штука.
А для batch update/delete операций можно делать некий UpdatePreview(), чтобы до выполнения увидеть SQL?
Если нет SubmitChanges, то в какие моменты происходят все обновления в базе?
Реально ли BLToolkit прикрутить к LinqPad?
Еще интересует завершенность LINQ в BLToolkit. На ormbattle.net видно, что какие-то тесты не проходятся еще, хотя данные наверное устарели. Т.е. интересует вопрос, все ли доделано и если не доделано, то что именно и когда примерно планируется.
Здравствуйте, Димчанский, Вы писали:
Д>Еще интересует завершенность LINQ в BLToolkit. На ormbattle.net видно, что какие-то тесты не проходятся еще, хотя данные наверное устарели. Т.е. интересует вопрос, все ли доделано и если не доделано, то что именно и когда примерно планируется.
Базовый функционал доделан. А вообще развивать можно почти до бесконечности.
... << RSDN@Home 1.2.0 alpha 4 rev. 1458 on Windows 7 6.1.7600.0>>
Я правильно понял, что для связей типа многие-ко-многим (через промежуточную таблицу с двумя полями-внешними ключами) мы должны явно описывать эту таблицу в коде в виде отдельного класса? Или можно как-то скрыть существование этой таблицы куда-нибудь подальше (просто указав имя промежуточной таблицы где-нибудь в атрибуте или еще как) и использовать в ассоциациях в коде коллекции из самих конечных, а не таких вот промежуточных/переходных сущностей (чтобы получилась более "чистая" модель классов)?
Здравствуйте, AndrewVK, Вы писали:
AVK>Базовый функционал доделан. А вообще развивать можно почти до бесконечности.
Я просто смотрю, что здесь Linq вроде как должен быть в версии 3.2, но она сейчас в разработке еще. Поэтому интересует, когда выйдет некая стабильная 3.2 с базовым функционалом. Т.е. не таскать же каждый день latest.
Здравствуйте, Димчанский, Вы писали:
AVK>>В момент вызова методов Insert/Update/Delete
Д>А как-то средставми BLT можно обновлять только то (в том числе отдельные поля), что реально изменилось?
Можно. Но это не имеет отношения к LINQ.
... << RSDN@Home 1.2.0 alpha 4 rev. 1464 on Windows 7 6.1.7600.0>>
Здравствуйте, Димчанский, Вы писали:
Д>А для batch update/delete операций можно делать некий UpdatePreview(), чтобы до выполнения увидеть SQL?
Batch операции пока сделаны вообще мимо поддержки Linq, через старый интерефейс SqlQuery. Но SQL посмотреть можно после выполнения операции в DbManager.LastQuery. Но надо учитывать, что пакетов может быть много и увидеть можно только последний.
Д>Если нет SubmitChanges, то в какие моменты происходят все обновления в базе?
Обновления происходят явно вызовами DML команд Insert/Update/Delete.
Д>Реально ли BLToolkit прикрутить к LinqPad?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Димчанский, Вы писали:
Д>Еще интересует завершенность LINQ в BLToolkit. На ormbattle.net видно, что какие-то тесты не проходятся еще, хотя данные наверное устарели. Т.е. интересует вопрос, все ли доделано и если не доделано, то что именно и когда примерно планируется.
Последние результаты тестов нужно смотреть здесь. Сами тесты с описанием результатов прохода здесь. В основном не проходят тесты, связанные с загрузкой списков подчинённых записей. Никаких принципиальных причин для того, что не сделать эту фичу нет, кроме того, что надо решить как лучше это сделать. Т.к. вариантов хоть и много, но идеального ни одного.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Овощ, Вы писали:
О>Я правильно понял, что для связей типа многие-ко-многим (через промежуточную таблицу с двумя полями-внешними ключами) мы должны явно описывать эту таблицу в коде в виде отдельного класса? Или можно как-то скрыть существование этой таблицы куда-нибудь подальше (просто указав имя промежуточной таблицы где-нибудь в атрибуте или еще как) и использовать в ассоциациях в коде коллекции из самих конечных, а не таких вот промежуточных/переходных сущностей (чтобы получилась более "чистая" модель классов)?
А как такое описание и такой запрос будет выглядеть в коде?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Димчанский, Вы писали:
Д>Я просто смотрю, что здесь Linq вроде как должен быть в версии 3.2, но она сейчас в разработке еще. Поэтому интересует, когда выйдет некая стабильная 3.2 с базовым функционалом. Т.е. не таскать же каждый день latest.
К релизу мы готовы. Сегодня/завтра будет зафиксирована версия. Скорее всего та, которая сейчас находится в репозитории.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, AndrewVK, Вы писали:
Д>>А как-то средставми BLT можно обновлять только то (в том числе отдельные поля), что реально изменилось? AVK>Можно. Но это не имеет отношения к LINQ.
Я имел ввиду, что если на обычном Linq2Sql я напишу нечто вроде:
var prod = new Product { ID=1000, Description ="Description", Discontinued = false, LastSale = DateTime.Now };
Products.InsertOnSubmit(prod);
SubmitChanges();
prod.Discontinued = true; // здесь я не знаю, какие изменения произошли и в каких полях
SubmitChanges();
то получу такие SQL запросы:
DECLARE @p0 Int SET @p0 = 1000
DECLARE @p1 VarChar(11) SET @p1 = 'Description'
DECLARE @p2 Bit SET @p2 = 0
DECLARE @p3 DateTime SET @p3 = '2010-02-25 18:11:06.789'
INSERT INTO [Product]([ID], [Description], [Discontinued], [LastSale])
VALUES (@p0, @p1, @p2, @p3)
GO
DECLARE @p0 Int SET @p0 = 1000
DECLARE @p1 Bit SET @p1 = 1
UPDATE [Product]
SET [Discontinued] = @p1
WHERE [ID] = @p0
Причем, если бы я вообще никакого реального изменения не сделал (хотя присвоения и были, но тех же самых значений),
то никакого апдейта реально в базе бы не произошло.
В L2S я получаю такие оптимизации из коробки.
Как я понял BLT больше похож на SQL с синтаксисом и соответствующими фичами C#.
Но если так, то как бы можно было сделать финты оптимизации изменений как в L2S? Т.е. получается, нужно создать некий механизм по отслеживания изменений в объекте и затем динамически сгенерировать запрос на обновление.
Как бы это можно было красиво сделать с использованием BLT?
Здравствуйте, Димчанский, Вы писали:
Д>Как бы это можно было красиво сделать с использованием BLT?
Никак. Читать раздел "Идеология и модель программирования". BLToolkit тем и ценнен, что не занимается такой ерундой, как отслеживание изменений.
Здравствуйте, olegkr, Вы писали:
IT>>которых в BLT нет. Пока. O>Нужно ли?
Смотря как сделать. Так как это сделано в тяжёлых ORM, т.е. гвоздями прибито к мапперу, то точно не нужно. Если же сделать заменяемый DataContext, то вай бы и нот. Сейчас это только DbManager, но в планах уже Linq to Middleware, есть мысли насчёт SQL grabber, можно добавить и Entity Service или какие-то его элементы.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IB, Вы писали:
IT>>Это всё элементы Entity Service (в частности это — Change Tracking), которых в BLT нет. Пока. IB>А может и не надо? Или только где-нибудь с боку, чтобы можно было выкинуть...
Ну не надо так не надо.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, IB, Вы писали:
IT>>>Это всё элементы Entity Service (в частности это — Change Tracking), которых в BLT нет. Пока. IB>>А может и не надо? Или только где-нибудь с боку, чтобы можно было выкинуть...
IT>Ну не надо так не надо.
а хотелось бы, но именно так, чтобы оно работало ровно тогда, когда надо... в общем, привода бы иметь хотелось.
Здравствуйте, Plotyan, Вы писали:
P>Почему именно так? Ведь теперь базовый класс обязан "знать" обо всех своих наследниках. А это не дает разбить модель на независимые части.
Варианты?
... << RSDN@Home 1.2.0 alpha 4 rev. 1464 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Plotyan, Вы писали:
P>>Почему именно так? Ведь теперь базовый класс обязан "знать" обо всех своих наследниках. А это не дает разбить модель на независимые части.
AVK>Варианты?
Ну собственно знать о параметрах наследования (т.е. о типе и значении дискриминатора) нужно только при работе с наследником. Вот и указывать их в определении класса-наследника, т.е. только его и помечать атрибутом InheritanceMapping. Это, кстати, позволит для разных наследников использовать разные дискриминаторы.
Здравствуйте, Plotyan, Вы писали:
P>Ну собственно знать о параметрах наследования (т.е. о типе и значении дискриминатора) нужно только при работе с наследником. Вот и указывать их в определении класса-наследника, т.е. только его и помечать атрибутом InheritanceMapping. Это, кстати, позволит для разных наследников использовать разные дискриминаторы.
А как об этом наследнике узнает собственно меппер? Сканировать все сборки, или явно где то их сбоку регистрировать?
... << RSDN@Home 1.2.0 alpha 4 rev. 1464 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Plotyan, Вы писали:
P>>Ну собственно знать о параметрах наследования (т.е. о типе и значении дискриминатора) нужно только при работе с наследником. Вот и указывать их в определении класса-наследника, т.е. только его и помечать атрибутом InheritanceMapping. Это, кстати, позволит для разных наследников использовать разные дискриминаторы.
AVK>А как об этом наследнике узнает собственно меппер? Сканировать все сборки, или явно где то их сбоку регистрировать?
По идее тем же способом, каким он узнает о сборке, в которой определен базовый класс.
Кстати, каким? Извиняюсь за незнание азов, но моё знакомство с BLToolkit измеряется в минутах, статья была прочитано только что.
Здравствуйте, Plotyan, Вы писали:
P>По идее тем же способом, каким он узнает о сборке, в которой определен базовый класс.
При чем тут сборка? Я спрашиваю про типы. Базовый тип указывается в запросе явно, как следствие мепперу сразу доступны все наследники. А вот если как ты предлагаешь — мепперу о наследниках узнать как?
P>Кстати, каким? Извиняюсь за незнание азов, но моё знакомство с BLToolkit измеряется в минутах, статья была прочитано только что.
Никаким. Базовый класс в запросе ты указываешь явно.
... << RSDN@Home 1.2.0 alpha 4 rev. 1464 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Plotyan, Вы писали:
P>>По идее тем же способом, каким он узнает о сборке, в которой определен базовый класс.
AVK>При чем тут сборка? Я спрашиваю про типы. Базовый тип указывается в запросе явно, как следствие мепперу сразу доступны все наследники. А вот если как ты предлагаешь — мепперу о наследниках узнать как?
Так же, как и о базовом типе.
P>>Кстати, каким? Извиняюсь за незнание азов, но моё знакомство с BLToolkit измеряется в минутах, статья была прочитано только что.
AVK>Никаким. Базовый класс в запросе ты указываешь явно.
Значит, явно указать в запросе наследника. Код из статьи:
from p in db.DiscontinuedProduct
select p;
или
from c in db.Product
where c is Northwind.DiscontinuedProduct
select c;
В первом случае тип наследника известен на момент создания "контекста" и маппер сможет разобраться с наследованием. Во втором это придется делать во время разбора выражения.
Здравствуйте, AndrewVK, Вы писали:
AVK>При чем тут сборка? Я спрашиваю про типы. Базовый тип указывается в запросе явно, как следствие мепперу сразу доступны все наследники. А вот если как ты предлагаешь — мепперу о наследниках узнать как?
Здравствуйте, IT, Вы писали:
IT>Не получится. Вот такой запрос
IT>
IT>from c in db.Product select c;
IT>
IT>должен вернуть объекты не родительского типа, а наследников.
Хм, тут не понял. Если Product — это базовый класс, то тут два варианта:
1. запрос возвращает все записи, включая всех наследников, в виде Product.
2. запрос возвращает только объекты класса Product, без наследников.
В первом случае все хорошо, а во втором да, нужно знать обо всех наследниках, чтобы их не выбрать. Но вроде первый вариант более логичен?
Здравствуйте, Plotyan, Вы писали:
P>Хм, тут не понял. Если Product — это базовый класс, то тут два варианта: P> 1. запрос возвращает все записи, включая всех наследников, в виде Product. P> 2. запрос возвращает только объекты класса Product, без наследников.
Если есть наследование, то должен быть полиморфизм, иначе все бессмысленно. А если есть полиморфизм, то когда я обращаюьс к абстрактному Product, реально должны создаваться экземпляры конкретных наследников.
P.S. Вообще, ИМХО, если уж заморачиваться наследованием, то нужно поддержать все три общеприменимых типа.
... << RSDN@Home 1.2.0 alpha 4 rev. 1464 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Plotyan, Вы писали:
P>>Хм, тут не понял. Если Product — это базовый класс, то тут два варианта: P>> 1. запрос возвращает все записи, включая всех наследников, в виде Product. P>> 2. запрос возвращает только объекты класса Product, без наследников.
AVK>Если есть наследование, то должен быть полиморфизм, иначе все бессмысленно. А если есть полиморфизм, то когда я обращаюьс к абстрактному Product, реально должны создаваться экземпляры конкретных наследников.
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, Plotyan, Вы писали:
P>>Можно ли задать параметры ассоциаций и наследования через XML схему маппинга?
AVK>Да.
Здравствуйте, Plotyan, Вы писали:
P>Где-нибудь можно почитать о том, как это сделать?
Наверно можно создать собственного наследника класса BLToolkit.Reflection.MetadataProvider.MetadataProviderBase (например по аналогии с LinqMetadataProvider) и там уже брать параметры маппинга и конфигурации откуда захочется и как захочется.
Здравствуйте, Plotyan, Вы писали:
IT>>должен вернуть объекты не родительского типа, а наследников.
P>Хм, тут не понял. Если Product — это базовый класс, то тут два варианта: P> 1. запрос возвращает все записи, включая всех наследников, в виде Product. P> 2. запрос возвращает только объекты класса Product, без наследников.
P>В первом случае все хорошо, а во втором да, нужно знать обо всех наследниках, чтобы их не выбрать. Но вроде первый вариант более логичен?
Запрос должен вернуть объекты, типы которых соответствуют описании иерархии. Но в данном случае это описание можно взять только у Product, больше негде.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Запрос должен вернуть объекты, типы которых соответствуют описании иерархии. Но в данном случае это описание можно взять только у Product, больше негде.
так вопрос скорее в том как узнавать.
отчего бы не вынести данное узнавание в метадата провайдер, в таком случае, кому надо, тот сможет припаивать свой код на этот предмет.
Здравствуйте, IT, Вы писали:
IT>Здравствуйте, Plotyan, Вы писали:
P>>Можно ли задать параметры ассоциаций и наследования через XML схему маппинга?
IT>В смысле задать атрибуты? По-моему, пока нет. Тем не менее, задавать поля/свойсва для ассоциаций всё равно придётся.
Я имел в виду определение маппинга через XML. BLToolkit вроде поддерживает не только атрибуты, но и XML, но здесь
Здравствуйте, IT, Вы писали:
IT>А как такое описание и такой запрос будет выглядеть в коде?
Вот например с базой Northwind. Есть там такие таблицы для связи many-to-many: "Employees" <- "EmployeeTerritories" -> "Territories".
Сейчас соответсвующие им классы в bltoolkit выглядят так (взял из unit-тестов):
[TableName("Employees")]
public class Employee
{
[PrimaryKey, NonUpdatable]
public int EmployeeID;
[Association(ThisKey="EmployeeID", OtherKey="EmployeeID")]
public List<EmployeeTerritory> EmployeeTerritories;
}
[TableName("EmployeeTerritories")]
public class EmployeeTerritory
{
[PrimaryKey]
public int EmployeeID;
[PrimaryKey, NotNull]
public string TerritoryID;
[Association(ThisKey="EmployeeID", OtherKey="EmployeeID")] public Employee Employee;
[Association(ThisKey="TerritoryID", OtherKey="TerritoryID")] public Territory Territory;
}
[TableName("Territories")]
public class Territory
{
[PrimaryKey, NotNull]
public string TerritoryID;
[Association(ThisKey="TerritoryID", OtherKey="TerritoryID")]
public List<EmployeeTerritory> EmployeeTerritories;
}
Таблица EmployeeTerritories в базе нужна — это понятно, но вот соответсвующий класс EmployeeTerritory, как мне кажется, выглядит несколько излишним.
Скажем в Entity Framework и в NHibernate есть нативная поддержка таких ассоциаций. Для них соответсвующие классы будут выглядеть как-то так (шаблонно):
public class Employee
{
public int EmployeeID;
public List<Territory> Territories;
}
public class Territory
{
public string TerritoryID;
public List<Employee> Employees;
}
Здесь коллекция Employee.Territories — это список самих территорий, с которыми связян данный сотрудник. А Territory.Employees — список всех сотрудников на данной территории. Промежуточного класса для их связи здесь нет (хотя конечно сама таблица связи где-то в настройках/конфигурации упоминается).
Наверно для bltoolkit можно реализовать нечто подобное, например, введя еще один атрибут ассоциации для связей many-to-many как-то так:
public class Employee
{
public int EmployeeID;
[ManyToManyAssociation(TableName="EmployeeTerritories", ThisKey="EmployeeID", OtherKey="TerritoryID")]
public List<Territory> Territories;
}
public class Territory
{
public string TerritoryID;
[ManyToManyAssociation(TableName="EmployeeTerritories", ThisKey="TerritoryID", OtherKey="EmployeeID")]
public List<Employee> Employees;
}
Linq-запросы к таким таблицам с использованием ассоциаций по идее должны остаться как и прежде. Только в случае если при разборе ET встречается упоминание такого вот свойства с соотвтевующей many-to-many-ассоциацией, то нужно будет генерировать еще один неявный джойн с неявно упоминаемой промежуточной таблицей (EmployeeTerritories).
Несколько сложнее будет придумать элегантное решение для создания/удаления связей между такими сущностями — просто из кода вставлять/удалять классы EmployeeTerritory в базу уже не получится (heavy-ORM здесь так или иначе используют паттерн unit-of-work).
Зачем все это надо? Ну лично мне было бы проще интегрировать bltoolkit в уже существующие приложения, или сочетать в одном приложении как bltoolkit, так и другие средства для работы с базой, или переходить с/на bltoolkit более "плавно".
Здравствуйте, AndrewVK, Вы писали:
AVK>Здравствуйте, ili, Вы писали:
ili>>отчего бы не вынести данное узнавание в метадата провайдер, в таком случае, кому надо, тот сможет припаивать свой код на этот предмет.
AVK>А оно разве сейчас не там?
а, блин... кажись я ослеп GetInheritanceMapping я не увидел, только GetInheritanceDiscriminator заметил, ща перепроверил и нашел...
Здравствуйте, Овощ, Вы писали:
О>Зачем все это надо? Ну лично мне было бы проще интегрировать bltoolkit в уже существующие приложения, или сочетать в одном приложении как bltoolkit, так и другие средства для работы с базой, или переходить с/на bltoolkit более "плавно".
А если описание промежуточных сущностей всё же требовать, но иметь возможность обходится без них в запросе?
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Димчанский, Вы писали:
Д>Что-то в последнее время у меня сайт bltoolkit.net загружается на 10-ый раз с дичайшими задержками и не полностью. Это у меня одного так?
Я туда взгромоздил ScrewTuen Wiki, может быть по-этому.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Димчанский, Вы писали:
IT>>Я туда взгромоздил ScrewTuen Wiki, может быть по-этому. Д>Ну вобщем ни побраузить, ни скачать что-либо не могу.
Здравствуйте, Димчанский, Вы писали:
IT>>Я туда взгромоздил ScrewTuen Wiki, может быть по-этому. Д>Ну вобщем ни побраузить, ни скачать что-либо не могу.
Хотя, похоже хостер тормозит. В общем, надо переезжать.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Димчанский, Вы писали:
Д>A можно ли в BLToolkit использовать динамически генерируемые предикаты (в виде Expression)? Как в L2S это делают.
Любой предикат и так динамический
... << RSDN@Home 1.2.0 alpha 4 rev. 1464 on Windows 7 6.1.7600.0>>
Здравствуйте, AndrewVK, Вы писали:
AVK>Любой предикат и так динамический
Т.е., как я понимаю, во второй параметр в Expressions.MapMember передается просто Expression?
static decimal? RoundToEven(decimal? value)
{
throw new NotImplementedException();
}
static void RoundToEvenTest()
{
Expressions.MapMember<decimal?,decimal?>(
value => RoundToEven(value),
value =>
value - Sql.Floor(value) == 0.5m && Sql.Floor(value) % 2 == 0?
Sql.Floor(value) :
Sql.Round(value));
using (var db = new NorthwindDB())
{
var query =
from o in db.Order
let sum = o.OrderDetails.Sum(d => d.Quantity * d.UnitPrice)
where RoundToEven(sum) >= 10
select o.Freight;
foreach (var item in query)
{
Console.WriteLine(item);
}
}
}
Здравствуйте, IT, Вы писали:
IT>Да. При нахождении RoundToEven в запросе он будет заменён на соответствующее выражение.
Тогда возникает вопрос с многопоточностью.
Т.е., к примеру, я хочу формировать предикат на выборку объектов в зависимости от условий поиска, которые ввел пользователь. Таким образом для каждого пользователя должен генерироваться свой предикат.
Как реализовать такой сценарий? Ведь, на сколько я понимаю, Expressions.MapMember — это регистрация замены выражения, которая будет работать глобально.
Здравствуйте, Димчанский, Вы писали:
Д>Как реализовать такой сценарий? Ведь, на сколько я понимаю, Expressions.MapMember — это регистрация замены выражения, которая будет работать глобально.
Именно. Тот вариант, который ты привёл по ссылке должен работать и без этого. Точно так же как и для L2S.
Если нам не помогут, то мы тоже никого не пощадим.
Re: Linq with BLToolkit
От:
Аноним
Дата:
10.04.10 07:25
Оценка:
Здравствуйте.
Прочитал статью, впечатляет объем и профессионализм проделанной работы. Лёгкость BLT — именно то, что я искал. Но вот в результате просмотра примеров из статьи возник вопрос: в листинге сгенерированного SQL все константы перешли в константы — это на самом деле так происходит, или же всё таки используются параметры? Тот же L2S везде пихает параметры (имеется в виду SqlParameter("@p1", 5)) для оптимизации и безопасности запросов к БД, а как это устроено внутри BLT?
Здравствуйте, Аноним, Вы писали:
А>Но вот в результате просмотра примеров из статьи возник вопрос: в листинге сгенерированного SQL все константы перешли в константы — это на самом деле так происходит, или же всё таки используются параметры?
Константы преобразуются в константы. Параметры в параметры. Существуют случаи, когда для некоторых провайдеров константы преобразуются в параметры, а параметры в константы. Это связано с совместимостью провайдеров.
А>Тот же L2S везде пихает параметры (имеется в виду SqlParameter("@p1", 5)) для оптимизации и безопасности запросов к БД, а как это устроено внутри BLT?
О какой оптимизации речь? Насчёт безопасности, если речь идёт об SQL injection, то с этим всё впорядке, да и программист, написавший такой код сам себе злобный буратино.
Если нам не помогут, то мы тоже никого не пощадим.
Только начинаю пользоваться BLT, и уже сразу с Linq . Увидел, что после выполнения Linq запроса подключение к БД остаётся открытым. Вопрос: можно ли как-то настроить MyDbManager так, чтобы он закрывал подключение автоматически, без вызова Dispose, а по завершении запроса?
Здравствуйте, Димчанский, Вы писали:
Д>A можно ли в BLToolkit использовать динамически генерируемые предикаты (в виде Expression)? Как в L2S это делают.
Здравствуйте!
Пробую с BLT выполнить следующую вещь:
((IQueryProvider)GetBltContext().GetTable<T>()).CreateQuery<T>(expression)
где expression это Expression<Func<T, bool>> expression.
Это кусок метода
public T GetSingle<T>(Expression<Func<T, bool>> expression) where T : class
{
IQueryable<T> queryable = ((IQueryProvider)GetBltContext().GetTable<T>()).CreateQuery<T>(expression);
var list = queryable.ToList();
return queryable.FirstOrDefault();
}
Соответствующий вызов, например,
GetSingle<Customer>(c => c.CustomerID == "NEWCS")
(это для базы Northwind)
При выполнении такого кода ловиться исключение System.ArgumentException: Queryable method call expected. Got 'c => (c.CustomerID == "NEWCS")'. Блуждание в дебаггере привело к мысли, что в качестве рутового узла при разборе дерева поддерживается только MemberAccess или Call или Parameter, но никак не Lambda...
Статью Игоря про Linq в BLT прочитал, но наверное не все/не так понял... Так или иначе непонятно, почему не обрабатывается такое выражение/что я делаю не так. EF4 и LinqToSql такие вещи проглатывают на счет раз.
Здравствуйте, ghostknight83, Вы писали:
G>Здравствуйте, Димчанский, Вы писали:
Д>>A можно ли в BLToolkit использовать динамически генерируемые предикаты (в виде Expression)? Как в L2S это делают. G>
G> public T GetSingle<T>(Expression<Func<T, bool>> expression) where T : class
G> {
G> IQueryable<T> queryable = ((IQueryProvider)GetBltContext().GetTable<T>()).CreateQuery<T>(expression);
G> var list = queryable.ToList();
G> return queryable.FirstOrDefault();
G> }
G>
Если конкретно этот пример — то он реализуется просто
G>При выполнении такого кода ловиться исключение System.ArgumentException: Queryable method call expected. Got 'c => (c.CustomerID == "NEWCS")'. Блуждание в дебаггере привело к мысли, что в качестве рутового узла при разборе дерева поддерживается только MemberAccess или Call или Parameter, но никак не Lambda...
Потому, что анализатор ищет экспрешены вызовов методов IQuerable<T> — Where, Select, Single, etc.
Я имею в виду если параллельно с полями таблицы надо еще вернуть значение хранимой функции, т.е. типа SELECT Field1, Field2, MyFunc(Field3) FROM mytable;
Здравствуйте, MozgC, Вы писали:
MC>Я имею в виду если параллельно с полями таблицы надо еще вернуть значение хранимой функции, т.е. типа SELECT Field1, Field2, MyFunc(Field3) FROM mytable;
Можно. См. Расширение BLToolkit. SQL функции.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>Можно. См. Расширение BLToolkit. SQL функции.
Прокопался полчаса.. что-то не получается как я хочу.
Во-первых, я так понял, что нельзя сделать одну функцию на все хранимые функции (т.е. чтобы можно было просто название ХП указывать и параметры)?
Во-вторых, у меня не получается по-простому вернуть поля + значение хранимой функции, т.к. свойству анонимного класса нельзя указать значение типа Sql.MyFunc(customer.ID). Если использовать let, например так:
var q = from c in db.GetTable<Customer>()
let balance = MySql.GetCustomerBalance(c.ID)
select new {c.ID, c.Name, balance};
то получаю исключение "'c.ID' cannot be converted to SQL."
Хочется же как-то так (в идеале):
var q = from c in db.GetTable<Customer>() select new { c.Name, MySql.GetCustomerBalance(c.ID) };
Здравствуйте, MozgC, Вы писали:
MC>Во-первых, я так понял, что нельзя сделать одну функцию на все хранимые функции (т.е. чтобы можно было просто название ХП указывать и параметры)?
Можно. Но придётся создать несколько таких функций по числу параметров и определиться с типом возвращаемого значения.
[SqlExpression("{0}({1})", 0, 1, ServerSideOnly=true)]
public static T SqlFunction<T>(string functionName, T p1)
{
return p1;
}
[SqlExpression("{0}({1}, {2})", 0, 1, 2, ServerSideOnly=true)]
public static T SqlFunction<T1,T2>(string functionName, T p1, T2 p2)
{
return obj;
}
MC>Во-вторых, у меня не получается по-простому вернуть поля + значение хранимой функции, т.к. свойству анонимного класса нельзя указать значение типа Sql.MyFunc(customer.ID). Если использовать let, например так:
MC>
var q = from c in db.GetTable<Customer>()
MC> let balance = MySql.GetCustomerBalance(c.ID)
MC> select new {c.ID, c.Name, balance};
MC>то получаю исключение "'c.ID' cannot be converted to SQL."
MC>Хочется же как-то так (в идеале):
MC>
var q = from c in db.GetTable<Customer>() select new { c.Name, MySql.GetCustomerBalance(c.ID) };
А как ты GetCustomerBalance объявлял? Нужно как-то так:
[SqlFunction(ServerSideOnly=true)]
public static double GetCustomerBalance(int ID)
{
throw new NotInplementedException();
}
Если нам не помогут, то мы тоже никого не пощадим.
public static class MySql
{
[SqlExpression("{0}({1})", 0, 1, ServerSideOnly = true)]
public static TResult StoredFunction<TResult, TParameter>(string functionName, TParameter parameter)
{
throw new NotImplementedException();
}
}
var q = from c in db.GetTable<Customer>()
let func = MySql.StoredFunction<double, int>("GetCustomerBalance", c.ID)
select new {c.ID, c.Name, func};
Тут выпадает исключение "'c.ID' cannot be converted to SQL."
Если избавиться от let, т.е. написать например так:
var q = from c in db.GetTable<Customer>()
select new Pair(c.Name, MySql.StoredFunction<double, int>("GetCustomerBalance", c.ID));
То получаем такой SQL:
SELECT
c.Name,
'GetCustomerBalance'(c.ID) as c1
FROM
customers c
Здравствуйте, IT, Вы писали:
MC>>Тут выпадает исключение "'c.ID' cannot be converted to SQL." IT>Это скорее всего бага.
Бага будет пофикшена? Не забудется?
IT>Да, точно. Строка будет рассмотрена как значение и заключена в кавычки. Тогда только наследование от SqlExpressionAttribute с перекрытием метода GetExpression.
О>Вот например с базой Northwind. Есть там такие таблицы для связи many-to-many: "Employees" <- "EmployeeTerritories" -> "Territories".
Разбираюсь со связью многие-ко-многим, в Unit тестах есть указанные выше таблицы. Но как с ними работать нет ни одного рабочего примера. Получается вытягивать данные только для агрегированых таблиц (Count и т.п.)
в небольшом примере не могли бы показать, как Вы поддерживаете такую связь
О>Скажем в Entity Framework и в NHibernate есть нативная поддержка таких ассоциаций. Для них соответсвующие классы будут выглядеть как-то так (шаблонно): О>[c#] О>public class Employee О>{ О> public int EmployeeID; О> public List<Territory> Territories; О>}
Здравствуйте, Зайцев Андрей, Вы писали: ЗА>Только начинаю пользоваться BLT, и уже сразу с Linq . Увидел, что после выполнения Linq запроса подключение к БД остаётся открытым. Вопрос: можно ли как-то настроить MyDbManager так, чтобы он закрывал подключение автоматически, без вызова Dispose, а по завершении запроса?
Немного поздновато, но все же: думаю это не MyDbManager оставляет а Connection Pool. Соответственно "pooling=false" в connection string. Но будет существенно меньше скорость (в общем случае)
... << RSDN@Home 1.2.0 alpha 4 rev. 1472>>
Re: Linq with BLToolkit
От:
Аноним
Дата:
02.08.10 16:36
Оценка:
Здравствуйте!
BLToolkit — просто чудо какое-то. Мы с коллегами в данный момент переводим один из проектов с L2S на BLT, модифицировали dblinq для генерации модели совместимой с BLT, все работает просто отлично. Однако остаются не ясными два момента:
1. Как правильно закрывать соединения с базой данных? Вариант с постоянным использованием using немного смущает тем, что это можно элементарно забыть сделать. Можно ли как-то с этим разобраться без ущерба производительности? (Например как в L2S)
2. Как лучше всего заполнить модель из базы данных с заполненными связанными сущностями. Из примера в статье мы видим только вариант в котором нужно перечислить все необходимые поля, включая те что не имеют ассоциации. Возможно ли указать необходимые сущности для маппинга и получить заполненный объект на подобии L2S LoadWith ?
Здравствуйте, Аноним, Вы писали:
А>1. Как правильно закрывать соединения с базой данных? Вариант с постоянным использованием using немного смущает тем, что это можно элементарно забыть сделать. Можно ли как-то с этим разобраться без ущерба производительности? (Например как в L2S)
Можно вообще не указывать контекст, тогда будет использоваться дефолтный и его время жизни будет контролироваться самой библиотекой.
ЗЫ. А как сделано в L2S?
А>2. Как лучше всего заполнить модель из базы данных с заполненными связанными сущностями. Из примера в статье мы видим только вариант в котором нужно перечислить все необходимые поля, включая те что не имеют ассоциации. Возможно ли указать необходимые сущности для маппинга и получить заполненный объект на подобии L2S LoadWith ?
В планах сразу после Linq over WCF.
Если нам не помогут, то мы тоже никого не пощадим.
Re[3]: Linq with BLToolkit
От:
Аноним
Дата:
02.08.10 17:49
Оценка:
Здравствуйте, IT, Вы писали:
IT>ЗЫ. А как сделано в L2S?
Я не знаю как сделано в L2S, однако после замены его провайдера на BLT и запуска ApacheBenchmark с 100 конкурентными запросами, база данных почти сразу выдала "too many connections". Когда обрамили контекст using все стало работать как надо. На сколько я понимаю дело в нашем контексте который генерируется dblinq. Возможно стоит попробовать добавить к нему деструктор...
Здравствуйте, Аноним, Вы писали:
А>Я не знаю как сделано в L2S, однако после замены его провайдера на BLT и запуска ApacheBenchmark с 100 конкурентными запросами, база данных почти сразу выдала "too many connections". Когда обрамили контекст using все стало работать как надо. На сколько я понимаю дело в нашем контексте который генерируется dblinq. Возможно стоит попробовать добавить к нему деструктор...
Деструктор поможет, но не сильно.
В принципе, на сегодняшний день, DataContext является интерфейсом и полностью отчуждаем. При желании можно написать DataContext на свой вкус и цвет.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, IT, Вы писали:
IT>В принципе, на сегодняшний день, DataContext является интерфейсом и полностью отчуждаем. При желании можно написать DataContext на свой вкус и цвет.
Наверное, я не правильно выразился. Наш DataContext напрямую наследуется от DbManager и в нем просто реализуются свойства, которые вызывают DbManager.GetTable<T>.
Мы реализовали схему, при которой не требуется явное освобождение ресурсов.
Базовый репозиторий:
public class Repository<T> where T : DbManager, new()
{
public T Context {
get { return DataContext.Value; }
}
[ThreadStatic]
private volatile static Lazy<T> dataContext;
public static Lazy<T> DataContext {
get { return dataContext ?? (dataContext = new Lazy<T>( () => new T())); }
set { dataContext = value; }
}
}
Реализация репозитория: (OlympDataContext — наш domain класс, который генерируется dblinq из БД, наследует DbManager)
public class NewsRepository : Repository<OlympDataContext>
{
public List<News> GetAllNews()
{
return Context.News.ToList();
}
}
Освобождение DbManager происходит в Global.asax.cs
protected void Application_EndRequest(object sender, EventArgs e)
{
var lazy = Repository<OlympDataContext>.DataContext;
if (lazy.IsValueCreated)
{
lazy.Value.Dispose();
Repository<OlympDataContext>.DataContext = null;
}
}
Таким образом мы имеем один DbManager на все запросы для каждой страницы. Можно создавать любое количество репозиториев в контроллерах. Не нужно беспокоиться о освобождении ресурсов.
Эта схема работает. Имеем ~250 страниц в секунду на средней клиентской машине при 5 запросах к mysql.
Это вторая реализация. Первая инициализировалась следующим образом:
protected void Application_BeginRequest(object sender, EventArgs e)
{
var lazy = new Lazy<OlympDataContext>(() => new OlympDataContext());
Repository<OlympDataContext>.DataContext = lazy;
}
Схема работала до того момента как мы начали использовать in-proc сессию. Как только мы добавили кэширование в сессии, начались случайные выпады в DbManager NullReferenceException при обращении к DbManager._connection. Стоит заметить, что из-за использования сессии производительность падает до 120 запросов в сек (независимо от схемы).
Здравствуйте, Erty Hackward, Вы писали:
IT>>В принципе, на сегодняшний день, DataContext является интерфейсом и полностью отчуждаем. При желании можно написать DataContext на свой вкус и цвет. EH>Наверное, я не правильно выразился. Наш DataContext напрямую наследуется от DbManager и в нем просто реализуются свойства, которые вызывают DbManager.GetTable<T>.
DBManager — это обёртка над соединением. Соответственно и работать с ним нужно подобным же образом, т.е. как с соединением. То, что DBManager реализует IDataContext — это лишь вопрос удобства. Вам нужен контекст, который будет сам создавать и освобождать соединение, когда нужно. Сейчас попробую, что-нибудь изобразить.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, Erty Hackward, Вы писали:
IT>>В принципе, на сегодняшний день, DataContext является интерфейсом и полностью отчуждаем. При желании можно написать DataContext на свой вкус и цвет. EH>Наверное, я не правильно выразился. Наш DataContext напрямую наследуется от DbManager и в нем просто реализуются свойства, которые вызывают DbManager.GetTable<T>.
Здесь пример использования класса DataContext. Думаю, на время запроса вам нужно взводить флаг KeepConnectionAlive в true, а потом сбрасывать в false. Сам объект обнулять не надо, т.к. дальше он будет открывать/закрывать соединение автоматически.
Если нам не помогут, то мы тоже никого не пощадим.
Здравствуйте, pr0ff, Вы писали:
P>Хм, а зачем двойная ленивая инициализация?
Первый уровень нужен для создания второго, потому что мы используем ThreadStatic и нельзя использовать конструкторы. Второй позволяет не создавать DbManager при освобождении ресурсов, если страница без запросов.
ИМХО правильней было бы контекст хранить в HttpContext.Current.Items. Это и логичней и асинхронные обработчики не будут глючить (в них выполнение обычно завершается в другом потоке)
Здравствуйте, pr0ff, Вы писали:
P>ИМХО правильней было бы контекст хранить в HttpContext.Current.Items. Это и логичней и асинхронные обработчики не будут глючить (в них выполнение обычно завершается в другом потоке)
Мы отказались от этой идеи, поскольку это потребовало бы передачи контекста в конструктор репозитория.
как нам кажется гораздо элегантнее:
var newsRepo = new NewsRepository();
Re[5]: Linq with BLToolkit
От:
Аноним
Дата:
10.10.10 21:44
Оценка:
Здравствуйте, IT, Вы писали:
IT>А если описание промежуточных сущностей всё же требовать, но иметь возможность обходится без них в запросе?
а можно увидеть, как обойтись без них в запросе? хотя бы на примерe "Employees" <- "EmployeeTerritories" -> "Territories".
Здравствуйте, Аноним, Вы писали:
IT>>А если описание промежуточных сущностей всё же требовать, но иметь возможность обходится без них в запросе? А>а можно увидеть, как обойтись без них в запросе? хотя бы на примерe "Employees" <- "EmployeeTerritories" -> "Territories".
SQL в обмен на Linq запрос.
Если нам не помогут, то мы тоже никого не пощадим.
Я правильно понял, что ассоциации используются в linq запросах как упрощение и укорачивание запроса вместо join'ов?
Есть ли возможность, получая некий объект, получить заполненнное ассоциированное свойство без выполнения ручных махинаций? Иначе говоря, для схемы в демо проекте, получая объекты Employee, получить для них заполненное свойство EmployeeTerritories.
Здравствуйте, Vlako, Вы писали:
V>Я правильно понял, что ассоциации используются в linq запросах как упрощение и укорачивание запроса вместо join'ов?
Правильно.
V>Есть ли возможность, получая некий объект, получить заполненнное ассоциированное свойство без выполнения ручных махинаций? Иначе говоря, для схемы в демо проекте, получая объекты Employee, получить для них заполненное свойство EmployeeTerritories.
В планах есть. Когда будет реализовано
Если нам не помогут, то мы тоже никого не пощадим.