Это MDI приложение и для каждого объекта открывается своя форма для редактирования объекта.
У каждой формы свой контекст (DataContext).
Мы решили воспользовать предлагаемой возможностью CompiledQuery!
Но вот проблема при первом выполнение запроса CompiledQuery запоминается контекст, но при последующих выполнениях этого же запроса в другом контексте, он НИЧЕГО не знает о полученных объектах, кажется что он получил результат для старого контекста. По этой причине когда мы пытаемся сохранить объект то он ругается, что объекты из другого контекста и это не поддерживается Linq to SQL.
Текст ошибки:
===================================
An attempt has been made to Attach or Add an entity that is not new, perhaps having been loaded from another DataContext. This is not supported. (System.Data.Linq)
------------------------------
Program Location:
at System.Data.Linq.ChangeTracker.StandardChangeTracker.Track(MetaType mt, Object obj, Dictionary`2 visited, Boolean recurse, Int32 level)
at System.Data.Linq.ChangeTracker.StandardChangeTracker.Track(Object obj, Boolean recurse)
at System.Data.Linq.ChangeTracker.StandardChangeTracker.Track(Object obj)
at System.Data.Linq.ChangeProcessor.TrackUntrackedObjects(MetaType type, Object item, Dictionary`2 visited)
at System.Data.Linq.ChangeProcessor.TrackUntrackedObjects(MetaType type, Object item, Dictionary`2 visited)
at System.Data.Linq.ChangeProcessor.TrackUntrackedObjects()
at System.Data.Linq.ChangeProcessor.SubmitChanges(ConflictMode failureMode)
at System.Data.Linq.DataContext.SubmitChanges(ConflictMode failureMode)
...
Здравствуйте, fabio, Вы писали:
F>Мы решили воспользовать предлагаемой возможностью CompiledQuery!
F>Но вот проблема при первом выполнение запроса CompiledQuery запоминается контекст, но при последующих выполнениях этого же запроса в другом контексте, он НИЧЕГО не знает о полученных объектах, кажется что он получил результат для старого контекста. По этой причине когда мы пытаемся сохранить объект то он ругается, что объекты из другого контекста и это не поддерживается Linq to SQL.
Так и должно быть. К CompliledQuery это вообще не имеет никакого отношения.
Прочитанные записи "привязаны" к своему контексту и с другим работать не будут. Есть паттерн такой — Unit of Work — как раз описывает работу с DataContext.
Если же всё-таки очень надо — клонируйте объекты и клоны можно приаттачить к другому контексту.
Здравствуйте, Curufinwe, Вы писали: >Так и должно быть. К CompliledQuery это вообще не имеет никакого отношения. >Прочитанные записи "привязаны" к своему контексту и с другим работать не будут. Есть паттерн такой — Unit of Work — как раз описывает работу с DataContext. >Если же всё-таки очень надо — клонируйте объекты и клоны можно приаттачить к другому контексту.
Если я правильно понимал CompliledQuery -> это сохраненый запрос на базе но не сохраненые записи в памяти приложения.
Вопрос: При клонирование объекта он действительно забывает про свой контекст и готов к Atach.
Здравствуйте, fabio, Вы писали:
F>Если я правильно понимал CompliledQuery -> это сохраненый запрос на базе но не сохраненые записи в памяти приложения.
Не совсем. В CompliledQuery хранится сгенерированный из Linq выражения SQL запрос, в который только осталось подставить параметры. Это позволяет не делать каждый раз идентичное преобразование Linq expression -> SQL query. БД как таковая в этом никак не учавствует.
F>Вопрос: При клонирование объекта он действительно забывает про свой контекст и готов к Atach.
Здравствуйте, Curufinwe, Вы писали:
F>>Если я правильно понимал CompliledQuery -> это сохраненый запрос на базе но не сохраненые записи в памяти приложения. C>Не совсем. В CompliledQuery хранится сгенерированный из Linq выражения SQL запрос, в который только осталось подставить параметры. Это позволяет не делать каждый раз идентичное преобразование Linq expression -> SQL query. БД как таковая в этом никак не учавствует. F>>Вопрос: При клонирование объекта он действительно забывает про свой контекст и готов к Atach. C>Да, проверено на практике.
А есть вариант сохранить скомпилированные запросы на Базе (по средствам Linq to SQL) чтоб еще поднять скорость исполнения?
Спасибо. За то что помогаете разобраться. Ато в инете нет нормальное объяснения.
Здравствуйте, fabio, Вы писали:
F>А есть вариант сохранить скомпилированные запросы на Базе (по средствам Linq to SQL) чтоб еще поднять скорость исполнения?
MSSQL самостоятельно кеширует скомпилированные запросы, если они одинаковые (т.е. текст запроса одинаковый, но параметры могут отличаться), так что делать что-то дополнительно здесь не надо
F>Спасибо. За то что помогаете разобраться. Ато в инете нет нормальное объяснения.
Так-как CompiledQuery в сформированные запросы подставлются параменты, то мы решили что можно передавать масивы при условии соответствия одному из значений:
на SQL:
select * from dbo.City
where id in (2,5,7,12)
на Linq to Sql:
var list = new int[]{2,5,7,12};
Context.GetTable<City>().Where(wr=>list.Contains(wr.ID)).ToList();
Мы попробовали использовать данную конструкцию в запросах CompiledQuery.
-- И он дает ошибку --
Здравствуйте, fabio, Вы писали:
F>Так-как CompiledQuery в сформированные запросы подставлются параменты, то мы решили что можно передавать масивы при условии соответствия одному из значений: F>на SQL:
F>
F> select * from dbo.City
F> where id in (2,5,7,12)
F>
F>на Linq to Sql:
F>
F> var list = new int[]{2,5,7,12};
F> Context.GetTable<City>().Where(wr=>list.Contains(wr.ID)).ToList();
F>
F>Мы попробовали использовать данную конструкцию в запросах CompiledQuery. F> -- И он дает ошибку --
F>Может кто-нибудь уже пробовал? Помогите.
Конечно пробовали
list в запросе может содержать разное количество данных, а при компиляции запроса количество параметров должно быть известно заранее поэтому на любую конструкцию list.Contains он ругается.
Лучший вариант не использовать CompiledQuery для подобных запросов.
Можно использовать raw SQL если скорость очень критична.
Есть конечно другое "полурешение" этой проблеммы с использованием CompiledQuery, но с очень большим гемороем, так что даже рассказывать не хочется
Здравствуйте, Curufinwe, Вы писали:
C>Конечно пробовали C>list в запросе может содержать разное количество данных, а при компиляции запроса количество параметров должно быть известно заранее поэтому на любую конструкцию list.Contains он ругается. C>Лучший вариант не использовать CompiledQuery для подобных запросов. C>Можно использовать raw SQL если скорость очень критична. C>Есть конечно другое "полурешение" этой проблеммы с использованием CompiledQuery, но с очень большим гемороем, так что даже рассказывать не хочется
У меня данный отбор стоит в основе всего запроса и мне очень критична скорость потому-что запрос получается очень большим.
Можете что-нибудь посоветовать по этому поводу.
Так чтоб это можно было вставить в CompiledQuery.
Здравствуйте, fabio, Вы писали:
F>У меня данный отбор стоит в основе всего запроса и мне очень критична скорость потому-что запрос получается очень большим. F>Можете что-нибудь посоветовать по этому поводу. F>Так чтоб это можно было вставить в CompiledQuery.
Я бы всё-таки посоветовал сначала протестировать скорость работы Linq без CompiledQuery и просто идентичного SQL запроса. Если разница в разы, а построить SQL вручную очень сложно, то может и стоит рассматривать то "полурешение".
Вкратце смысл следующий:
В полуавтоматическом режиме преобразовываем linq expression wr=>list.Contains(wr.ID) в wr=> wr.ID == id1 || wr.ID == id2 || и т.д. (т.е. работать придётся напрямую с linq выражением).
Далее для каждого размера массива компилируется свой Linq запрос (поэтому если рамеры массива могуть быть большими > 10, то этот подход вообще не имеет смысла). Получается, что для каждого размера массива производится только одна компиляция.
Если этот подход всё-таки вас заинтересует, то возможно позже я смогу выслать какие-то примеры кода. Сейчас просто вдаватся в подробности нет времени.
Здравствуйте, Curufinwe, Вы писали:
C>Я бы всё-таки посоветовал сначала протестировать скорость работы Linq без CompiledQuery и просто идентичного SQL запроса. Если разница в разы, а построить SQL вручную очень сложно, то может и стоит рассматривать то "полурешение".
C>Вкратце смысл следующий: C>В полуавтоматическом режиме преобразовываем linq expression wr=>list.Contains(wr.ID) в wr=> wr.ID == id1 || wr.ID == id2 || и т.д. (т.е. работать придётся напрямую с linq выражением). C>Далее для каждого размера массива компилируется свой Linq запрос (поэтому если рамеры массива могуть быть большими > 10, то этот подход вообще не имеет смысла). Получается, что для каждого размера массива производится только одна компиляция.
C>Если этот подход всё-таки вас заинтересует, то возможно позже я смогу выслать какие-то примеры кода. Сейчас просто вдаватся в подробности нет времени.
Я произвел замеры и примерно скорость измеряется в пределах 2-10 (от сложности запроса с полученными данными после второй фильтрации)(это условия применяется только во второй фильтрации).
Думаю надо включать полушарие.
Думаю идея linq expression wr=>list.Contains(wr.ID) в wr=> wr.ID == id1 || wr.ID == id2 || --> не очень подходит.
Здравствуйте, fabio, Вы писали: F>то он ругается, что объекты из другого контекста и это не поддерживается Linq to SQL.
public void Detach()
{
_category = default(EntityRef<Category>);
_items = default(EntitySet<Item>);
// и т.д. для всех EntityRef и EntitySet
}
После вызова этого метода, объект готов к привязке к другому контексту.
P.S. после этого работать со старым контекстом не рекомендуется — хз как он себя поведет.
Здравствуйте, fabio, Вы писали: F>Может кто-нибудь уже пробовал? Помогите.
Не пробовал.
Вызывайте DataContext.GetCommand(query), а дальше компилируйте команду методом Prepare, меняйте значения параметров, получайте DataReader и передавайте его методу DataContext.Translate. Будет работать только с таким же кол-вом элементов (как и любой другой вариант).
Или же можно попробовать массив передать в троке, а на сервере через хранимку или функцию преобразовать эту строку в таблицу. Правда хз будет ли быстрее.