如何构建NHibernate标准来查找具有所有指定子项的父项

时间:2010-03-09 22:39:41

标签: nhibernate subquery

项目可以有很多部分。 Part上的属性是Ipn,它是一串数字。

  • 项目“A”具有部件“1”,“2”,“3”
  • 项目“B”具有部件“2”,“3”,“4”
  • 项目“C”具有部件“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子句类似于

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

我想要的结果只是项目A和B.如何构建NHibernate标准以获得我需要的结果集?

我搜索的部件数量可能会有所不同,可能是n个部件。

2 个答案:

答案 0 :(得分: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

其他用途

在此之后我没有完成我的代码,如果somone需要我必须找到的东西,我会在这里添加它。我希望附加信息属于这里,但我不确定,因为这是我在stackoverflow.com上的第一篇文章

对于以下示例,我们需要一个更复杂的part-class:

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,可以轻松完成 的搜索。

完全/仅搜索过的孩子

这是我在最长时间内工作的棘手部分。我希望父母完全按照给定的部件列表。 继续建筑项目的例子:我正在寻找带有房屋部分和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 */ )
                     )

(更多一个房子和/或一个guarden是可能的,因为没有检查“重复”条目)

答案 1 :(得分:0)

您的查询要求我们从Project到Part进行两次连接。这在标准中是不可能的。

<强> 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