为了更容易地解释我的问题,我将创建以下虚构的示例,说明一个非常基本的多对多关系。 汽车可以有多个零件,零件可以属于许多汽车。
DB SCHEMA:
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执行这种查询有没有一种不错的方法?
答案 0 :(得分: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>();
希望有所帮助。
答案 1 :(得分: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));
答案 2 :(得分: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();
AFAIK having
子句仅在HQL中可用。如果需要更多列,可以按列表修改选择/分组。另一种可能的方法是生成一个内部连接到每个特定部分id的HQL查询。我认为ICritiria不可能,因为它只允许你加入一次房产。