Сложный запрос с несколькими поисками
Я пытаюсь получить элементы из одного списка, где любое из значений для нескольких запросов содержится в результате другого запроса.
Ситуация такова: я создаю веб-часть, с помощью которой пользователи могут создавать отчеты ("объединение"), используя набор списков sharepoint (Foundation 2010) для пяти раскрывающихся списков свойств. Свойства связаны через поиск в списках. Каждый раз при изменении выпадающего списка происходит обратная передача, и следующие выпадающие списки повторно заполняются.
Проблема заключается в том, что связи между правами собственности не совсем соответствуют выпадающим спискам. Это связано с тем, что выпадающие входы выбраны для интуитивно понятного ввода отчетов, а модель данных предназначена для простого и интуитивного ввода системных данных, а не для эффективного и легкого программирования...
Соответствующая часть модели данных:
-Locaties (локации) это список
-Contracten (контракты) представляет собой список, каждый контракт имеет одну или несколько локаций (multi-lookup)
-Ургентность (срочность) - это список, у каждого срочности есть один контракт (поиск)
-Meldingtypes (тип отчета) представляет собой список
-Categorieën (категории) представляет собой список, каждая категория имеет один тип объединения (поиск), каждая категория имеет один или несколько контрактов (многократный поиск)
Входные данные отчета следующие:
Пользователь выбирает Locatie первым
Пользователь выбирает второй тип Meldingtype
Пользователь выбирает категорию третьим
Пользователь выбирает Контракт четвертым
В итоге пользователь выбирает Urgentie
Конечно, каждый раз, когда выпадающий список должен быть заполнен только допустимыми параметрами. Поэтому, когда выбрано местоположение, в выпадающем списке должны отображаться только те типы объединения, которые имеют категории с контрактом и выбранное местоположение... да, я знаю;)
У меня проблемы с созданием запросов для заполнения выпадающих списков. Я пробовал несколько методов, включая несколько запросов, конструкции с селектором Contains, и ни один из них не работал. В последней итерации я создал запрос, который использует свойство элемента, когда на него ссылается multilookup. Вот пример для раскрывающегося списка типа соединения:
private void fillMeldingtypeDropdown(Intermediair.IntermediairDataContext idctx)
{
var meldingtypeData = (from l in idctx.Locaties
from co in l.ContractenItem
from ca in co.CategorieënItem
where l.Id == selectedLocatieId
select ca.Meldingtype
).Distinct<Intermediair.Item>();
foreach (Intermediair.Item meldingtype in meldingtypeData)
{
ctrl_Meldingtype.Items.Add(new ListItem(meldingtype.Titel, meldingtype.Id.ToString()));
}
}
selectedLocatieId - это свойство, которое получает значение из раскрывающегося элемента управления Locatie.
Приведенный выше код вызывает исключение System.InvalidOperationException. Описание ошибки на голландском языке, в переводе это будет выглядеть примерно так: "В запросе используются неподдерживаемые элементы, такие как ссылки на несколько списков или проекция полной сущности с EntityRef/EntitySet"
Я получаю ту же ошибку, если использую EntitySet.Contains:
private void fillCategorieDropdown(Intermediair.IntermediairDataContext idctx)
{
var contractenData = from c in idctx.Contracten
where c.LocatieS.Contains(
( from l in idctx.Locaties
where l.Id == selectedLocatieId
select l
).First<Intermediair.LocatiesItem>()
)
select c;
var categorieenData = ( from ca in idctx.Categorieën
from co in contractenData
where ca.Contract.Contains(co) && ca.Meldingtype.Id == selectedMeldingtypeId
select ca
).Distinct<Intermediair.CategorieënItem>();
foreach (var categorie in categorieenData)
{
ctrl_Categorie.Items.Add(new ListItem(categorie.Titel, categorie.Id.ToString()));
}
}
Я попробовал несколько перестановок, но я не могу найти правильную. Я не могу найти хороших примеров для запросов linq-to-sharepoint с полями с множественным поиском, и я еще недостаточно хорошо разбираюсь в linq, поэтому я, вероятно, делаю некоторые ошибки мэра. Пока я в этом разбираюсь, буду очень признателен за любую полезную идею.
[Редактировать: еще одна попытка не удалась] Я пытался сделать отдельные запросы всех шагов, чтобы проверить промежуточные результаты. Я также опробовал комбинацию Any-Contains, чтобы сопоставить многократные контракты категории с множественными контрактами, которые я получил от местоположения.
var locatieItem = (from l in idctx.Locaties
where l.Id == selectedLocatieId
select l
).First();
var contractenData = from c in locatieItem.ContractenItem
select c;
var categorieenData = from c in idctx.Categorieën
where c.Contract.Any(co => contractenData.Contains(co))
select c;
var meldingtypeData = (from c in categorieenData
select c.Meldingtype
).Distinct();
foreach (var meldingtype in meldingtypeData)
{
ctrl_Meldingtype.Items.Add(new ListItem(meldingtype.Titel, meldingtype.Id.ToString()));
}
locatieItem и contractenData заполнены, как и ожидалось, но categoryorenData генерирует ту же ошибку снова. [/Редактировать]
PS Поскольку голландские имена в коде очень удобочитаемы на английском языке, я не переводил названия. Извините за путаницу.
2 ответа
Я решил проблему, sortof. Это дает некоторые накладные расходы: слишком много записей извлекается из базы данных. Но только дубликаты, так что, надеюсь, кэширование решит большую часть этих накладных расходов.
Запрос, который я сейчас использую, обманчиво прост.
var categorieenContractenData = from c in contractenData
select c.CategorieënItem;
Это не дает мне один набор Категорий Items, но набор множеств Категорий Items, некоторые из которых могут быть дубликатами. Затем я перебираю каждую запись с помощью двойного цикла foreach и помещаю элементы в три структуры данных для облегчения поиска. Это означает, что для заполнения каждого из трех выпадающих списков мне нужно выполнить только один linq-запрос.
Я ожидаю, что некоторые реальные данные в базе данных к концу недели, так что я собираюсь проверить, достаточно ли это быстро. Если нет, мне придется сделать дополнительный список (заполненный приемниками событий).
Полный код:
private void ensureLoadData(Intermediair.IntermediairDataContext idctx)
{
if (dataLoaded) return;
meldingtypes = new SortedList<string, int?>();
categorieen = new SortedList<int?,SortedList<string,int?>>();
contracten = new SortedList<int?, SortedList<int?, SortedList<string, int?>>>();
IQueryable<Intermediair.ContractenItem> contractenData = from c in
(from l in idctx.Locaties
where l.Id == selectedLocatieId
select l
).SingleOrDefault().ContractenItem
select c;
var categorieenContractenData = from c in contractenData
select c.CategorieënItem;
foreach (EntitySet<Intermediair.CategorieënItem> categorieenPerContract in categorieenContractenData)
{
foreach (Intermediair.CategorieënItem categorie in categorieenPerContract)
{
if (!meldingtypes.ContainsKey(categorie.Meldingtype.Titel))
{
meldingtypes.Add(categorie.Meldingtype.Titel, categorie.Meldingtype.Id);
categorieen.Add(categorie.Meldingtype.Id, new SortedList<string,int?>());
contracten.Add(categorie.Meldingtype.Id, new SortedList<int?, SortedList<string, int?>>());
}
if (!categorieen[categorie.Meldingtype.Id].ContainsKey(categorie.Titel))
{
categorieen[categorie.Meldingtype.Id].Add(categorie.Titel, categorie.Id);
contracten[categorie.Meldingtype.Id].Add(categorie.Id, new SortedList<string,int?>());
foreach (Intermediair.ContractenItem contract in categorie.Contract)
{
contracten[categorie.Meldingtype.Id][categorie.Id].Add(contract.Titel, contract.Id);
}
}
}
}
dataLoaded = true;
}
Существуют некоторые ограничения для linq в SharePoint 2010. Возможно, эта статья поможет вам выбрать правильный путь: http://www.chaholl.com/archive/2010/03/12/joins-in-linq-to-sharepoint-2010.aspx