2 Вопрос: Предложение Linq GroupBy, не включающее элементы с нулевым счетом

вопрос создан в Wed, May 8, 2019 12:00 AM

У меня есть запрос ниже, который должен сгруппировать результат по Id, EntityName, DocType, Jurisdiction. Для каждой группы запрос также возвращает элементы 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
  1. Не является ли nae.EntityId уникальным в nettingAgreementEntities.Result? Это больше похоже на кандидата в Групповое объединение , а не (внутреннее) Join + GroupBy. В настоящее время Внутреннее объединение фильтрует ваш запрос,
    2019-05-09 08: 24: 19Z
  2. Привет @IvanStoev Ты говоришь, пока у меня есть nae.EntityId во внутреннем объединении, я никогда не получу желаемый результат?
    2019-05-09 08: 35: 28Z
  3. Нет. Я говорю, что ваш текущий запрос содержит 2 внутренних объединения, которые будут фильтровать nae без no и no без np. Хотя GroupJoin (или левое внешнее объединение) этого не сделает.
    2019-05-09 08: 39: 45Z
  4. Привет @IvanStoev Я смоделировал запрос на основе вашей рекомендации. если я опубликую свой запрос как ответ, вы сможете исправить его? он взрывается, когда я пытаюсь выполнить левое внешнее соединение.
    2019-05-09 09: 34: 51Z
  5. Конечно .. Но мне нужны участвующие классы entity /dto. И определения nettingAgreementEntities.Result, nettingOpinions.Result и nettingProducts.Result - это IQueryable из EF и т. Д.
    2019-05-09 10: 18: 46Z
2 ответа                              2                         

Чтобы резюмировать комментарии, в настоящее время ваш запрос использует Внутреннее соединение для связи NettingAgreementEntity с NettingAgreementProducts. Это не только умножает набор результатов (и, следовательно, требует использования GroupBy после), но также отфильтровывает NettingAgreementEntity без NettingAgreementProducts.

Вы можете достичь цели, переключившись на Групповое присоединение (или Левое внешнее соединение + GroupBy).

Но зачем вводить все эти осложнения. Свойства EF-навигации позволяют практически забыть о ручных объединениях, а также позволяют легко увидеть множественность, независимо от того, нужно ли группировать результат или нет.

Итак, я бы предложил добавить отсутствующее свойство навигации по коллекции в класс NettingAgreementEntity:

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

При желании сделайте то же самое для NettingAgreementProduct на тот случай, если в будущем вам понадобится нечто подобное для продуктов (это отношение «многие ко многим», и его можно будет запрашивать с обеих сторон).

Также я бы переименовал свойства навигации класса NettingOpinion в NettingAgreementProductNavigation и 0От 60035099111100101035062 до чего-то более короткого, например NettingAgreementEntityNavigation и Product. Эти имена (а также имена свойств навигации по коллекции) не влияют на схему базы данных, но IMHO обеспечивают лучшую читаемость.

Получив это, вы увидите, что требуемый запрос LINQ - это вопрос простых Entity, которые преобразуют класс сущности в DTO и позволяют транслятору запросов EF создавать необходимые объединения:

Select     
2
2019-05-09 12: 51: 29Z
  1. Спасибо за вашу помощь. Я обновил свой ответ (последний запрос) на основе вашей рекомендации, и теперь он работает.
    2019-05-09 21: 16: 21Z

Ниже приведено то, что я пробовал до сих пор. Я считаю, что на данном этапе запрос не совсем корректен, так как он по-прежнему не включает группы, у которых нет товаров.

NettingOpinion Entity

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

NettingAgreementProduct Entity

[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; }

}
    
0
2019-05-09 21: 15: 52Z
  1. На основании предложения @Ivan Stoev я обновил свой ответ (последний запрос), который сейчас работает.
    2019-05-09 21: 17: 58Z
                     var result = from nae in nettingAgreement
                     join no in nettingOpinion on nae.EntityId equals no.EntityId into pp
                     from test in pp.DefaultIfEmpty(new NettingOpinion())
                     join np in nettingProduct on test.ProductId equals np.Id into npd
                     from npdSubset in npd.DefaultIfEmpty()
                     group npdSubset 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 ?? 0,
                             Name = x == null ? string.Empty : x.Name
                         }).ToList()
                     };
источник размещен Вот