如何为SQL交叉联接编写Linq表达式
本文关键字:Linq 表达式 SQL | 更新日期: 2023-09-27 18:26:26
我正在创建一个报告,根据用户定义的筛选条件列出数据库中的人员。例如,我可以根据姓名、年龄等进行过滤。
var people = db.People.AsQueryable();
if (filterByName)
people = people.Where(p => p.LastName.Contains(nameFilter));
if (filterByAge)
people = people.Where(p => p.Age == age);
现在,筛选标准之一是显示那些没有进行所需免疫接种的人。我有Immunization
和PersonImmunization
的表(在PersonID, ImmunizationID
上有一个唯一的索引)。如果有人丢失了任何PersonImmunization
记录,或者他们收到的剂量符合要求,则应将其包括在内,否则不包括在内。
如果我在写一个SQL查询,它将是:
select p.*
from Person p
cross join Immunization i
left join PersonImmunization pi
on pi.PersonID = p.ID and pi.ImmunizationID = i.ID
where pi.ID is null or pi.Doses < i.RequiredDoses;
现在,为了使where子句成为其中的一部分,我需要使用Expression
谓词来表达这一点:
if (filterByImmunizations) {
Expression<Func<Person, bool>> nonCompliantImmunization =
person => <now what?>;
people = people.Where(nonCompliantImmunization);
}
我遇到的第一个问题是如何在表达式中使用免疫。然后,一旦我有了它,我怀疑找到不合规的人可能会更简单,但如果你能把它包括在你的答案中,我会非常感激!
编辑:我被要求解释为什么我如此热衷于使用Expression<Func<Person, bool>>
获得解决方案。原因是我已经构建了一个完整的通用框架,用于在几个不同的上下文中编写复杂的用户定义查询。为了让您了解引擎内部的内容,下面是我的基类内部的一个片段
public abstract class QueryBuilder<T> where T : EntityObject {
public static IQueryable<T> FilterQuery(IQueryable<T> query, IEnumerable<QueryConditionLite> filters, bool anyConditionSufficient) {
...
}
protected Expression<Func<TBase, bool>> GetPredicate(Expression<Func<TBase, double>> expression, IQueryCondition condition) {
...
}
}
然后我有一个PersonQueryBuilder : QueryBuilder<Person>
,我想在其中创建一个过滤器,显示不符合免疫要求的人。我想你会同意查询语法不会减少它。
我会把它当作一个多部分连接:
var nonCompiantImmunization =
from p in Persons
from i in Immunizations
let pi = PersonImmunizations.Where(x =>
x.ImmunizationID == i.ID && x.PersonID == p.ID)
where !pi.Any() || pi.Sum(x => x.Doses) < i.RequiredDoses
select new { p, i };
编辑:为了使其符合Expression<Func<Person, bool>>
约束,我想您可以将其改写为:
Expression<Func<Person, bool>> nonCompliantImmunization =
person => (
from i in Immunizations
let pi = PersonImmunizations.Where(x =>
x.ImmunizationID == i.ID && x.PersonID == person.ID)
where !pi.Any() || pi.Sum(x => x.Doses) < i.RequiredDoses
select true
).Any();
您应该能够写以下内容:
var people = db.People.AsQueryable();
if(filterByImmunizations)
{
people = from p in people
from i in db.Immunization
from pi in db.PersonImmunization.Where(x =>
x.PersonID == p.ID && x.ImmunizationID == i.ID).DefaultIfEmpty()
where pi.ID == null || pi.Doses < i.RequiredDoses
select p;
}