2012-04-18 7 views
2

私の問題をより簡単に説明するために、非常に基本的な多対多関係を示す次の架空の例を作成します。 は多くのの部分の部分は多くのの車に属することができます。Nhibernate QueryOver - 多対多関係のすべてを実行する

DBスキーマ:私は、リストからそれらに割り当てられたすべての部分を持っているすべての車を取得したいと思い、この非常に単純なモデルを使用して

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

CAR_TABLE 
--------- 
CarId 
ModelName 

CAR_PARTS_TABLE 
--------------- 
CarId 
PartId 

PARTS_TABLE 
----------- 
PartId 
PartName 

クラス私が探している部品の

だから私は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配列から指定されたすべてのPartsを持つCars。

次のクエリがあります。これは、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を使用してこの種のクエリを実行する方法はありますか?

答えて

3

これはまさにあなたが望むべきである...

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

部品のコンテキストから開始するように、クエリー・パスを非常に巧妙に逆転させます。 :)私はこれを試し、あなたに知らせるでしょう。ありがとう。 – ctrlplusb

+1

より効率的に、完璧に動作します。バツ – ctrlplusb

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

希望します。

+0

申し訳ありませんが、私はこれを再検討しており、これが私の問題を解決していません。私は提供された配列のすべての部品を持つすべての車を必要としますが、配列に指定された部品はありません。お詫び! – ctrlplusb

0

having句私の知る限り、この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(); 

の修正版は、HQLでのみ使用可能です。列数を増やしたい場合は、選択/グループ別リストを変更できます。もう一つの可能​​な方法は、インナーが各特定のパートIDに結合するHQLクエリーを生成することです。私はICritiriaでそれが可能であるとは思っていません。私はそれを必要としてきたよう

関連する問題