Linq大师 - 过滤相关实体

时间:2010-04-27 05:33:30

标签: .net linq subquery entity-framework-4

我的表格结构如下:

Person 1-M PesonAddress
Person 1-M PesonPhone
Person 1-M PesonEmail
Person 1-M Contract 
Contract M-M Program
Contract M-1 Organization

在此查询结束时,我需要一个填充的对象图,其中每个人都有:

  • PesonAddress的
  • PesonPhone的
  • PesonEmail的
  • PesonPhone的
  • 合同 - 这有各自的
    • 计划的

现在我有了以下查询,我认为它工作得很好,但它有一些问题:

from people in ctx.People.Include("PersonAddress")
                        .Include("PersonLandline")
                        .Include("PersonMobile")
                        .Include("PersonEmail")
                        .Include("Contract")
                        .Include("Contract.Program")
where people.Contract.Any(
    contract => (param.OrganizationId == contract.OrganizationId)
        && contract.Program.Any(
            contractProgram => (param.ProgramId == contractProgram.ProgramId)))
select people;

问题在于它会根据标准过滤此人,但不会过滤合同或合同的程序。它带回了所有合同,每个人不仅具有组织ID为x的组合,而且每个合同的程序都分别相同。

我想要的只是那些至少有一份合同,其中包含一个OrgId为x的合同,并且该合同中的合同具有一个具有y的Id的程序......并且返回的对象图只包含合同匹配和该合同中匹配的程序。

我有点理解为什么它不起作用,但我不知道如何更改它以便它正在工作......

这是我迄今为止的尝试:

from people in ctx.People.Include("PersonAddress")
                        .Include("PersonLandline")
                        .Include("PersonMobile")
                        .Include("PersonEmail")
                        .Include("Contract")
                        .Include("Contract.Program")
let currentContracts = from contract in people.Contract
                where (param.OrganizationId == contract.OrganizationId)
                select contract 
let currentContractPrograms = from contractProgram in currentContracts 
                    let temp = from x in contractProgram.Program
                        where (param.ProgramId == contractProgram.ProgramId)
                        select x
                    where temp.Any()
                    select temp
where currentContracts.Any() && currentContractPrograms.Any()
select new Person { PersonId = people.PersonId, FirstName = people.FirstName, ..., ...., 
                    MiddleName = people.MiddleName, Surname = people.Surname, ..., ...., 
                    Gender = people.Gender, DateOfBirth = people.DateOfBirth, ..., ...., 
                    Contract = currentContracts, ... };  //This doesn't work

但这有几个问题(Person类型是EF对象):

  • 我自己做了映射,在这种情况下,有很多要映射
  • 当我尝试将列表映射到某个属性(即奖学金= currentScholarships)时,它说我不能,因为IEnumerable正在尝试强制转换为EntityCollection
  • 包含不起作用

因此,我如何让它工作。请记住,我正在尝试将此作为编译查询,所以我认为这意味着匿名类型已经出局。

3 个答案:

答案 0 :(得分:2)

只是不要使用Include,手动过滤。您可以先过滤与所需ProgramId和OrganizationId相关联的合同。之后,您可以选择与所选合同相关的人员。 A附上了示例代码。您需要对其进行修改才能正确使用M-M关系。但无论如何逻辑应该是正确的。

public class PersonDetails
{
    public Person person;
    public List<Contract> contracts;
}

var selected_program = (from pr in ctx.Programs where pr.Id == param.ProgramId select pr).Single();

//select contracts by OrganizationId and ProgramId
var selected_contracts = from c in ctx.Contracts
                where c.OrganizationId == param.OrganizationId
                from p in ctx.Programs
                where p.Id == param.ProgramId
                where p.ContractId == c.Id
                select c;

//select persons and contracts
var people =
    from p in ctx.People
    select new PersonDetails()
    {
        person = p,
        contracts = (from c in selected_contracts
                     where c.PersonId == p.Id
                     select c).ToList()
    };

//select people associated with selected contracts
var selected_people = from p in people where p.contracts.Count > 0 select p;

答案 1 :(得分:1)

包含在实体框架中将始终带回关系中的所有内容,无法执行部分包含或Linq to SQL具有的等效的AssociateWith。

相反,如果您只想恢复一些合同,则需要将其拆分为两个查询,并利用实体框架执行的自动连接。

一旦两个查询都被执行,您的Person对象将仅包含Contracts集合中Contracts查询带回的合同。

答案 2 :(得分:0)

如同Mant101所说,你无法将Linq中的.Include部分过滤为实体。查看Person对象,作为存储在数据库中的有关此人的所有信息的表示,包括所有contracts。所有的装备都是分开做的。

这些问题似乎在这里经常出现。至少我觉得我见过一些,但找不到很多。以下是处理此主题的另一个问题:conditional include in linq to entities?

那里还有一个可行的anser:只需以新的匿名类型返回你的(整个)人物对象,以及任何其他(过滤的)信息。