C# 是否可以使用NHibernate查询具有一个或多个可能子对象的所有对象?

C# 是否可以使用NHibernate查询具有一个或多个可能子对象的所有对象?,c#,sql,linq,nhibernate,fluent-nhibernate,C#,Sql,Linq,Nhibernate,Fluent Nhibernate,我有一个名为Recipes的表格,每行包含一个配方。我还有一张名为RecipeIngElements的表格,其中包含一种特定配方使用的成分。因此,每个配方行都有一个或多个子RecipeElements行 我要做的是创建一个查询,以查找包含所需配料列表中任何配料的所有配方。例如,向我展示所有使用面粉、鸡蛋或香蕉的食谱 SQL将如下所示: SELECT * FROM Recipes r WHERE EXISTS (select 1 from RecipeIngredients where Re

我有一个名为Recipes的表格,每行包含一个配方。我还有一张名为RecipeIngElements的表格,其中包含一种特定配方使用的成分。因此,每个配方行都有一个或多个子RecipeElements行

我要做的是创建一个查询,以查找包含所需配料列表中任何配料的所有配方。例如,向我展示所有使用面粉、鸡蛋或香蕉的食谱

SQL将如下所示:

SELECT * FROM Recipes r
   WHERE EXISTS (select 1 from RecipeIngredients where RecipeId = r.RecipeId and IngredientId = ANY (5, 10, 15) limit 1);
var session = ...// get a ISession 

Reciepe reciepe = null; // this will be a reference to parent

// the SELECT inside of EXISTS
var subquery = QueryOver.Of<ReciepeIngredient>()
    // The PARENT handling here
    // the filter, to find only related ingredients
    .Where(item => item.ReciepeId == reciepe.ID)
    .Where(Restrictions.In("ID", new[] { 5, 10, 15 }))
    // Select clause
    .Select(ing => ing.ID)

    ;
然而,我很难弄清楚如何将其表示为LINQ查询,或者使用.QueryOver方法。我不想在SQL中硬编码,因为这需要与数据库无关,并且我希望配置的NHibernate方言生成正确的代码


有什么想法吗?

NHibernate支持这种SQL语句,称为

, 语法如下所示:

SELECT * FROM Recipes r
   WHERE EXISTS (select 1 from RecipeIngredients where RecipeId = r.RecipeId and IngredientId = ANY (5, 10, 15) limit 1);
var session = ...// get a ISession 

Reciepe reciepe = null; // this will be a reference to parent

// the SELECT inside of EXISTS
var subquery = QueryOver.Of<ReciepeIngredient>()
    // The PARENT handling here
    // the filter, to find only related ingredients
    .Where(item => item.ReciepeId == reciepe.ID)
    .Where(Restrictions.In("ID", new[] { 5, 10, 15 }))
    // Select clause
    .Select(ing => ing.ID)

    ;
有了上面的子查询,我们可以这样使用它

// the '() => reciepe' setting is essential here, it represents parent in a subquery
var query = session.QueryOver<Reciepe>(() => reciepe);

query.WithSubquery
    // our EXISTS (...
    .WhereExists(subquery);

var list = query
    .List<Reciepe>();
注意:让我们在此检查更深层次的子查询用法

尝试以下Linq查询:

        recipes.Where(r => r.RecipeIngredients.Any(i => new long[]{5, 10, 15}.Contains(i.Id)));
还有一些细节:

事实证明,Radim的答案是表达子查询的最佳方式,但有几个问题需要我花一段时间才能解决。因此,我也会发布一个答案来填写细节

首先,线路:

.Where(Restrictions.In("ID", new[] { 5, 10, 15 }))
如果ID引用实体本身,则实际上不起作用。换言之:

.Where(Restrictions.In("Ingredient", arrayOfIds))
将引发非常混乱的空引用异常,因为配料字段映射到配料对象。使用IngreditID也不起作用。在这种情况下,您必须使用:

.Where(Restrictions.In("Ingredient", arrayOfIds
   .Select(id => new Ingredients(id)).ToArray()))
将ID数组强制转换为一组对象。在那之后,事情开始运作

我还发现了一个简单的性能改进,使查询运行速度明显加快,至少在PostgreSQL上是如此。如果将子查询更改为:

如果存在,请从其中的RecipeIndients中选择RecipeIndientId RecipeId=r。RecipeId和IngCreditId位于:p0,:p1

致:

如果存在,请从其中的RecipeIndients中选择RecipeIndientId RecipeId=r。RecipeId和IngCreditId位于:p0,p1限制1

它只需检查嵌套查询中的一行。对我来说,查询速度大约是原来的两倍。这很容易表达:

var subquery = QueryOver.Of<RecipeIngredients>()
   .Where(item => item.Recipe.RecipeId == recipe.RecipeId)
   .Where(Restrictions.In("Ingredient", allowedIngs))
   .Select(i => i.RecipeIngredientId).Take(1);

希望这有帮助

好极了!我明天会把这件事弄得一团糟,看看能不能让它工作。。谢谢伟大的NHibernate是很棒的工具,先生;。什么更重要?如果不是最好的方法,你已经选择了纠正。使用子查询,您将过滤根实体reciepe,而它仍然保持平坦,而不是成倍增加。很多人尝试使用抓取。。。在获取集合的情况下,以笛卡尔乘积结尾。这种方法子查询是开箱即用的,可以进行分页—Take,Skip。太好了,开始工作了!我在下面添加了我自己的答案以及我发现的更多细节。但是,您的方法非常有效,可以生成快速高效的SQL。谢谢!如果我运行这个,我会得到:NHibernate.dll中发生了类型为“System.exception”的未处理异常附加信息:无法识别的方法调用:System.Linq.Enumerable:Boolean Any[TSource]System.Collections.Generic.IEnumerable1[TSource],System.Func2[TSource,System.Boolean]我非常喜欢这样做。很高兴看到你是如何玩的钻石-NHibernate