C# 如何为SQL交叉联接编写Linq表达式?

C# 如何为SQL交叉联接编写Linq表达式?,c#,linq,linq-to-entities,entity-framework-5,C#,Linq,Linq To Entities,Entity Framework 5,我正在创建一个报告,根据用户定义的筛选条件列出数据库中的人员。例如,我可以按姓名、年龄等进行筛选 var people = db.People.AsQueryable(); if (filterByName) people = people.Where(p => p.LastName.Contains(nameFilter)); if (filterByAge) people = people.Where(p => p.Age == age); 现在,筛选标准之一是显示未接

我正在创建一个报告,根据用户定义的筛选条件列出数据库中的人员。例如,我可以按姓名、年龄等进行筛选

var people = db.People.AsQueryable();
if (filterByName)
  people = people.Where(p => p.LastName.Contains(nameFilter));
if (filterByAge)
  people = people.Where(p => p.Age == age);
现在,筛选标准之一是显示未接受所需免疫接种的人。我有
免疫
个人免疫
(在
个人ID、免疫ID上有唯一索引)。如果有人缺少任何
个人免疫记录
或如果他们接受的剂量符合要求,则应包括在内,否则不包括在内

如果我正在编写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;
我遇到的第一个问题是如何将免疫接种应用到表达式中。然后,一旦我有了它,我怀疑找到不合规的人可能会更简单,但如果你能在你的答案中包括这一点,我将非常感谢

编辑:我被要求解释为什么我如此热衷于使用
表达式获得解决方案。原因是我已经构建了一个完整的通用框架,用于在几个不同的上下文中编写复杂的、用户定义的查询。为了让您了解引擎内部的内容,下面是我的基类内部的一个片段:

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) { 
    ... 
  }
}
公共抽象类QueryBuilder,其中T:EntityObject{
公共静态IQueryable筛选器查询(IQueryable查询、IEnumerable筛选器、bool anyConditionQuery){
...
}
受保护的表达式GetPredicate(表达式表达式,IQueryCondition){
... 
}
}

然后我有一个
PersonQueryBuilder:QueryBuilder
,我想在其中创建一个过滤器,显示不符合免疫要求的人。我想您会同意,查询语法无法解决这个问题。

我会将其作为一个多部分连接:

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 };
var非计划化=
以个人身份从p开始
从免疫接种开始
设pi=personimmunications.Where(x=>
x、 免疫ID==i.ID&&x.PersonID==p.ID)
哪里pi.Any()| | pi.Sum(x=>x.doges)
编辑:为了使其符合
表达式的约束条件,我想您可以将其改写为:

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();
表达式不符合免疫=
个人=>(
从免疫接种开始
设pi=personimmunications.Where(x=>
x、 免疫ID==i.ID&&x.PersonID==person.ID)
其中!pi.Any()| | pi.Sum(x=>x.doges)
您应该能够这样写:

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;
}
var people=db.people.AsQueryable();
if(过滤器B通信)
{
people=来自people中的p
从数据库中的i开始
从db.PersonImmunization.Where(x=>
x、 PersonID==p.ID&&x.ImmunizationID==i.ID).DefaultIfEmpty()
式中,pi.ID==null | | pi.剂量
-1绝对不是。这将丢失交叉连接和左连接。另外,我需要它作为一个
表达式
;查询语法不起作用。@Shaul表达式在逻辑上是相同的,您不需要交叉连接和左连接,db将选择最佳执行计划。@wendazhou在这里重新阅读答案,您将看到这只会选择现有的
PersonImmunization
记录(失去左连接),并且不能保证所有的免疫记录都被包括在内。@Shaul:我现在明白了区别,你的逻辑是把没有PI记录作为缺席的证明。我的逻辑现在应该是你的了。
pi
在我的代码中是可枚举的。。。
.Where
子句
PersonImmunications
(这只是PersonImmunications表)返回一个
IEnumerable
+1这是正确的Linq语法,但我特别希望它作为
表达式
。为了避免混淆,这个问题从我的实际代码中被简化了;请接受,因为查询语法不足以满足我的目的。@Nilesh-为了避免混淆,这个问题在我的实际代码中被简化了;请接受,因为我需要一个
表达式
,而查询语法不足以满足我的目的。请编辑您的问题,为禁止有效的工作解决方案添加理由。为什么您似乎认为您只能让查询使用。Where(…)?@AndrewCoonce根据您的request@Tory,不知道你的问题是什么意思,但也许我最近的编辑会回答这个问题?
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;
}