2010-03-09 1 views
2

В проекте может быть много частей. Свойством Part является Ipn, представляющая собой строку цифр.Как построить критерии NHibernate для поиска родителей, у которых есть все указанные дети.

  • Проект "А" имеет частей "1", "2", "3"
  • Проект "Б" имеет частей "2", "3", "4"
  • Проект "С" имеют части «2»
  • Проект «D» имеют части «3»

Я хочу, чтобы найти все проекты, которые имеют все указанные части, связанные с ним. Мой текущий запрос

  var ipns = new List<String> { "2", "3" } 

      var criteriaForIpns = DetachedCriteria 
       .For<Part>() 
       .SetProjection(Projections.Id()) 
       .Add(Expression.In("Ipn", ipns)); 

      _criteriaForProject 
       .CreateCriteria("Ipns") 
       .Add(Subqueries.PropertyIn("Id", criteriaForIpns)); 

Это дает мне обратно все проекты, которые имеют какой-либо из частей, таким образом, результирующий набор проектов A, B, C и D.

SQL, где генерируется положение, внешний вид что-то вроде

WHERE part1_.Id in (SELECT this_0_.Id as y0_ 
        FROM Parts this_0_ 
        WHERE this_0_.Ipn in ('2' /* @p0 */,'3' /* @p1 */)) 

Моего желаемого результата будет только проекты а и В. Как я могу построить критерии NHibernate, чтобы получить набор результатов, что мне нужно?

Количество деталей, которые я искал, может варьироваться, это может быть n количество деталей.

ответ

0

Ваш запрос требует, чтобы мы сделали два соединения из проекта в часть. Это невозможно в критериях.

HQL
Вы можете выразить этот запрос непосредственно в HQL.

var list = session.CreateQuery(@" 
    select proj from Project proj 
     inner join proj.Parts p1 
     inner join proj.Parts p2 
    where p1.Id=:id1 
    and p2.Id=:id2 
    ") 
    .SetInt32("id1", 2) 
    .SetInt32("id2", 3) 
    .List<Master>(); 

Критерии
С Criteria API, вы бы запросить для тех проектов, которые имеют одну из указанных частей, и фильтровать результаты в C#.

У каждого из них есть критерии, требующие загрузки Project.Parts или сопоставьте это как lazy = "extra".

Затем, используя существующий критерий запроса сверху.

// Load() these if necessary 
List<Parts> required_parts; 

var list = _criteriaForProject.List<Project>() 
    .Where(proj => { 
     foreach(var p in required_parts) { 
      if (!proj.Parts.Contains(p))) { 
       return false; 
      } 
      return true; 
     } 
    }); 

// if _criteriaForProject is a Detached Criteria, that would be: 
var list = _criteriaForProject.GetExecutableCriteria(session) 
    .List<Project>() 
    .Where(// etc 
+0

Быстрые методы <>() и .Where() из LINQ? от NHibernate? Я пытаюсь интегрировать ваш ответ в свое решение, но я не нахожу эти методы. – jsmorris

+0

ICriteria.List() - метод NH, а IEnumerable.Where() - метод LINQ. –

2

вчера я работал над подобной проблемой. Мне нужно было выбрать/загрузить все родительские объекты с точно заданным списком дочерних объектов. Я мог бы решить это с помощью 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 */) 

Basicaly добавить для каждого ребенка подзапрос, который проверяет его в Существование проекта и объединить их с и, таким образом только проект со всем, что будут отобраны дети.

Приветствия

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 */) 

За исключением детей

Отрицать это и поиск partens, которые делают не есть искомая дети легко сделать с помощью Subqueries.PropertyNotIn вместо подзапросов .PropertyIn.

/только искавшие детей

Ровно Это была сложная часть я должен был работать на долгое время. Я хотел, чтобы родители с точно указанным списком частей. Чтобы остаться на примере строительно-проекта: Я ищу проекты домового части и guarden-части, но никаких других частей

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 */) 
        ) 

(Более один дом и/или один стражник возможен, так как не выполняется никаких «дублированных» записей)