LINQ 按同一列连接多个表

LINQ join multiple tables by same column

我有一个 table 发短信,我需要加入另外两个 table 学生Staff 搜索有关这 2 table 的信息。

学生 字段:

  1. 编号
  2. 姓名
  3. ...以及一些学生特有的其他字段

员工 字段:

  1. 编号
  2. 姓名
  3. ...以及一些特定于员工的其他字段

发短信 字段:

  1. 编号
  2. PersonId // 包含学生 ID 或员工 ID
  3. PersonTypeId // 表示PersonId是student还是staff类型(student = 1, staff = 2)

现在我需要编写一个 linq 查询来搜索 table 按学生或教职员工姓名发送的短信,但我无法通过 linq 来实现此目的。

var query = (from t in texting
             join s in studentBo.GetListQuery()
             on t.PersonId equals s.Id
             join st in staffBo.GetListQuery()
             on t.PersonId equals st.Id
             where ...
             select t);

这将 table 连接在一起,但它不关心 PersonId 类型是什么,所以它们都是混合的。我如何指定以便它根据正确的 PersonTypeId 正确加入 PersonId?似乎没有其他东西可以附加在 on 子句或 where 子句上来实现这一点 = (.

您将不得不合并 Student 和 Staff 表,否则您的所有查询都会太复杂,因为您将不得不使用 Union

Person

Id
Name
PersonType

Texting

Id
PersonId

并查询

var query = (from t in texting
             join p in person
             on t.PersonId equals p.Id
                  where ...
             select t);

PS 如果您仍然想要使用 2 个表而不是一个表进行查询,则必须 post 真正的代码。

您需要将这些作为两个单独的查询来执行,投影到新类型,然后合并结果。乱七八糟,但方法如下。

首先让你的学生:

var textingStudents = (
    from s in students
    join t in texting on s.Id equals t.PersonId
    where t.PersonTypeId == 1
    select new { id = s.Id, personTypeId = 1, name = s.Name }).ToList();

现在以几乎完全相同的方式让您的员工:

var textingStaff = (
    from s in staff
    join t in texting on s.Id equals t.PersonId
    where t.PersonTypeId == 2
    select new { id = s.Id, personTypeId = 2, name = s.Name }).ToList();

现在您可以合并两者:

var allTextingPeople = textingStudents.Union(textingStaff);

如果您需要其他属性,则将 then 添加到 select 语句中声明的匿名类型 - 请记住,该类型需要在 textingStudents 和 [=15] 中具有相同的属性=] 结果。或者,定义一个 class 并在两个查询中执行 select new MyUnionClass { ... }

编辑 使用您概述的当前方法,您可能会陷入一个受伤的世界。如果您使用的是关系数据库(即 sql 服务器),您几乎可以肯定没有在 Texting table 上定义诸如外键之类的约束,这意味着您最终会遇到 ID 冲突和以后肯定会出现错误。最好的方法可能是用一个 table 来表示 StaffStudent(我们称它为 Person 并用一列定义人员的“类型”——该列本身将是外键 link 到另一个 table 与您的列表 PersonTypes

所以您有一个 name,并且您想要所有引用具有此名称的 StudentTextings,以及所有引用 Textings 成员的 Textings Staff 使用此名称。

我的建议是将学生短信与员工短信合并。你可以在一个大的 LINQ 语句中做到这一点,但是这会使它很难理解。所以我将分两步完成,然后在一个查询中连接它:

const int student = 1;
string name = "William Shakespeare";

var studentTextings = textings.Where(texting => texting.PersonTypeId == student)
    .Join(students.Where(student => student.Name == name),

    texting => texting.PersonId,    // from every Texting take the foreign key
    student => student.Id,          // from every Student take the primary key

    // parameter resultSelector:
    // from every texting with its matching student make one new:
    (texting, studentWithThisTexting) => new
    {
        // Select the Texting properties that you plan to use
        Id = texting.Id,
        ...
    }

换句话说:在所有短信中,只保留那些引用学生的短信,所以你知道外键指的是学生table中的主键。从所有学生中,只保留那些具有请求名称的学生。

加入所有剩余的短信和少数在主键和匹配外键上具有此名称的学生。

为员工做类似的事情:

const int staff = 2;
var staffTextings = textings.Where(texting => texting.PersonTypeId == staff)
    .Join(staffMembers.Where(staffMember => staffMember.Name == name),

    texting => texting.PersonId,    // from every Texting take the foreign key
    staffMember => staffMember.Id,  // from every Staff member take the primary key

    // parameter resultSelector:
    (texting, staffMembers) => new
    {
        // Select the Texting properties that you plan to use
        Id = texting.Id,
        ...
    }

现在你所要做的就是连接这两个。请注意:您只能连接相似的项目,因此两个连接中的 resultSelector 应该 select 完全相同类型的对象。

var textingsOfPersonsWithThisName = studentTextings.Concat(staffTextings);

还有改进的空间!

如果仔细观察,您会发现短信 table 将被扫描两次。之所以这样,是因为你的数据库没有规范化。

发给学生的短信会变成发给员工的短信吗?如果没有,我的建议是创建两个 table:StudentTextings 和 StaffTextings。除了查询会更快之外,因为您不必检查 PersonType,这还有一个好处,如果稍后您决定 StudentTexting 与 StaffTexting 不同,您可以更改 tables没有 运行 问题。

如果您真的认为有时您需要更改短信的类型,并且您不想通过创建新短信来做到这一点,您还应该有两个 tables:一个与StudentTextings,一个与 StaffTextings,两者 table 都与 Texting 具有一对一的关系。

所以 Students 与 StudentTextings 一对多,而 Students 与 Textings 一对一。 Staff 和 StaffTextings 类似。

所以学生 [4] 有 3 个 ID 为 [30]、[34]、[37] 的 StudentTexting。这些 StudentTextings 中的每一个都有一个值为 [4] 的外键 StudentId。每个StudentTexting都用外键引用自己的Texting:[30]指的是texting [101],所以它有外键101,等等

现在,如果发短信 [101] 必须成为员工 [7] 的短信,您将必须删除引用 [101] 的 StudentTexting 并创建一个引用员工 [7] 和发短信的新 StaffTexting [101]

顺便说一下,由于组合 [StudentId, TextingId] 是唯一的,table StudentTextings 可以使用这个组合作为主键。类似于 StaffTextings