简体   繁体   English

如何在“WHERE”条件下使用List编写LINQ to Entities查询

[英]How to write a LINQ to Entities query with List in a “WHERE” condition

I would like to know to to write most efficient LINQ ( EDIT : to Entities) query with a list as a condition. 我想知道以列表作为条件编写最有效的LINQ( 编辑 :实体)查询。 Here is the thing. 这就是事情。

Lets say we have the following data structure: 可以说我们有以下数据结构:

 public class Recipe
 {
   public int Id;
   public string Name;
   public List<Ingredient> IngredientList;
 }

 public class Ingredient
 {
   public int Id;
   public string Name;
 }

Now, I would like to make a query which will search all the Recipes which have ALL given ingredients. 现在,我想做一个查询,搜索所有具有所有给定成分的食谱。

 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;
 }

Basically this is the problem how to determine whether a given set is a subset of another set. 基本上这是如何确定给定集合是否是另一集合的子集的问题。

I have tried with the following query (the main idea is usage of the Intersect operation): 我尝试过以下查询(主要思想是使用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();

But I get the following error: 但是我收到以下错误:

Unable to create a constant value of type 'Closure type'. 无法创建“闭包类型”类型的常量值。 Only primitive types ('such as Int32, String, and Guid') are supported in this context. 在此上下文中仅支持原始类型(例如Int32,String和Guid')。

well, if IngredientList really is a List<T> , you'll be able to do: 好吧,如果IngredientList真的是List<T> ,你将能够做到:

recipes = context.Recipes.Where(recipe => recipe.IngredientList.Exists(i => i.Id == ingredient.Id)).ToList();

but that means all the list needs to be populated. 但这意味着需要填充所有列表。 Since this looks like a LINQ to SQL query, I'm guessing IngredientList is just a connected table...? 由于这看起来像LINQ to SQL查询,我猜测IngredientList只是一个连接表......? In that case, you won't have the full list, but you'll still be able to do something similar: 在这种情况下,您将没有完整列表,但您仍然可以执行类似的操作:

recipes = context.Recipes.Where(recipe => recipe.IngredientList.Count(i => i.Id == ingredient.Id) > 0).ToList();

...and it should still just query the sql server once. ...它应该仍然只查询一次sql server。

EDIT 编辑

As was just pointed out in comments, this doesn't quite answer the question. 正如评论中刚才指出的那样,这并没有完全回答这个问题。 As for contains-all search, I don't think it can be done without looping through the input. 至于包含所有搜索,我认为没有循环输入就可以完成。 The good thing is that this can be done without enumerating the IEnumerable recipes , so the below code will still just hit the sql server once, with a single query: 好的一点是,这可以在不枚举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();

The query will not be executed until ToList() is hit. 在命中ToList()之前,不会执行查询。

Don't use a List<Ingredient> for the ingredients that you want to find; 不要使用List<Ingredient>作为您想要找到的成分; use a HashSet<Ingredient> and the IsProperSubsetOf method, which accepts a collection as its argument: 使用HashSet<Ingredient>IsProperSubsetOf方法,该方法接受一个集合作为其参数:

.Where(x => ingredients.IsProperSubsetOf(x.IngredientList))

In addition to being an O(n+m) operation, this has the added benefit of being code that tells you what it's doing when you look at it. 除了作为O(n + m)操作之外,还有一个额外的好处,就是可以在看到它时告诉你它正在做什么。

Edit 编辑

In case the above is not clear: 如果上述情况不明确:

public List<Recipe> GetRecipesWhichHaveGivenIngredients(HashSet<Ingredient> ingredients)
{
   using (DataContext context = new DataContext())
   {
       return context.Recipes
           .Where(x => ingredients.IsProperSubsetOf(x.IngredientList)  
           .ToList();
   }
 }

don't know if this will work in Linq2SQL, but in Linq2Object, this works: 不知道这是否适用于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;
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM