#asp.net-mvc #entity-framework #search #repository-pattern #entity-framework-4.1
#asp.net-mvc #entity-framework #Поиск #репозиторий-шаблон #entity-framework-4.1
Вопрос:
Я использую шаблон Generic Repository для абстрагирования моего EF 4.1 DbContext. Я также использую уровень сервиса для запроса и фиксации изменений в репозитории (ах). В любом случае, чего я пытаюсь добиться здесь, это разрешить пользователю вводить поисковую фразу в строку поиска. Затем SearchService должен запросить базу данных, чтобы найти любые категории верхнего уровня, подкатегории или элементы, которые содержат поисковую фразу в своих названиях. На первый взгляд, я думал, что это будет просто сделать, но, по-видимому, все немного сложнее. Вот что я пытался сделать:
public IList<Item> Search(string searchPhrase)
{
var result = new List<Item>();
var tmp = from c in _repository.GetQuery<TopLevelCategory>(c=>c.Children)
where c.Name.Contains(searchPhrase)
select c;
if(tmp.Count() > 0)
{
foreach (var c in tmp)
{
var children = c.Children;
foreach (var childCategory in children)
{
result.Concat(childCategory.Items);
}
}
}
var tmp2 = from c in _repository.GetQuery<ChildCategory>(c=>c.Items)
where c.Name.Contains(searchPhrase)
select c;
if(tmp.Count() > 0)
{
foreach (var childCategory in tmp2)
{
result.Concat(childCategory.Items);
}
}
var tmp3 = from c in _repository.GetQuery<Item>()
where c.Title.Contains(searchPhrase)
select c;
if(tmp3.Count() > 0)
{
result.Concat(tmp3);
}
return resu<
}
}
Я знаю, что это выглядит некрасиво и запутанно, но я просто попробовал посмотреть, возвращает ли он правильные результаты. Ну, это не так, это вызвало следующее исключение:
Сведения об исключении: Система.Исключение InvalidOperationException: С этой командой уже связан открытый DataReader, который должен быть закрыт первым.
Source:
Line 31: foreach (var childCategory in children)
Line 32: {
Line 33: result.Concat(childCategory.Items);
Line 34: }
В любом случае, должен быть более разумный способ сделать это… Есть предложения?
Ответ №1:
В исключении говорится, что вы открыли DataReader для загрузки категорий верхнего уровня с их дочерними элементами, но вы запускаете отложенную загрузку во внутреннем цикле foreach. Для этого требуется другой DataReader для открытия и чтения отложенных загруженных элементов:
// Iterate top level categories => fist openned DataReader
foreach (var c in tmp)
{
// Child category is eager loaded
var children = c.Children;
foreach (var childCategory in children)
{
// Items are not eager loaded => trigger lazy loading and open new DataReader
result.Concat(childCategory.Items);
}
}
Чтобы решить эту проблему, вы должны изменить строку подключения и добавить поддержку MARS MultipleActiveResultSets=true
. MARS поддерживается по крайней мере SQL Server 2005 и 2008. Другой способ избежать этого — также загружать элементы с нетерпением.
Это больше похоже на задачу для полнотекстового поиска на уровне базы данных.
Комментарии:
1. @Ladislav Mrnka: Спасибо за ответ. Имеет полный смысл. Да, вы правы, я включаю категории подуровня в категории верхнего уровня, но я не знаю, могу ли я также включить элементы. Это потому, что для элементов в классе категории верхнего уровня нет свойства навигации. Есть ли способ обойти это? А как насчет MARS, влияет ли это как-нибудь на производительность?
2. MARS — это режим по умолчанию, если вы разрешаете EF создавать строку подключения (например, при использовании EDMX). В вашем сценарии нет способа избежать MARS. В этом случае не должно быть никаких проблем с производительностью. Для описания производительности, связанной с MARS, смотрите: msdn.microsoft.com/en-us/library /…
3. @Ladislav Mrnka: Я добавил MARS в строку подключения и отладил ее. По-видимому, никакие элементы не извлекаются с помощью запроса категорий верхнего уровня, потому что я не включаю объект Item, поэтому мне нужно обойти это… И, кстати, по какой-то причине запрос выполняется внутри бесконечного цикла, вызывающего исключение StackOverflowException. Есть мысли?
4. Какая часть получает бесконечный цикл?
5. @Ladislav Mrnka: Я не совсем уверен, это показывает мне, что ошибка возникает из
@Html.Action("Search", "Item")
вызова внутри моего представления. Но я думаю, что это на уровне категорий верхнего уровня…
Ответ №2:
Ошибка, которую вы получаете, может быть связана с MARS. Вам просто нужно включить несколько активных наборов результатов (MARS), просто добавьте ‘MultipleActiveResultSets= True’ в строку подключения. Проверьте здесь
- Вы можете создать представление, которое объединяет имена на верхнем уровне, дочернем уровне и элементах. Затем предоставьте это представление как объект. Помните, что у EF будут проблемы с представлениями, поскольку его разработчик пытался определить поле ID.
- Вы можете посмотреть на lucene на случай, если считаете, что вам требуются более надежные возможности запросов.