我想知道用列表作为条件来编写最有效的LINQ(编辑:到实体)查询。事情就是这样。
假设我们有以下数据结构:
public class Recipe
{
public int Id;
public string Name;
public List<Ingredient> IngredientList;
}
public class Ingredient
{
public int Id;
public string Name;
}
现在,我想进行一个查询,搜索所有具有所有给定成分的食谱。
public List<Recipe> GetRecipesWhichHaveGivenIngredients(List<Ingredients> ingredients)
{
List<Recipe> recipes;
using (DataContext context = new DataContext())
{
//efficient LINQ query goes here
recipes = context.Recipes.Where(recipe => /*medaCode recipe.IngredientList.Contains(ingredients) */).ToList();
}
return recipes;
}
基本上这是如何确定给定集合是否是另一集合的子集的问题。
我尝试过以下查询(主要思想是使用Intersect操作):
List<Recipe> recipes = dataContext.Recipes.Include("Ingrediens").Where(rec => rec.IngredientList.Select(ingr => ingr.Id).Intersect(ingredients.Select(sy => sy.Id)).Count() == ingredients.Count).ToList();
但是我收到以下错误:
无法创建常量值 键入'关闭类型'。只有原始 类型('如Int32,String和 在此背景下支持Guid'。
答案 0 :(得分:2)
好吧,如果IngredientList
真的是List<T>
,你就能做到:
recipes = context.Recipes.Where(recipe => recipe.IngredientList.Exists(i => i.Id == ingredient.Id)).ToList();
但这意味着需要填充所有列表。由于这看起来像是LINQ to SQL查询,我猜测IngredientList
只是一个连接表......?在这种情况下,您将没有完整列表,但您仍然可以执行类似的操作:
recipes = context.Recipes.Where(recipe => recipe.IngredientList.Count(i => i.Id == ingredient.Id) > 0).ToList();
...它应该只查询一次sql server。
修改强>
正如刚才在评论中指出的那样,这并没有完全回答这个问题。对于 contains-all 搜索,我认为如果不循环输入就不能完成。好的一点是,这可以在不枚举IEnumerable recipes
的情况下完成,所以下面的代码仍然只需用一个查询就可以点击一次sql server:
var recipes = context.Recipes.AsEnumerable<Recipe>();
ingredients.ForEach(i =>
var recipes = recipes.Where(r =>
r.IngredientList.Count(ii => ii.Id == i.Id) > 0
);
);
return recipes.ToList();
在ToList()
被击中之前,不会执行查询。
答案 1 :(得分:2)
不要使用List<Ingredient>
作为您想要找到的成分;使用HashSet<Ingredient>
和IsProperSubsetOf
方法,它接受一个集合作为其参数:
.Where(x => ingredients.IsProperSubsetOf(x.IngredientList))
除了作为O(n + m)操作之外,还有一个额外的好处,就是可以在看到它时告诉你它正在做什么。
修改强>
如果上述情况不明确:
public List<Recipe> GetRecipesWhichHaveGivenIngredients(HashSet<Ingredient> ingredients)
{
using (DataContext context = new DataContext())
{
return context.Recipes
.Where(x => ingredients.IsProperSubsetOf(x.IngredientList)
.ToList();
}
}
答案 2 :(得分:0)
不知道这是否适用于Linq2SQL,但在Linq2Object中,这可行:
public static class Util
{
public static List<Recipe> GetRecipesWhichHaveGivenIngredients(this List<Recipe> recipies, List<Ingredient> ingredients)
{
int icount=ingredients.Count;
var res = recipies.Where(r => r.IngredientList.Where(i => ingredients.Contains(i)).Count() == icount).ToList();
return res;
}
}