繁体   English   中英

如何将 Group By 与 Task 一起使用<ienumerable<entity> &gt; 在 LINQ </ienumerable<entity>

[英]How to use Group By with Task<IEnumerable<Entity>> in LINQ

我是实体框架和 LINQ 的新手。 试图通过例子来学习它。 我有一个名为“参与者”的实体,如下所示:

    public class Participant
    {
        public int Id { get; set; }
        public int Count { get; set; }
        public string Zip { get; set; }
        public string ProjectStatus { get; set; }
        public string IncomingSource { get; set; }
    }

我正在尝试使用 Group by 并将结果返回为Task<IEnumerable<Participant>> The Sql Query that I found the is: SELECT Count(Id) as #, Zip FROM [database].[dbo].[Participants] GROUP BY Zip Order By Zip

我试图实现相同结果的代码如下所示:

    public Task<IEnumerable<Participant>> GetResults()
    {

        var results = context.Participants
        .GroupBy(i => i)
        .Select(i => new { 
            Count = i.Key, 
            Zip = i.Count() 
            }
        ).ToList();

        return results;
    }

但是,这给了我一个转换问题。 The complete error stack is: Cannot implicitly convert type 'System.Collections.Generic.List<<anonymous type: project.API.Models.Participant Count, int Zip>>' to 'System.Threading.Tasks.Task<System.Collections.Generic.IEnumerable<project.API.Models.Participant>>'

我不确定如何解决转换这些问题。 任何帮助,将不胜感激。

当您使用 GroupBy 时,您希望创建具有共同点的元素组。 您希望共有的属性在参数keySelector中指定。

使用此参数,您说:请创建组Paraticipants ,所有组都具有keySelector中指定的属性的相同值。

在您的情况下:您希望创建具有相同 ZIP 值的参与者组。 换句话说:如果您获取同一组中的两个参与者,您希望确定它们对于 ZIP 具有相同的值。

所以首先,改变你的 keySelector:

var result = dbContext.Participants.GroupBy(participant => participant.Zip)

结果是一系列组。 每个组都有一个密钥,每个组都是(没有)一个参与者序列。 所有参与者的属性 Zip 的值等于 Key 的值。

之后,你想参加每个组,并从每个组中创建一个新的参与者 object,它只填充了两个属性

  • 计数是组中参与者的数量

  • Zip 是 Group 中任何元素的 Zip,正如我们之前看到的 Group 的 Key。

    .Select(groupOfParticipantsWithSameKey => new Participant { Count = groupOfParticipantsWithSameKey.Count(), Zip = groupOfParticipantsWithSameKey.Key, });

您是否注意到我用适当的标识符更改了标识符i 选择正确的标识符将帮助您识别 LINQ 中的问题。 这可能有点牵连,但它可以帮助您了解您正在处理的序列中的每个元素代表什么。

顺便说一句, Enumerable.GroupBy有一个重载,即带有参数 resultSelector 的重载。 这使您的 Select 变得不必要。

var result = context.Participants
    .GroupBy(participanti => participant.Zip,

    // parameter resultSelector, take each common Zip, and all Participants that have this Zip
    // to make one new object
    (zip, participantsWithThisZip) => new Participant
    {
        Zip = zip,
        Count = participantsWithThisZip.Count(),
    });

这个更容易理解,因为你去掉了标识符 Key。

一个小的设计改进

您创建了一个方法,该方法获取所有参与者并为每个使用的 Zip 返回一个参与者,其中 Count 是具有此 Zip 的参与者的数量。

如果您将更频繁地使用它,那么最好为此创建一个单独的方法,即IQueryable<Participant>的扩展方法。 这样,您可以对每个参与者序列重用该方法,而不仅仅是数据库中的所有参与者。 请参阅揭秘的扩展方法

public static class ParticpantExtensions
{
    public static IQueryable<Participant> ToParticipantsWithSameZip(
        this IEnumerable<Participant> participants)
    {
        return participants.GroupBy(
        participanti => participant.Zip,
        (zip, participantsWithThisZip) => new Participant
        {
            Zip = zip,
            Count = participantsWithThisZip.Count(),
        });
    }
}

用法:

你原来的方法:

Task<IList<Participant>> FetchParticipantsWithSameZipAsync() 
{
    using (var dbContext in new MyDbContext(...))
    {
        return await dbContext.ToParticipantsWithSameZip().ToListAsync();
    }
}

您可以在非异步版本中重用它:

IList<Participant>> FetchParticipantsWithSameZipAsync() 
{
    using (var dbContext in new MyDbContext(...))
    {
        return dbContext.ToParticipantsWithSameZip().ToList();
    }
}

但现在您也可以将其与其他 LINQ 方法交织在一起:

var newYorkParticipantsWithSameZip = dbContext.Participants
    .Where(participant => participant.State == "New York")
    .ToParticipantsWithSameZip()
    .OrderBy(participant => participant.Count())
    .ToList();

几个优点:

  • 可重复使用的
  • 代码看起来更干净,
  • 更容易理解它的作用
  • 您可以在没有数据库的情况下对其进行单元测试:任何IQueryable<Participant>都可以。
  • 如果您需要更改ToParticipantsWithSameZip ,您只需更改一处并重写测试。

因此,如果您将在多个地方使用它:考虑扩展方法

两种最简单的方法是从方法签名中删除Task使方法同步

public IEnumerable<Participant> GetResults()

或者,如果您希望方法使用async 和 await 模式,请在方法签名中使用async关键字并调用awaitToListAsync()

public async Task<IEnumerable<Participant>> GetResults()
{
    var results = await context.Participants
        .GroupBy(i => i)
        .Select(i => new { 
            Count = i.Key, 
            Zip = i.Count() 
            }
        ).ToListAsync();

注意:在这种情况下,您可能需要重命名方法GetResultsAsync

正如 TheGeneral 提到的......使用异步和重命名方法:

public async Task<IEnumerable<Participant>> GetResultsAsync()
{
    return context.Participants
    .GroupBy(i => i.Zip)
    .Select(i => new Participant 
     { 
        Count = i.Count(), 
        Zip = i.Zip 
        }
    ).ToListAsync();
}

Select 就像你想从你的特定查询中提取什么。 在这里,您正在使用 new {...} 创建一个匿名类型

.Select(i => new { 
            Count = i.Key, 
            Zip = i.Count() 
            }

这就是它产生 List<匿名>' 这种类型的原因。

但是你想要 List 那怎么办? 匿名类型返回参与者。 像这样

.Select(i => new Participant 
     { 
        Count = i.Count(), 
        Zip = i.Zip 
        }

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM