Nhibernate QueryOver - 对多对多关系执行 a 具有全部

本文关键字:全部 关系 QueryOver Nhibernate 执行 | 更新日期: 2023-09-27 18:31:38

为了更容易地解释我的问题,我将创建以下虚构示例,说明一个非常基本的多对多关系。 一辆汽车可以有很多零件,一个零件可以属于许多汽车

数据库架构:

CAR_TABLE
---------
CarId
ModelName
CAR_PARTS_TABLE
---------------
CarId
PartId
PARTS_TABLE
-----------
PartId
PartName

类:

public class Car 
{
  public int CarId {get;set;}
  public string Name {get;set;}
  public IEnumerable<Part> Parts {get;set;}
}
public class Part 
{
  public int PartId {get;set;}
  public string Name {get;set}
}

使用这个非常简单的模型,我想从我正在搜索的零件列表中获取任何分配了所有零件的汽车。

假设我有一个 PartId 数组:

var partIds = new [] { 1, 3, 10};

我想在数据库调用方面模拟以下 c# 代码:

var allCars = /* code to retrieve all cars */
var results = new List<Car>();
foreach (var car in allCars) 
{
  var containsAllParts = true;
  foreach (var carPart in car.Parts)
  {
    if (false == partIds.Contains(carPart.PartId))
    {
      containsAllParts = false;
      break;
    }
  }
  if (containsAllParts)
  {
    results.Add(car);
  }
}
return results;

需要明确的是:我想获取具有从 partIds 数组指定的所有零件的汽车。

我有以下查询,它的效率非常低,因为它为 partIds 数组中的每个 id 创建一个子查询,然后对它们的每个结果执行 IsIn 查询。我迫切希望找到一种更有效的方式来执行此查询。

Car carAlias = null;
Part partAlias = null;
var searchCriteria = session.QueryOver<Car>(() => carAlias);
foreach (var partId in partIds)
{
  var carsWithPartCriteria = QueryOver.Of<Car>(() => carAlias)
    .JoinAlias(() => carAlias.Parts, () => partAlias)
    .Where(() => partAlias.PartId == partId)
    .Select(Projections.Distinct(Projections.Id()));
  searchCriteria = searchCriteria
    .And(Subqueries.WhereProperty(() => carAlias.Id).In(carsWithPartCriteria));
}
var results = searchCriteria.List<Car>();

有没有一种体面的方法可以使用NHibernate执行这种查询?

Nhibernate QueryOver - 对多对多关系执行 a 具有全部

这应该正是你想要的...

Part part =  null;
Car car = null;
var qoParts = QueryOver.Of<Part>(() => part)
                .WhereRestrictionOn(x => x.PartId).IsIn(partIds)
                .JoinQueryOver(x => x.Cars, () => car)
                .Where(Restrictions.Eq(Projections.Count(() => car.CarId), partIds.Length))
                .Select(Projections.Group(() => car.CarId));
Part partAlias=null;
Session.QueryOver<Car>().JoinQueryOver(x=>x.Parts,()=>partAlias)
.WhereRestrictionOn(()=>partAlias.Id).IsIn(partIds)  //partIds should be implement an ICollection
.List<Car>();

希望有帮助。

此答案的修改版本

var cars = session.CreateQuery("SELECT c.id FROM Car c JOIN c.Parts p WHERE p.PartId IN(:parts) GROUP BY c HAVING COUNT(DISTINCT p) = :partsCount")
        .SetParameterList("parts", partIds)
        .SetInt32("partsCount", partIds.Count)
        .List();

AFAIK having条款仅在 HQL 中可用。如果需要更多列,可以修改选择/分组依据列表。另一种可能的方法是生成一个内部联接到每个特定部件 id 的 HQL 查询。我认为ICritiria也不可能做到这一点,因为它只允许您加入一次财产。