【问题标题】:Linq guru - filtering related entitiesLinq guru - 过滤相关实体
【发布时间】:2010-05-06 05:30:13
【问题描述】:

我的表结构如下:

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;

问题在于它将人员过滤到标准而不是合同或合同的程序。它带回了所有合同,每个人不仅拥有 OrganizationId 为 x 的合同,而且每个合同的程序也同样如此。

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

我有点明白为什么它不起作用,但我不知道如何更改它以使其正常工作......

这是我迄今为止的尝试:

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 对象):

  • 我只能自己做映射,在这种情况下,要映射的东西很多
  • 当我尝试将列表映射到属性(即 Scholarship = currentScholarships)时,它说我不能,因为 IEnumerable 正试图被转换为 EntityCollection
  • 包含不起作用

因此,我该如何让它发挥作用。请记住,我正在尝试将其作为编译查询来执行,因此我认为这意味着匿名类型已被淘汰。

【问题讨论】:

  • 您确定 Contract-Program 是 M-M 吗?那么有一个交叉引用表吗?
  • 是的...在数据库中,在 Contract 和 Program 之间有一个表,它有助于 M-M,但 Entity 框架已经抽象出那个“实体”...

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


【解决方案1】:

只是不要使用包含,手动过滤。您可以先筛选与所需 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;

【讨论】:

  • @Fedor:我尝试在我的情况下实施这种方法,但出现以下异常...... LINQ to Entities 无法识别方法'System.Collections.Generic.List1[Contract](System.Collections.Generic.IEnumerable1 [Contract])' 方法,并且该方法不能翻译成 store 表达式。
  • 所以这需要修复。不幸的是,我无法构建您的代码来说明问题所在。您可以在 PersonDetails 中使用 IEnumerable 而不是 List。甚至使用匿名类代替 PersonDetails。
  • 虽然这不是确切的答案,但我确实引导我找到它...感谢您的帮助。
【解决方案2】:

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

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

一旦两个查询都已执行,您的 Person 对象将仅包含由其 Contracts 集合中的 Contracts 查询返回的 Contracts。

【讨论】:

  • 很抱歉,但我并没有发现这一切有什么帮助...您能否提供一个您所谈论内容的示例...
  • 这里是一个带有示例的文章的链接,实际上是为了回答 Stack Overflow 问题而写的:blogs.msdn.com/alexj/archive/2009/10/13/…
【解决方案3】:

就像 Mant101 所说,您不能将 Linq 中的 .Include 部分过滤为实体。查看Person 对象作为存储在数据库中关于此人的所有信息的表示,包括所有contracts。所有的装配都是单独完成的。

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

那里还有一个可行的分析器:只需以新的匿名类型返回您的(整个)人对象,以及关于它的任何其他(过滤的)信息。

【讨论】:

  • 这种方法的问题是,正如我上面提到的,我只能自己完成所有映射......在这种情况下,我必须在 select 语句中进行大量映射。此外,由于查询的编译性质,无法使用匿名类型进行某些映射...最后在链接中被接受为答案的查询中,它仅有效地执行左连接而不是内连接。 ..我只需要那些零件符合标准的经销商,然后只有那些也符合标准的零件……
  • 编写一个查询,只带回您想要的零件,并将结果拉入内存 (ToList) 编写一个查询,带回您想要的经销商,并将结果放入内存。编写一个查询,返回您想要的 Person 对象。当您遍历这个时,每个人将只有在第二个查询中带回的经销商,每个经销商将只有第一个查询带回的零件。不需要任何匿名类型或映射,框架会自动连接关系。您还可以预编译所有三个查询。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-04-14
  • 2021-12-14
  • 1970-01-01
  • 2013-01-03
  • 2017-04-11
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多