C# 如何在同时按父实体字段和子实体字段进行搜索时编写有效的LINQ查询

C# 如何在同时按父实体字段和子实体字段进行搜索时编写有效的LINQ查询,c#,linq,nhibernate,C#,Linq,Nhibernate,我在理解如何最好地用C编写linq查询以解决以下问题时遇到问题: 我有两个模型,让我们称之为项目和人。Person可以有多个项,因此每个项对象中都有对Person对象的引用。在我的查询中,我想找到所有符合特定条件的人,例如,他们的性别是男性,但他们也必须有符合其他条件的项目,例如,他们有名为Car的项目 我前面的人通过选择项目并按人员分组来解决这个问题。因此,查询类似于: var result=Items.FilterByx=>x.Person.Gender==Male&&x.Name==Car

我在理解如何最好地用C编写linq查询以解决以下问题时遇到问题:

我有两个模型,让我们称之为项目和人。Person可以有多个项,因此每个项对象中都有对Person对象的引用。在我的查询中,我想找到所有符合特定条件的人,例如,他们的性别是男性,但他们也必须有符合其他条件的项目,例如,他们有名为Car的项目

我前面的人通过选择项目并按人员分组来解决这个问题。因此,查询类似于:

var result=Items.FilterByx=>x.Person.Gender==Male&&x.Name==Car.GroupByx=>x.Person.ToList.Selectg=>g.Key

我在想,ToList应该为糟糕的性能负责,但是如果没有它,我无法让查询工作。然而,也有一些事情让我摸不着头脑,告诉我这不是可能的最佳方法,因为过滤通常在90%的情况下只针对个人进行。因此,我认为更好的方法是只选择符合标准的人员,然后根据需要检查他们的物品。有可能写一个linq来解决这个问题吗

一种解决方案是首先检查是否需要对项目进行过滤,然后执行适当的查询,例如:

if(shouldFilterItems)
{
    result = Items.FilterBy(x => x.Person.Gender == "Male" && x.Name == "Car").GroupBy(x => x.Person).ToList().Select(g => g.Key);
}
else
{
    result = Persons.FilterBy(x => x.Gender == "Male");
}

但是,如果可能的话,我希望避免这种解决方案,并将所有内容都放在一个查询中。是否有可能编写这样的查询,并且它的性能是否比初始的group by更好?

与LINQ to objects一样,执行顺序会对这样的语句产生很大影响,因为它无法自动优化,这与EF和SQL执行计划不同

对您来说,一种可能的优化方法是对属性而不是对象运行GROUPBY语句,另一种优化方法是只从组中取出第一个人来完成列表

var result = items.Where(x=> x.Name=="Car" && x.person.Gender=="Male").GroupBy(x => x.person.UID).Select(x => x.First().Key).ToList();
其中一个人的UID是该人的唯一标识符

我有一个简单的小提琴设置,你可以看到完全不准确的执行时间。它们完全取决于您的数据和对象


我想推荐的另一种优化方法是将所有这些数据管理移到一个实际的数据库中,并通过EF或一个过程访问它。

人和项目之间有一个真正的一对多关系:每个人都有零个或多个项目,每个项目都只属于一个人

如果您遵循一对多关系的实体框架指导原则,您将获得类似以下内容:

class Person
{
    public int Id {get; set;}

    // every person has zero or more items:
    public virtual ICollection<Item> Items {get; set;}

    public Gender Gender {get; set;}
    ... // other properties
}

class Item
{
    public int Id {get; set;}

    // every Item belongs to exactly one Person using foreign key
    public int PersodId {get; set;}
    public virtual Person Person {get; set;}

    public string Name {get; set;}
    ... // other properties
}
var malesWithTheirCars = myDbContext.Persons
    .Where(person = person.Gender == Gender.Male)
    .Select(person => new
    {
        Name = person.Name,
        ...
        Cars = person.Items.Where(item.Name == "Car").ToList(),
    });
换句话说:从收集的人中,只取那些性别与性别相等的人。男性,以及至少有一个项目的名称与汽车相等的人。从这些选定的人员中,仅将选定的属性转移到本地流程

如果你想让所有的男性都拥有自己的汽车,我会选择以下选项:

class Person
{
    public int Id {get; set;}

    // every person has zero or more items:
    public virtual ICollection<Item> Items {get; set;}

    public Gender Gender {get; set;}
    ... // other properties
}

class Item
{
    public int Id {get; set;}

    // every Item belongs to exactly one Person using foreign key
    public int PersodId {get; set;}
    public virtual Person Person {get; set;}

    public string Name {get; set;}
    ... // other properties
}
var malesWithTheirCars = myDbContext.Persons
    .Where(person = person.Gender == Gender.Male)
    .Select(person => new
    {
        Name = person.Name,
        ...
        Cars = person.Items.Where(item.Name == "Car").ToList(),
    });
注意,这也会选择没有汽车的男性。如果要忽略这些,请添加:

    .Where(person => person.Cars.Any());

你好,谢谢你的意见。我尝试以以下方式运行我的查询:items.Wherex=>x.Name==Car&&x.person.Gender==Male.GroupByx=>x.person.Id.Selectx=>x.First.person,但我一直得到以下异常InnerException={Column'person.Id'在选择列表中无效,因为它既不包含在聚合函数中,也不包含在GROUP BY子句中。}。但是,这似乎是NHibernate异常,而不是与LINQ相关的异常。此方法的一个缺点是,您无法在GroupBy子句之后实际获取父对象,您需要扩展GroupBy以包含所有需要使用进一步SQL的字段,或者使用Id列表通过Intersect从所有人员列表中重新选择人员。请记住性能缺陷,有时按整个对象分组仍然会更快。