如何在linq中与task< ienumerable< entity>>一起使用group by

5rgfhyps  于 2021-07-24  发布在  Java
关注(0)|答案(4)|浏览(364)

我对实体框架和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>> . 我找到的sql查询是: 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;
    }

然而,这给了我一个转换问题。完整的错误堆栈是: 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>>' 我不知道如何解决这些问题。任何帮助都将不胜感激。

hiz5n14c

hiz5n14c1#

当您使用groupby时,您希望创建具有共同点的元素组。您想要共用的属性在参数中指定 keySelector .
使用此参数可以说:please make groups of Paraticipants ,都具有中指定的属性的相同值 keySelector .
在您的例子中:您希望创建具有相同zip值的参与者组。换句话说:如果您获取同一组中的两个参与者,那么您需要确定他们的zip值相同。
因此,首先,更改keyselector:

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

结果是一系列的组。每个组都有一个键,每个组都是(不是!)一系列参与者。所有参与者的属性zip的值都等于key的值。
在那之后,您需要获取每个组,并从每个组中创建一个新的participant对象,该对象只填充了两个属性
count是组中的参与者数
zip是组中任何元素的zip,正如我们前面看到的,它是组的键。
.select(groupofparticipantswithsamekey=>new participant{count=groupofparticipantswithsamekey.count(),zip=groupofparticipantswithsamekey.key,});
你注意到我把标识符改了吗 i 用正确的标识符。选择合适的标识符将帮助您识别linq中的问题。这可能有点牵强,但它有助于您理解正在处理的序列中的每个元素代表什么。
顺便说一下,有一个enumerable.groupby重载,这个重载带有一个参数resultselector。这使得您的选择不必要。

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

这个更容易理解,因为您已经消除了标识符键。

小小的设计改进

您已经创建了一个方法,该方法接受所有参与者,并为每个使用的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 ,只有一个地方,你必须改变和重写测试。
因此,如果您将在多个地方使用它:请考虑扩展方法

dy1byipe

dy1byipe2#

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

public IEnumerable<Participant> GetResults()

或者如果您希望该方法使用async和await模式,请使用 async 方法签名和调用中的关键字 await 以及 ToListAsync() ```
public async Task<IEnumerable> GetResults()
{
var results = await context.Participants
.GroupBy(i => i)
.Select(i => new {
Count = i.Key,
Zip = i.Count()
}
).ToListAsync();

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

cgh8pdjw3#

正如将军所说。。使用异步和重命名方法:

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

ffx8fchx4#

select类似于您想从特定查询中提取什么。在这里,您将使用新的{…}创建一个匿名类型

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

这就是为什么它的生产列表是这种类型的。
但是你想要名单那怎么办?匿名类型返回参与者。这样地

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

相关问题