【问题标题】:Select all columns from main table and only 1 column from JOINed table从主表中选择所有列,从 JOINed 表中选择 1 列
【发布时间】:2022-09-28 03:12:16
【问题描述】:

我有两张桌子:

 Table1
 Id         ArticleName      ArticleTypeId
 1          Blah Blah        3
 2          Helo Blah        5

Table2
ArticleTypeId       TypeName
3                   Business
5                   Construction

我正在尝试在ArticleTypeId 上加入 TableA 和 TableB,并且基本上从 Table1 中返回所有内容,从 Table2 中返回 TypeName

这是我正在尝试做的事情,但我不确定要在语句中编辑 SELECT 以包含 TypeName

var articles = (from s in _context.Articles
                        join b in _context.ArticleTypes on s.ArticleTypeId equals b.ArticleTypeId
                        select s).ToList();

或者有没有更简单的方法来做到这一点?

目标:

 Id      ArticleName      TypeName
 1       Blah Blah        Business
 2       Helo Blah        Construction
  • 您是否具有从文章到文章类型的直接属性访问权限?如果是,为什么不_context.Articles.Select(a => new { Id = a.Id, ArticleName = a.ArticleName, TypeName = a.ArticleType.TypeName });

标签: c# linq entity-framework-core


【解决方案1】:

所以你有两个表,一个带有Articles(表1)的表和一个带有ArticleTypes(表2)的表。我决定给你的表命名是有意义的,这使得讨论更容易。

ArticlesArticleTypes之间是一对多的关系:每个Article都有一个ArticleType,即外键ArticleTypeId所指的Article类型。每个 ArticleType 都有零个或多个引用它的文章。

您正在使用实体框架。如果您关注了Entity Framework Coding Conventions,您将拥有类似于以下内容的课程。

class Article
{
    public int Id {get; set;}
    public string Name {get; set;}

    // every Article has one ArticleType, namely the one that the foreign key refers to
    public int ArticleTypeId {get; set;}
    public virtual ArticleType ArticleType {get; set;}
}

class ArticleType
{
    public int Id {get; set;}
    public string TypeName {get; set;}

    // every ArticleType has zero or more Articles referring to it (one-to-many)
    public virtual ICollection<Article> Articles {get; set;}
}

在实体框架中,非虚拟属性是指表的列;虚拟属性是指表之间的关系(一对多,多对多,...)

外键 ArticleTypeId 是一个实列,因此该属性是非虚的。属性 ArticleType 是虚拟的,因为它表示一对多关系。

为了完整起见您的 DbContext:

class MyWarehouse : DbContext
{
    public DbSet<Article> Articles {get; set;}
    public DbSet<ArticleType> ArticleTypes {get; set;}
}

我正在尝试在 ArticleTypeId 上加入 TableA 和 TableB 并基本上从 Table1 中返回所有内容,从 Table2 中返回 TypeName

在你定义了你的类之后,你的查询就很容易了。最简单的方法是使用虚拟属性。

使用虚拟属性

要求给我所有文章的 ID 和名称,每篇文章都有它的 TypeName。

using (var wareHouse = new MyWareHouse(...))
{
    var requestedArticles = wareHouse.Articles.Select(article => new
    {
        // Select only the Article Properties that you plan to use
        Id = article.Id,
        Name = article.Name,
        TypeName = article.ArticleType.TypeName,
    });

    // Process the requested Articles before disposing the wareHouse
}

换句话说:从 Articles 表中的每篇文章中获取 Id、Name 以及它拥有的唯一一个 TypeName。

实体框架知道 Articles 和 ArticleTypes 之间的关系。因为您使用虚拟属性Article.ArticleType,它知道要执行哪个连接。

使用虚拟属性,您还可以获得每个 ArticleType 以及具有此 ArticleTypes 的所有文章

var constructionArticles = wareHouse.ArticleTypes
.Where(articleType => articleType.TypeName == "construction")
.Select(articleType => new
{
    Id = articleType.Id,
    TypeName = articleType.TypeName,

    // fetch all articles that have this TypeName
    Articles = articleType.Articles.Select(article => new
    {
        Id = article.Id,
        Name = article.Name,

        // no need to fetch the foreign key, you already got this value
        // ArticleTypeId = article.ArticleTypeId,
    })
    .ToList(),
})
.ToList();

实体框架知道这种关系,并会为你做正确的(组)加入。

您是否注意到使用虚拟属性的感觉是多么自然?

自己加入

有些人不想使用虚拟属性,他们更喜欢自己做(Group-)joins。

使用具有参数 resultSelector 的方法 Join 的重载,因此您可以指定所需的结果。

// Join Articles with ArticleTypes
var requestedArticles = wareHouse.Articles.Join(wareHouse.ArticleTypes,

// from every Article take the foreign key
article => articleTypeId,

// from every ArticleType take the primary key
articleType => articleType.Id,

// parameter resultSelector:
// take each article and its one and only matching ArticleType to make one new
(article, articleType) => new
{
    Id = article.Id,
    Name = article.Name
    TypeName = articleType.TypeName,
});

如果你有一对多的关系,比如学校和他们的学生,客户和他们的订单,或者 ArticleTypes 和他们的文章,使用 GroupJoin 并从“一”端开始。如果你想要学生,他就读的学校的每个学生,使用加入,并从“多”端开始。

var schoolsWithTheirStudents = dbContext.Schools
.Where(school => school.City == ...)     // if you don't want all Schools
.GroupJoin(dbContext.Students,

// from every School take the primary key
school => school.Id,

// from every Student take the foreign key to the School he attends
student => student.SchoolId,

// resultSelector: take each Schools with its matching Students to make one ned
(school, studentsWhoAttendThisSchool) => new
{
    // Select only the School properties that you plan to use:
    Id = school.Id,
    Name = school.Name,
    Address = school.Address,
    ...

    // studentsWhoAttendThisSchool is a queryable sequence,
    // so you can use LINQ on it:
    Students = studentsWhoAttendThisSchool.Select(student => new
    {
        Id = student.Id,
        Name = student.Name,
        ...
    })
    .ToList(),
});

})

【讨论】:

  • 感谢您花时间如此彻底地回答这个问题。欣赏它
猜你喜欢
  • 1970-01-01
  • 2011-03-30
  • 2012-11-03
  • 2018-03-28
  • 1970-01-01
  • 2013-09-02
  • 2015-11-03
  • 1970-01-01
相关资源
最近更新 更多