【问题标题】:EF core line group by many to manyEF核心线组多对多
【发布时间】:2018-11-17 01:26:54
【问题描述】:

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

我的桌子是这样的:

报告表:

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 server 和 EF core linq 中工作。关于如何开始的任何指示?到目前为止,我能够将表格连接在一起,但不知道如何将我的结果连接到一行中以用于具有多个类别的报告。我得到的是下面的东西,而不是上面例子中我想要的结果:

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

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

【问题讨论】:

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


    【解决方案1】:

    您描述的模型与 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 个DbSets 代表您的表格:

    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) 轻松完成)。

    最后是实际的查询。有了导航属性就很简单了——基本上就是Selects:

    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();
    

    【讨论】:

    • 啊非常感谢你的解释也很清楚。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-08
    • 2013-03-02
    相关资源
    最近更新 更多