3

私の問題をより簡単に説明するために、非常に基本的な多対多の関係を示す次の架空の例を作成します。Carは多くのPartを持つことができ、Partは多くのCarに属することができます。

データベース スキーマ:

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

この非常に単純なモデルを使用して、検索対象のパーツのリストからすべてのパーツが割り当てられている車を取得したいと考えています。

たとえば、PartIds の配列があるとします。

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 を使用してこの種のクエリを実行する適切な方法はありますか?

4

3 に答える 3

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

それが役立つことを願っています。

于 2012-04-20T01:41:17.183 に答える
0

この回答の修正版

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

私の知る限り、このhaving句はHQLでのみ使用できます。さらに列が必要な場合は、select/group by リストを変更できます。別の考えられる方法は、特定の各パーツ ID に内部結合する HQL クエリを生成しすぎることです。プロパティに一度しか参加できないため、ICritiriaでもそれは可能ではないと思います。

于 2012-08-07T20:14:16.363 に答える