Consider the following classes:
public class Recipe
{
public string Id { get; set; }
public ICollection<RecipeFacet> RecipeFacets { get; set; }
}
public class RecipeFacet
{
public string Id { get; set; }
public Facet Facet { get; set; }
public string RecipeId { get; set; }
}
public class Facet
{
public string Name { get; set; }
}
I need to improve an existing query. I was thinking of using Linq's deferred execution. How would I write a Linq query that returns only Recipes
, that contains ALL Facets
I specify in a list of Tuples
?
This is the original code that loops through Recipes
and its Facets
. It works but it is slow if my intial results
query has lots of Recipes
.
IQueryable<Recipe> result; //assume we have data here
string query = "Cuisine:American+Recipe-Type:dinners";
IEnumerable<Tuple<string, string>> taxFacets = query
.Split(' ')
.Select(tf => tf.Split(':'))
.Select(tf => new Tuple<string, string>(tf[0], tf[1]))
.Distinct();
var recipeFacetCollection = result.Select(r => r.RecipeFacets).ToList();
var matchedRecipesIds = new List<string>();
var recIds = result.Select(r => r.Id).ToList();
// initially, include all recipes
matchedRecipesIds.AddRange(recIds);
// loop through each recipe's facet collection
foreach (var col in recipeFacetCollection)
{
// loop through the tax facets from the query
foreach (var tf in taxFacets)
{
var exists = col.Any(f => f.Facet.Name.Equals(tf.Item2, StringComparison.OrdinalIgnoreCase));
// remove any recipe that is missing a facet
if (!exists)
{
matchedRecipesIds.Remove(col.First().RecipeId);
}
}
}
result = result.Where(r => matchedRecipesIds.Contains(r.Id));
How can I have a nice Linq query with deferred execution?
UPDATE::
Turning my Tuple into a List allows me to do this. But this query doesn't return any of my records.
This is my criteria:
Recipes
, that have a collection of RecipeFacts
, that contains Facets
that have Name = "American" AND Name = "dinners".
var listFacets = new List<string>()
{
"American",
"dinners"
};
result = result
.Where(r => r.RecipeFacets
.All(f => !listFacets.Any(t => t.Equals(f.Facet.Name, StringComparison.OrdinalIgnoreCase))));
Your query logic selects all recipes whose facets don't exist in listFacets. @Hung's logic is closer but selects recipes that have all of their facets in listFacets
I think that you want to select all recipes that contain all listFacets.
Simplifying the example to use lists of strings:
var listFacets = new[] { "a", "d" };
var recipes = new[] { new[] { "a" },
new[] { "a", "d" },
new[] { "a", "d", "e" },
new[] { "x" }
};
// correct query, returns 2 results ad and ade
var result = recipes.Where(r => listFacets.All(f => r.Any(rf => rf == f)));
// original incorrect query, returns x
var result2 = recipes.Where(r => r.All(f => !listFacets.Any(rf => rf == f)));
I am not quite sure cause your code block is quite long but here is what I can come up with
result = result.Where(r => r.RecipeFacets.All(f => taxFacets.Any(t => t.Item1.Equals(f.Facet.Name))));
Let me know if it helps or not
从listFacets集合的检查中的子句中删除惊叹号。
result = result.Where(r => r.RecipeFacets.All(f => listFacets.Any(t => t.Equals(f.Facet.Name, StringComparison
I got this from @Rimp's help.
WHERE - filter
ALL - Require that All values from listFacets
ANY - is in ANY of the Facets
.
result = result
.Where(x => listFacets
.All(lf => x.RecipeFacets
.Any(f => f.Facet.Slug.Equals(lf, StringComparison.OrdinalIgnoreCase))));
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.