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

时间:2014-01-23 03:07:32

标签: c# sql linq nhibernate fluent-nhibernate

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

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

SQL看起来像这样:

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

但是,我很难弄清楚如何将其表达为LINQ查询,或使用.QueryOver<T>方法。我不想在SQL中使用硬编码,因为这需要与数据库无关,我希望配置的NHibernate方言能够生成正确的代码。

有什么想法吗?

3 个答案:

答案 0 :(得分:2)

NHibernate支持这个名为

的SQL语句

语法如下:

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

注意:让我们在这里检查更深入的子查询用法Query on HasMany reference

答案 1 :(得分:1)

更多细节:

Radim的答案结果是表达子查询的最佳方式,但是有一些 gotchas 花了我一段时间才弄明白。因此,我也会发布一个答案来填写详细信息。

首先,该行:

.Where(Restrictions.In("ID", new[] { 5, 10, 15 }))

如果ID引用实体本身,实际是否有效。换句话说:

.Where(Restrictions.In("Ingredient", arrayOfIds))

由于 Ingredient 字段映射到Ingredients对象,因此会引发非常混乱的空引用异常。使用"IngredientId"也无效。在这种情况下,你必须使用它:

.Where(Restrictions.In("Ingredient", arrayOfIds
   .Select(id => new Ingredients(id)).ToArray()))

将ID数组转换为Ingredients个对象的数组。事后,事情开始发挥作用。

我还发现了一个简单的性能改进,使查询显着更快,至少在PostgreSQL上。如果您更改子查询:

  

WHERE存在(SELECT RecipeIngredientId FROM recipeingredients WHERE   RecipeId = r.RecipeId和IngredientId in(:p0,:p1))

要:

  

WHERE存在(SELECT RecipeIngredientId FROM recipeingredients WHERE   RecipeId = r.RecipeId和IngredientId in(:p0,:p1)LIMIT 1)

只需检查嵌套查询中的单行。查询的速度大约是我的两倍。这很容易表达:

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

希望这有帮助!

答案 2 :(得分:0)

试试这个Linq查询:

        recipes.Where(r => r.RecipeIngredients.Any(i => new long[]{5, 10, 15}.Contains(i.Id)));