2012-04-18 59 views
2

爲了更容易解釋我的問題,我將創建以下虛構示例,說明非常基本的多對多關係。 A 汽車可以有很多零件零件可以屬於很多汽車Nhibernate QueryOver - 執行所有的多對多關係

DB模式:

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?

回答

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 2012-08-08 08:20:48

+1

更高效地完美和方式。 X – ctrlplusb 2012-08-10 14:25:29

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 2012-08-07 13:21:11

0

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可用。如果您需要更多列,您可以修改選擇/分組依據列表。另一種可能的方法是生成一個內部連接到每個特定部件ID的HQL查詢。我認爲這與ICritiria無關,因爲它只允許你加入一次財產。