如何在 LINQ 中将 Group By 与 Task<IEnumerable<Entity>> 结合使用
How to use Group By with Task<IEnumerable<Entity>> in LINQ
我是 entity framework 和 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; }
}
我正在尝试使用分组依据 return 结果为 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>>'
我不确定如何解决转换这些问题。任何帮助将不胜感激。
两种最简单的方法是从方法签名中删除 Task
,使方法 同步
public IEnumerable<Participant> GetResults()
或者,如果您希望该方法使用 async 和 await 模式,请在方法签名中使用 async
关键字 并调用 await
和 ToListAsync()
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()
}
这就是它产生的原因
列出<anonymous>'这个类型.
但是你想要 List 怎么办?匿名类型 return 参与者。
像这样
.Select(i => new Participant
{
Count = i.Count(),
Zip = i.Zip
}
当您使用 GroupBy 时,您希望对具有共同点的元素进行分组。您想要共享的 属性 在参数 keySelector
.
中指定
使用此参数,您说:请将 Paraticipants
分组,所有 属性 的值都与 keySelector
中指定的值相同。
在您的情况下:您希望将具有相同 ZIP 值的参与者分组。换句话说:如果您获取同一组中的两个参与者,您希望确定它们具有相同的 ZIP 值。
所以首先,更改您的密钥Select或:
var result = dbContext.Participants.GroupBy(participant => participant.Zip)
结果是一组序列。每个组都有一个密钥,每个组都是(不是!)一系列参与者。所有参与者的 属性 Zip 值都等于 Key 的值。
之后,您想要获取每个组,并从每个组中创建一个新的 Participant 对象,该对象只填充了两个属性
Count 是群组中的参与者人数
Zip 是组中任何元素的 Zip,正如我们之前看到的组的键。
.Select(groupOfParticipantsWithSameKey => 新参与者
{
计数 = groupOfParticipantsWithSameKey.Count(),
邮编 = 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 的 returns 一个参与者,其中计数是拥有此 Zip 的参与者的数量。
如果您将更频繁地使用它,那么最好为此创建一个单独的方法,即 IQueryable<Participant>
的扩展方法。这样您就可以对每个参与者序列重复使用该方法,而不仅仅是数据库中的所有参与者。参见 Extension Methods demystified
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
,您只需更改一个地方并重写测试。
因此,如果您将在多个地方使用它:请考虑扩展方法
我是 entity framework 和 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; }
}
我正在尝试使用分组依据 return 结果为 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>>'
我不确定如何解决转换这些问题。任何帮助将不胜感激。
两种最简单的方法是从方法签名中删除 Task
,使方法 同步
public IEnumerable<Participant> GetResults()
或者,如果您希望该方法使用 async 和 await 模式,请在方法签名中使用 async
关键字 并调用 await
和 ToListAsync()
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()
}
这就是它产生的原因 列出<anonymous>'这个类型.
但是你想要 List 怎么办?匿名类型 return 参与者。 像这样
.Select(i => new Participant
{
Count = i.Count(),
Zip = i.Zip
}
当您使用 GroupBy 时,您希望对具有共同点的元素进行分组。您想要共享的 属性 在参数 keySelector
.
使用此参数,您说:请将 Paraticipants
分组,所有 属性 的值都与 keySelector
中指定的值相同。
在您的情况下:您希望将具有相同 ZIP 值的参与者分组。换句话说:如果您获取同一组中的两个参与者,您希望确定它们具有相同的 ZIP 值。
所以首先,更改您的密钥Select或:
var result = dbContext.Participants.GroupBy(participant => participant.Zip)
结果是一组序列。每个组都有一个密钥,每个组都是(不是!)一系列参与者。所有参与者的 属性 Zip 值都等于 Key 的值。
之后,您想要获取每个组,并从每个组中创建一个新的 Participant 对象,该对象只填充了两个属性
Count 是群组中的参与者人数
Zip 是组中任何元素的 Zip,正如我们之前看到的组的键。
.Select(groupOfParticipantsWithSameKey => 新参与者 { 计数 = groupOfParticipantsWithSameKey.Count(), 邮编 = 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 的 returns 一个参与者,其中计数是拥有此 Zip 的参与者的数量。
如果您将更频繁地使用它,那么最好为此创建一个单独的方法,即 IQueryable<Participant>
的扩展方法。这样您就可以对每个参与者序列重复使用该方法,而不仅仅是数据库中的所有参与者。参见 Extension Methods demystified
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
,您只需更改一个地方并重写测试。
因此,如果您将在多个地方使用它:请考虑扩展方法