Reputation: 31105
If I have a Rule which has many Criteria and a user selects multiple criteria, how do I write a linq query giving me all the rules where EVERY criteria within that rule exists with the user's selected criteria?
Here is how I'm doing it in SQL currently:
Tables
rule:
ruleId int
categoryId int
ruleCriteria:
ruleId int,
criteriaId int
@userCriteria:
criteriaId int
categoryId int
Query
SELECT
r.ruleId
FROM dbo.rule r
INNER JOIN dbo.ruleCriteria rc ON r.ruleId= rc.ruleId
LEFT OUTER JOIN @userCriteria uc
ON rc.criteriaId = uc.criteriaId
AND r.categoryId = uc.categoryId
GROUP BY r.ruleId
HAVING COUNT(rc.criteriaId) = COUNT(uc.criteriaId)
For the linq query, I'll have these objects (more denormalized, but I could put them in the same structure as the tables if that helps):
userCriteria:
criteriaId int
categoryId int
ruleCriteria:
ruleId int
categoryId int
criteriaId int
My goal is the same as the SQL query which is to get a distinct list of matching rules. How would you do it?
Upvotes: 3
Views: 552
Reputation: 411
This query generates better SQL than the other one, and it's more direct:
from rc in ruleCriterias
group rc by rc.ruleId into rules
where rules.All(rc => userCriterias.Any(uc =>
uc.criteriaId == rc.criteriaId && uc.categoryId == rc.categoryId))
select rules.Key
You should make sure all the columns are NOT NULL
, because if they aren't, this has to generate extra SQL to check to see if things are null.
Upvotes: 1
Reputation: 411
I played around with some test data in LINQPad and this is what I came up with.
from rc in ruleCriteria
group new { rc.criteriaId, rc.categoryId } by rc.ruleId into rules
where rules.Count() == userCriteria.Count(uc =>
rules.Contains(new { uc.criteriaId, uc.categoryId }))
select rules.Key
Here's the explanation, line-by-line:
ruleCriteria
{ criteriaId, categoryId }
by the ruleId
and save it in rules
userCriteria
is the same as the total number of rules, meaning the user has all the criteria.ruleId
.Upvotes: 1
Reputation: 7126
Unless I am misunderstanding (and therefore over simplyfying the problem) maybe the all .All() method is what you need.
var userCriteria = ; //TODO: Build user criteria
var result = context.Rules.Where(r => r.CategoryId == userCriteria.CategoryId
&& r.RuleCriteria.All(rc => rc.id == userCriteria.CriteriaId));
This will only return rulls where all the containing criteria match your criteria. I can't guarantee your sql though
Upvotes: 0