【问题标题】:how to flatten many to many relationship如何扁平化多对多关系
【发布时间】:2014-12-29 17:06:50
【问题描述】:

我有这个表和关系。

编辑

public class Person
{
  public Person()
  {
    Books = new HashSet<Book>();
  }

  public int PersonId { get; set; }
  public string fname { get; set; }
  public string lname { get; set; }
  public string Company { get; set; }

  public virtual ICollection<Book> Books { get; set; }
}

public class Book
{
  public Book()
  {
    Persons = new HashSet<Person>();
  }

  public int BookId { get; set; }
  public string Title { get; set; }

  public virtual ICollection<Person> Persons { get; set; }
}

类数据

{
  lname = "Bar", fname = "Foo", Company = "FooBar", Books = { Title = "Book1" }, { Title = "Book2" }
}

人员表

+-------+-------+---------+----------+
| fname | lname | Company | PersonId |
+-------+-------+---------+----------+
| Foo   | Bar   | FooBar  | 1        |
| Bob   | Joe   | Forest  | 2        |
| Temp  | Yoo   | Temp    | 3        |
+-------+-------+---------+----------+

书桌

+--------+-------+
| BookId | Title |
+--------+-------+
| 1      | Book1 |
| 2      | Book2 |
| 3      | Book3 |
+--------+-------+

PersonBook 表

+----------+--------+
| PersonId | BookId |
+----------+--------+
| 1        | 2      |
| 1        | 1      |
| 2        | 1      |
+----------+--------+

结果

+----------+---------+------+
| Name     | Company | Book |
+----------+--------+-------+
| Yoo, Temp| Temp   |       |
| Joe, Bob | Forest | Book1 |
| Bar, Foo | FooBar | Book1 |
| Bar, Foo | FooBar | Book2 |
+----------+--------+-------+

现在,Person 和 Book 基本上是多对多的关系。我已经有一个 Sql 查询,但如何在 Linq 查询版本中执行此操作?

嗯,是这样的:

select 
    (isnull(c.lname, '') + ', ' + isnull(c.fname, '')) as Name
    ,isnull(c.Company , '') as Company
    ,isnull(f.Book, '') as Book
from Person c
left join PersonBook cf
on c.PersonId = cf.PersonId
left join Book f
on f.BookId = cf.BookId
order by f.Title, c.lname

任何帮助将不胜感激!谢谢!

【问题讨论】:

  • 你也需要展示你的课程。
  • 您有机会查看我的答案吗?

标签: c# linq linq-to-entities


【解决方案1】:

LINQ 中的左连接有点棘手,它需要join intoDefaultIfEmpty

from p in db.People
join bj in db.Books on p.PersonId equals bj.PersonID into j
from b in j.DefaultIfEmpty()
select new { p.fName, p.lName, p.Company, b == null ? null : b.Title }

【讨论】:

  • 其他版本的 linq 查询怎么样?具有“延迟效应”查询的那个?
  • 延迟效果?如果这就是您要问的,这将推迟执行。
  • 我明白了。我说的是类似这样的查询,People.Select(p =&gt; new .....) 类似的东西。
  • 只是语法不同。我的答案中提供的一种称为基于语法的查询,并由编译器转换为方法调用(包括您提到的Select)。
  • 感谢您的解释。 PersonBook去哪儿了?你是如何选择PersonBook 表的?
【解决方案2】:

查看您的类,您的模型中不应该有 PersonBook 类。使用 EF,您可以在没有联结类的情况下对多对多关联进行建模。 EF 会透明地查询数据库中的联结表。

所以你的查询应该是这样的

var query = from p in db.People
            from b in p.Books.DefaultIfEmpty() // For a left outer join
            select new { p.fName, p.lName, p.Company, b.Title };
var result = query.asEnumerable()
                  .Select(x => new 
                    {
                        Name = string.Format("{0}, {1}", x.lName, x.fName),
                        p.Company,
                        Book = b.Title
                    }

如果您监控生成的 SQL,您会看到 EF 加入了PersonBook 表。

【讨论】:

  • 嗨。你是对的,没有PersonBook 类。我只有两个实体。我展示它以供参考,但准确地说是一张表格。谢谢你。我试过你的查询,它似乎没有做left join 操作。
  • 对于左连接,您可以使用p.Books.DefaultIfEmpty()
  • 谢谢伙计。它解决了我的问题。出于好奇,查看查询,您怎么知道它直接关联键?您没有指定任何键。
  • 这就是 EF 从您的类模型中推断出来的。按照惯例,它将PersonId 作为Person 等的主键。
【解决方案3】:

这就是我所拥有的:-

var result = from x in (from p in persons
             join pb in personBooks
             on p.PersonID equals pb.PersonID into g
             from result1 in g.DefaultIfEmpty()
             select new
             {
                 Name = p.lName + "," + p.fName,
                 Company = p.Company,
                 BookID = result1 == null ? 0 : result1.BookID
              })
              join y in books 
              on x.BookID equals y.BookID into g2
              from result2 in g2.DefaultIfEmpty()
              select new 
              {
                  Name = x.Name,
                  Company = x.Company,
                  Book = result2 == null ? String.Empty : result2.Title
              };

我使用了一些乱七八糟的范围变量名称,请在您的实际查询中更改它们:)

得到以下结果:-

【讨论】:

  • 嗨。谢谢你。但是,personBooks 似乎没有在智能感知中显示。我错过了什么吗?
  • @BoyPasmo - PersonBooks 是一个单独的类,对吧?我创建了一个单独的课程并进行了测试,与您尝试的完全相同吗?
  • 实际上,我使用的是Code First,它生成为Table,因为它是Join Table 我不需要为PersonBook 创建一个类,尽管它创建了一个表@987654329 @。我只有两个班,@​​987654330@ 和 Book
  • 哦,实际上我考虑了问题中的表格及其各自的数据。如果尚未解决,您可以提供您在PersonBook 实体中获得的数据,我会试一试:)
【解决方案4】:

使用 Linq 方法,您可以这样使用 SelectMany:

var PersonBook = Persons
            .SelectMany(p => p.Books,
                (p, b) =>
                    new
                    {
                        Name = p.fname + ", " + p.lname,
                        Company = p.Company,
                        BookId = b == null ? -1 : b.BookId,
                    })
              .Join(Books, p => p.BookId, b => b.BookId,
                     (p, b) =>
                         new
                         { 
                             Name = p.Name,
                             Company = p.Company,
                             Book = p.BookId == -1 ? "" : b.Title
                          });

【讨论】:

  • 嗨。谢谢!但我认为它没有使用left join。我还想列出没有BookPersons
猜你喜欢
  • 1970-01-01
  • 2015-12-04
  • 2010-09-22
  • 1970-01-01
  • 2016-11-24
  • 2021-07-19
  • 2021-08-22
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多