【问题标题】:Nhibernate QueryOver - Performing a Has All for a Many-to-Many relationshipNhibernate QueryOver - 为多对多关系执行 Has All
【发布时间】:2012-04-30 08:19:14
【问题描述】:

为了更容易解释我的问题,我将创建以下虚构示例,说明一个非常基本的多对多关系。一个Car可以有很多Parts,而一个Part可以属于很多Cars

数据库架构:

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 执行这种查询?

【问题讨论】:

    标签: c# nhibernate many-to-many queryover


    【解决方案1】:

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

    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));
    

    【讨论】:

    • 非常巧妙的将查询路径反转为从Parts的上下文开始。 :) 我会试试这个,让你知道。谢谢。
    • 完美运行,效率更高。 x
    【解决方案2】:

    这个answer的修改版

    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 中可用。如果您想要更多列,您可以修改 select/group by 列表。另一种可能的方法是生成一个内部连接到每个特定部件 ID 的 HQL 查询。我认为 ICritiria 也不可能,因为它只允许你加入一个属性一次。

    【讨论】:

      【解决方案3】:
      Part partAlias=null;
      Session.QueryOver<Car>().JoinQueryOver(x=>x.Parts,()=>partAlias)
      .WhereRestrictionOn(()=>partAlias.Id).IsIn(partIds)  //partIds should be implement an ICollection
      .List<Car>();
      

      希望对您有所帮助。

      【讨论】:

      • 对不起,我已经重新审查了这个,因为我需要它,这并不能解决我的问题。我需要所有具有所提供阵列中所有零件的汽车,而不是具有阵列中指定的任何零件的汽车。道歉!
      猜你喜欢
      • 2011-04-23
      • 2015-01-02
      • 1970-01-01
      • 2011-03-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-23
      相关资源
      最近更新 更多