如何为SQL交叉连接编写Linq表达式?

时间:2013-08-05 15:36:35

标签: 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);

现在,过滤标准之一是向没有进行必要免疫接种的人员展示。我有ImmunizationPersonImmunization的表格(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>,其中我想创建一个过滤器,显示不符合其免疫要求的人。我想你会同意查询语法不会削减它。

2 个答案:

答案 0 :(得分:2)

我将其视为多部分联接:

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();

答案 1 :(得分:1)

你应该能够这样写:

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