Linq GroupBy子句不包括计数为零的项目

时间:2019-05-08 15:58:43

标签: c# .net entity-framework linq

我下面有一个查询,该查询应按ID,EntityName,DocType,管辖区对结果进行分组。对于每个组,查询还返回ProductList项目。

目前,如果该组包含一个或多个产品,那么我可以看到结果给出了一个包含Id,EntityName,DocType,Jurisdiction和ProductList组合的组,但是,如果结果中不包含某个产品特定的组我完全看不到该组。我想做的是显示分组,即使该分组中没有任何产品。因此,如果ProductList的计数为零,我想设置       ProductList =新列表NettingAgreementProductDto。任何输入将不胜感激。

          var result = from nae in nettingAgreementEntities.Result
                     join no in nettingOpinions.Result 
                          on nae.EntityId equals no.EntityId 
                     join np in nettingProducts.Result 
                          on no.ProductId equals np.Id
                     group np by new 
                               { nae.EntityId, 
                                 nae.EntityName, 
                                 nae.DocType,
                                 nae.Jurisdiction 
                               } into g                      
                     select new NettingAgreementEntityDto
                     {
                         Id = g.Key.EntityId,
                         EntityName = g.Key.EntityName,
                         DocType = g.Key.DocType,
                         Jurisdiction = g.Key.Jurisdiction,
                         ProductList =  g.Select(x => new                                            
                                        NettingAgreementProductDto
                                        {
                                           Id = x.Id,
                                           Name = x.Name
                                        }).ToList()
                     };

2 个答案:

答案 0 :(得分:2)

要回顾评论,当前您的查询使用Inner Join来将NettingAgreementEntityNettingAgreementProduct关联。这不仅会乘以结果集(因此需要您在之后使用GroupBy),而且还会过滤掉NettingAgreementEntity而没有NettingAgreementProduct的情况。

您可以通过切换到Group Join(或Left Outer Join + GroupBy)来实现目标。

但是为什么要输入所有这些并发症呢? EF导航属性使您几乎无需考虑手动联接,还可以轻松查看多重性,从而了解是否需要对结果进行分组。

所以我建议将当前缺少的集合导航属性添加到您的NettingAgreementEntity类中:

public class NettingAgreementEntity
{
    // ...
    public virtual ICollection<NettingOpinion> Opinions { get; set; }
}

(可选)对NettingAgreementProduct执行相同的操作,以防将来您需要类似的产品(这是多对多关系,应该可以从双方查询)。

我还要将NettingOpinion类导航属性NettingAgreementProductNavigationNettingAgreementEntityNavigation重命名为较短的名称,例如ProductEntity。这些名称(以及集合导航属性的名称)不会影响数据库架构,但是IMHO具有更好的可读性。

有了这些,您将看到所需的LINQ查询是一个简单的Select事务,它将实体类转换为DTO,并让EF查询翻译器为您生成必要的联接:

var result = db.Set<NettingAgreementEntity>()
    .Selec(nae => new NettingAgreementEntityDto
    {
        Id = nae.EntityId,
        EntityName = nae.EntityName,
        DocType = nae.DocType,
        Jurisdiction = nae.Jurisdiction,
        ProductList = nae.Opinions
            .Select(no => new NettingAgreementProductDto
            {
                no.Product.Id,
                no.Product.Name,
            }).ToList(),
    });

答案 1 :(得分:0)

以下是我到目前为止尝试过的。我认为该查询在此阶段还不太正确,因为它仍然忽略了没有产品的组。

NettingOpinion实体

CREATE TABLE Table1 (a int,
                     b numeric(12,2),
                     c datetime2(0),
                     d date,
                     e varchar(20),
                     f sysname,
                     g varbinary);

CREATE TABLE Table2 (a int,
                     b numeric(12,2),
                     c datetime2(0),
                     d date);

GO
DECLARE @SQL nvarchar(MAX);

SET @SQL = STUFF((SELECT NCHAR(13) + NCHAR(10) + 
                         N'ALTER TABLE Table2 ADD ' + QUOTENAME(T1.name) + N' ' + T1.system_type_name + N';'
                  FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM Table1',NULL, NULL) T1
                  WHERE NOT EXISTS(SELECT 1
                                   FROM sys.dm_exec_describe_first_result_set(N'SELECT * FROM Table2',NULL, NULL) T2
                                   WHERE T1.[name] = T2.[name])
                  ORDER BY T1.column_ordinal
                  FOR XML PATH(N''),TYPE).value('.','nvarchar(MAX)'),1,2,N'');
PRINT @SQL;

EXEC sp_executesql @SQL;
GO

SELECT *
FROM dbo.Table2;

GO

DROP TABLE dbo.Table2;
DROP TABLE dbo.Table1;

NettingAgreementProduct实体

[Table("NettingOpinion",Schema = "dbo")]
public class NettingOpinion
{
    [Key]
    [Column("NettingOpinionID")]
    public int NettingOpinionId { get; set; }

    [Column("ProductID")]
    public int ProductId { get; set; }

    [Column("EntityID")]
    public int EntityId { get; set; }

    [ForeignKey("ProductId")]
    public virtual  NettingAgreementProduct NettingAgreementProductNavigation { get; set; }

    [ForeignKey("EntityId")]
    public virtual  NettingAgreementEntity NettingAgreementEntityNavigation { get; set; }
}  

NettingAgreementEntity

    public  class NettingAgreementProduct
    {
        public NettingAgreementProduct()
        {
            NettingAgreementDefaults = new HashSet<NettingAgreementDefaults>();
        }

        [Column("ProductID")]
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int Id { get; set; }

        [Required]
        [Column("ProductName")]
        [StringLength(255)]
        public string Name { get; set; }

        public virtual ICollection<NettingAgreementDefaults> NettingAgreementDefaults { get; set; }
    }

实际查询

[Table("NettingAgreementEntity")]
public class NettingAgreementEntity
{
    [Key]
    [Column("EntityID")]
    public int EntityId { get; set; }

    [MaxLength(50,ErrorMessage = "DocType Cannot be more than 50 characters")]
    [Required]
    public string DocType { get; set; }


    [Column("Jurisdiction")]
    [MaxLength(2, ErrorMessage = "Jurisdiction Cannot have more than two characters")]
    [Required]
    public string Jurisdiction { get; set; }

    [MaxLength(255,ErrorMessage = "EntityName Cannot have more than 255 characters")]
    [Required]
    public string EntityName { get; set; }

    [ForeignKey("DocType")]
    public virtual  DocType DocTypeNavigation { get; set; }

}