EF核心线群由多对多

时间:2018-11-17 01:26:54

标签: sql-server linq asp.net-core entity-framework-core

我有一个多对多关系表,试图在表中显示报告列表。

我的桌子看起来像这样:

报告表:

Id| ReportName |
1 | report 1   |
2 | report 2   |
3 | report 3   |

报告类别表:

Id| Name     |
1 | General  |
2 | Specific |

ReportMapping连接表:

Id| ReportId | CategoryId |
1 | 1        | 1          |
2 | 1        | 2          |
3 | 2        | 1          |
4 | 2        | 2          |

在此示例中,报告可以有多个类别,只有2个,但可以有更多类似的例子,比如说1个报告可以有5个类别,例如General,Specific,Test2,Test3和Test4

我想在.net核心应用程序的表/列表上显示以下格式:

ReportId| Report Name | Report Categories
1       | report 1    | General, Specific
2       | report 2    | General, Specific

我很难使它在sql服务器和EF核心linq中都可以工作。关于如何开始的任何指示?到目前为止,我已经能够将表格连接在一起,但是不知道如何将结果串联到具有多个类别的报表的一行中。我得到的是类似下面的内容,而不是上面的示例所希望的结果:

ReportId | Report Name | Report Categories
1        | report 1    | General
1        | report 1    | Specific
2        | report 2    | General
2        | report 2    | Specific

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

1 个答案:

答案 0 :(得分:2)

您描述的模型与EF Core文档中Many-to-many示例中的Post / Tag模型几乎相同。

所以您将有3个代表表格记录的类

public class Report
{
    public int Id { get; set; }
    public string ReportName { get; set; }
    public ICollection<ReportMapping> Mappings { get; set; }  // navigation
}

public class ReportCategory
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<ReportMapping> Mappings { get; set; } // navigation
}

public class ReportMapping
{
    public int Id { get; set; }
    public int ReportId { get; set; }
    public int CategoryId { get; set; }
    public Report Report { get; set; } // navigation
    public ReportCategory Category { get; set; } // navigation
}

和3个DbSet代表您的表:

public DbSet<Report> Reports { get; set; }
public DbSet<ReportCategory> ReportCategories { get; set; }
public DbSet<ReportMapping> ReportMappings { get; set; }

请注意,联结实体(表)中的Id属性(列)是多余的,因此,如果您不受现有数据库的约束,请考虑删除它并像示例中那样配置复合PK

modelBuilder.Entity<ReportMapping>()
    .HasKey(e => new { e.ReportId, e.CategoryId });

还要注意标记为// navigation的属性。这些就是所谓的导航属性(请参见Definition of Terms),它们表示关系的末端,并允许您访问LINQ查询中的相关数据。 无需使用join构造-请参见Don’t use Linq’s Join. Navigate!,这是EF(核心)推荐/首选的LINQ查询编写方式。

这是您的数据库模型。由于您希望查询返回特定的结果类型,因此首先定义一个代表该结果的类(例如DTO,ViewModel等),例如:

public class ReportInfo
{
    public int ReportId { get; set; }
    public string ReportName { get; set; }
    public IEnumerable<string> ReportCategories { get; set; }
}

请注意,我将ReportCategories定义为字符串序列,而不是单个字符串。这是因为第一,数据库本身不支持结果集的字符串连接,其次,第二,用逗号连接只是表示数据的多种方式之一。通常,数据格式化是客户的责任。因此,您以原始数据的原始格式(字符串列表)返回数据,并让客户端将其格式化(在这种情况下,可以使用string.Join(",", info.ReportCategories)轻松地做到这一点)。

最后是实际查询。有了导航属性,它就非常简单-基本上只需Select s:

var result = db.Reports
    .Select(r => new ReportInfo
    {
        ReportId = r.Id,
        ReportName = r.ReportName,
        ReportCategories = r.Mappings
            .Select(m => m.Category.Name)
            .ToList() // <-- to avoid N + 1 subquery in EF Core 2.1+
    })
    .ToList();