Динамический запрос с linq к объектам с использованием PredicateBuilder
Я пытаюсь выполнить запрос к очень простой таблице, но не получаю никакого результата. Таблица содержит журнал действий со следующими столбцами:
- Я БЫ
- ActionTypeID (создание, редактирование или удаление)
- EntryID (идентификатор созданной / отредактированной / или удаленной записи)
- TableName (где было совершено действие)
- ControlName
- Лицо (совершившее действие. NT-пользователь с отделом)
- Дата
То, что я хотел получить, это список всех EntryID с ActionTypeID == 1 и Person (отдел), заканчивающийся одной из динамического списка строк. Проблема заключается в том, что только первое действие для одного EntryID в журнале действий должно быть проверено на соответствие одному из этих отделов.
Я знаю, это звучит довольно странно. Это может быть результатом моего плохого английского и неспособности проектирования базы данных моего бывшего коллеги. Я не знаю, что хуже.
Моя попытка получить желаемый результат заключается в следующем:
var predicate = PredicateBuilder.False<ActionLog>();
var pOuter = PredicateBuilder.True<ActionLog>();
pOuter = pOuter.And(h => h.TableName.ToUpper() == "Contact".ToUpper());
pOuter = pOuter.And(h => h.ActionType.ID == 1);
foreach (var dep in b.DepartmentList)
{
predicate = predicate.Or(x => x.Person.EndsWith("/" + dep.Value));
}
pOuter = pOuter.And(predicate.Expand());
mContactIDs = (from h in db.ActionLog
orderby h.Date
select h).AsExpandable().Where(pOuter).Select(x => x.EntryID).Distinct().ToList();
Но это возвращает мне все записи, где "Персона" заканчивается выбранными отделами. Я просто хочу проверить первую запись с этим EntryID.
Я надеюсь, что кто-нибудь понимает, что я пытаюсь перевести с запутанного немецкого на странный английский.
о, кстати, это.Net 3.5
РЕДАКТИРОВАТЬ Я думаю, у меня есть некоторые проблемы с поиском правильного объяснения - я попробую его с некоторыми фиктивными данными.
ID | ActionTypeID | EntryID | TableName | ControlName | Person | Date
----------------------------------------------------------------------------------------
1 | 1 | 3 | 'Contacts' | NULL | 'xxxx/department1' | 04/11/2012
2 | 1 | 3 | 'Contacts' | NULL | 'yyyy/department2' | 04/11/2012
3 | 1 | 5 | 'Contacts' | NULL | 'yyyy/department2' | 04/13/2012
4 | 1 | 14 | 'Contacts' | NULL | 'zzzz/department1' | 04/16/2012
В моем примере я бы искал записи в журнале с "Лицо, оканчивающееся на" / Department2 ", создало первое вхождение нового EntryID", "TableName ==" Контакты "" и "ActionTypeID == 1". Результат, который я хочу получить, будет (только EntryIDs) 5, так как это единственная запись, в которой пользователь отдела2 был первым. Вставка этого EntryID с ActionTypeID 1. Я знаю, что это глупо, и если я спроектировал базу данных, то я Я бы так не поступил, но теперь я должен с этим справиться. Если количество отделов для запроса не будет динамическим, я бы использовал этот фрагмент:
IQueryable<ActionLog> log = db.ActionLog;
mContactIDs = (from k in db.Contacts
where (from h in log
where h.TableName == "Contacts"
&& h.EntryID == k.ID
&& h.ActionType.ID == 1
orderby h.Date
select h).FirstOrDefault().Person.EndsWith("/" + b.Department)
select k.ID).ToList();
Но я не знаю, как связать эти динамические отделы с глупым дизайном (и несколькими ActionTypeID 1 (создать) для одной записи. Я даже не знаю, почему приложение сохраняет это)
Спасибо, Бен.
2 ответа
Я решил использовать Entity SQL вместо этого.
List<int> mContactIDs = new List<int>();
using (var db = new Entities())
{
string queryString = @"
SELECT
VALUE H.EntryID
FROM
Entities.ActionLog AS H
WHERE
H.ID IN (
SELECT VALUE TOP (1) Hi.ID
FROM Entities.ActionLog AS Hi
WHERE Hi.ActionType.ID = 1
AND Hi.TableName = 'Kontakt'
AND Hi.EntryID = H.EntryID
ORDER BY Hi.Date
)
{0}
GROUP BY H.EntryID";
List<string> lDepartments = new List<string>();
foreach (var dep in b.DepartmentList)
{
lDepartments.Add(string.Format(" H.Person LIKE '%{0}' ", dep.Value));
}
string sDepartmentQuery = string.Empty;
if (lDepartments.Count > 0)
{
sDepartmentQuery = string.Format(" AND ({0}) ", string.Join(" OR ", lDepartments.ToArray()));
}
var result = db.CreateQuery<int>(string.Format(queryString, sDepartmentQuery));
if (result != null && result.Count() > 0)
{
mContactIDs = result.ToList();
}
}
Я думаю, что проблема заключается в захваченном значении переменной (т.е. dep.Value
) внутри цикла foreach. Сделайте копию переменной и используйте ее в выражении. Проблема объясняется здесь.
foreach (var dep in b.DepartmentList)
{
var depValue = dep.Value;
predicate = predicate.Or(x => x.Person.EndsWith("/" + depValue));
}