вчера я работал над подобной проблемой.
Мне пришлось выбрать / загрузить все родительские объекты с точно заданным списком дочерних объектов.
Я мог бы решить это с помощью Criteria-API с одним недостатком (см. * 1 ниже).
public class Project
{
public virtual int ProjectId{get;set;}
public virtual IList<Part> Parts{get;set;}
...
}
public class Part
{
public virtual int PartId{get;set;}
public virtual Project Project{get;set;} // *1 this is the drawback: I need a public property for the ForegienKey from the child to the parent
...
}
Вот критерии:
DetachedCriteria top = DetachedCriteria.For<Project>();
foreach(Part part in searchedParts)
{
DetachedCriteria sub = DetachedCriteria.For<Part>();
sub.Add(Expresion.Eq("PartId",part.PartId));
sub.SetProjection("Project");
top.Add(Subqueries.PropertyIn("ProjectId",sub));
}
Вернемся к вашему примеру: SQL будет выглядеть так.
SELECT * FROM project
WHERE
projectid IN ( SELECT projectid FROM part WHERE partid = 1 /* @p0 */ )
AND projectid IN ( SELECT projectid FROM part WHERE partid = 2 /* @p1 */ )
В основном я добавляю для каждого дочернего элемента подзапрос, который проверяет его наличие в проекте и объединяет их с и, поэтому будет выбран только проект со всеми этими дочерними элементами.
Привет
Juy Juka
Дополнительные виды использования
Я не закончил с моим кодом после этого, и если кому-то понадобится то, что я должен был выяснить, я добавлю его сюда. Я надеюсь, что дополнительная информация принадлежит здесь, но я не уверен, потому что это мой первый пост на stackoverflow.com
Для следующих примеров нам понадобится более сложный класс детали:
public class Part
{
public virtual int PartId{get;set;}
public virtual Project Project{get;set;}
public virtual PartType PartType{get;set;}
...
}
public class PartType
{
public virtual int PartTypeId{get;set;}
public virtual string Name{get;set;}
...
}
Другой критерий для дочерних объектов
Можно использовать тот же код, если у вас нет первичного ключа (-ей) для поиска деталей, но вы хотите найти детали с другими свойствами.
// I am asuming building-projects with houses, gardens, garages, driveways, etc.
IEnumerable<PartType> searchedTypes = new PartType[]{housePart, gardenPart};
// could be a parameter or users choise or what ever
DetachedCriteria top = DetachedCriteria.For<Project>();
foreach(PartType type in searchedTypes)
{
DetachedCriteria sub = DetachedCriteria.For<Part>();
sub.Add(Expresion.Eq("PartType",type)); // this is all that had to be changed. We could even use more complex operations with and, or, not, etc.
sub.SetProjection("Project");
top.Add(Subqueries.PropertyIn("ProjectId",sub));
}
Ожидаемый SQL
SELECT * FROM project
WHERE
projectid IN ( SELECT projectid FROM part WHERE parttype = 1 /* @p0 // aka. housePart */ )
AND projectid IN ( SELECT projectid FROM part WHERE parttype = 2 /* @p1 // aka. gardenPart */ )
Исключая детей
Чтобы отрицать это и искать партнеров, которые не имеют искомые дочерние элементы, легко сделать, используя Subqueries.PropertyNotIn вместо Subqueries.PropertyIn.
Точно / только разыскиваемые дети
Это была сложная часть, над которой мне приходилось работать дольше всего. Я хотел родителей с точно данным списком частей.
Чтобы остаться с примером строительного проекта: я ищу проекты с частью дома и частью гвардии, но без других частей
IEnumerable<PartType> searchedTypes = new PartType[]{housePart, gardenPart};
DetachedCriteria top = DetachedCriteria.For<Project>();
ICriterion notCriterion = null;
foreach(PartType type in searchedTypes)
{
ICriterion subCriterion = Expresion.Eq("PartType",type);
DetachedCriteria sub = DetachedCriteria.For<Part>();
sub.Add(subCriterion);
sub.SetProjection("Project");
top.Add(Subqueries.PropertyIn("ProjectId",sub));
// I am collecting all valid criterions for child-objects and negate them
subCriterion = Expresion.Not(subCriterion);
notCriterion = notCriterion == null ? subCriterion:Expresion.And(notCriterion,subCriterion);
}
// with the negated criterions I exclude all parent-objects with an invalid child-object
DetachedCriteria not = DetachedCriteria.For<Part>();
not.Add(notCriterion);
sub.SetProjection("Project");
top.Add(Subqueries.PropertyNotIn("ProjectId",not));
Ожидаемый SQL
SELECT * FROM project
WHERE
projectid IN ( SELECT projectid FROM part WHERE parttype = 1 /* @p0 // aka. housePart */ )
AND projectid IN ( SELECT projectid FROM part WHERE parttype = 2 /* @p1 // aka. gardenPart */ )
AND projectid NOT IN ( SELECT projectid FROM part
WHERE
NOT ( parttype = 1 /* @p2 // aka. housePart */ )
AND NOT ( parttype = 2 /* @p3 // aka. gardenPart */ )
)
(Возможно использование более одного дома и / или одного гвардейца, поскольку не делается пометка "дублированных" записей)