抛出异常使用流利的 api 从组中获取项目
Exception thrown Getting items from group using fluent api
这是我要完成的查询:
var contacts = await dbContext.Contacts
.GroupBy(o => o.UserId)
.Select(group => new
{
UserId = group.Key,
Contacts = group.ToList()
}).ToListAsync();
这是联系人实体:
[Table("Contacts")]
public class WAContact
{
public int Id { get; set; }
public string Phone { get; set; }
public string Name { get; set; }
[NotNull]
public int UserId { get; set; }
public WAUser User { get; set; }
}
此代码抛出此异常:
.ToList()' could not be translated. Either rewrite the query in a form
that can be translated, or switch to client evaluation explicitly by
inserting a call to either AsEnumerable(), AsAsyncEnumerable(),
ToList(), or ToListAsync().
我看过执行 ToList() 检索组项目的示例,但不知道我的代码中发生了什么。
P.D。经过更多测试后,我注意到我在调用 First()、Last() 等时也遇到了同样的错误。但是 Count() 例如可以工作。奇怪!
这是个问题。
联系人 = group.ToList()
为什么不像
那样更改代码
var grouprs= await dbContext.Contacts.Select(c=>c)
.GroupBy(o => o.UserId);
您收到此异常是因为 EF 无法将 LINQ 转换为等效的 SQL。
将您的查询更改为此
// Load the Contact from the DB
var contacts = await dbContext.Contacts.ToListAsync();
// Perform the group by in memory
var userContacts = contacts
.GroupBy(o => o.UserId)
.Select(group => new
{
UserId = group.Key,
Contacts = group.Select(contact => new
{
contact.Name,
contact.Phone,
contact.Id
}).ToList()
}).ToList();
现在 EF 将能够将 LINQ 转换为正确的 SQL。
此查询无法翻译成 SQL。
我已经为此类错误写了一个小答案,您的查询位于列表的顶部:LINQ to Database: how to group entities properly and GroupBy limitations
您可以通过多种方式实现您想要的查询,具体取决于您想要的结果:
A) Return 所有 WAContact 实体
因为每个实体都必须有一个 UserId,所以不需要实际查询 WAUsers table:
var userIdsWithContactsWithoutJoin = context.Contacts
.AsEnumerable()
.GroupBy(c => c.UserId)
.ToList();
代码仅执行 SELECT
,然后切换到 client-evaluation 以对内存中的 returned 数据进行分组:
SELECT `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
FROM `Contacts` AS `c`
B) Return 所有 WAUser ID 仅与相关的 WAContact 实体(完整)
var userIdsWithContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId),
(u, c) => new
{
c.UserId,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.UserId, j => j.Contact)
.ToList();
代码首先执行 INNER JOIN
,然后切换到 client-evaluation 以对内存中的 returned 数据进行分组:
SELECT `c`.`UserId`, `c`.`Id`, `c`.`Name`, `c`.`Phone`
FROM `Users` AS `u`
INNER JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
C) Return 所有 WAUser 实体(完整),有或没有相关的 WAContact 实体(完整)
var usersWithOrWithoutContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId)
.DefaultIfEmpty(),
(u, c) => new
{
User = u,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.User, j => j.Contact)
.ToList();
代码首先执行 LEFT JOIN,然后切换到 client-evaluation 以对内存中的 returned 数据进行分组:
SELECT `u`.`Id`, `u`.`Name`, `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
FROM `Users` AS `u`
LEFT JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
所有三个查询return尽可能少的数据然后使用AsEnumerable()
切换到client-evaluation在内存中执行实际分组。
示例程序
这是一个完整的示例项目,演示了查询(包括检查):
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
namespace IssueConsoleTemplate
{
[Table("Contacts")]
public class WAContact
{
public int Id { get; set; }
public string Phone { get; set; }
public string Name { get; set; }
[NotNull]
public int UserId { get; set; }
public WAUser User { get; set; }
}
[Table("Users")]
public class WAUser
{
public int Id { get; set; }
public string Name { get; set; }
}
//
// DbContext:
//
public class Context : DbContext
{
public DbSet<WAContact> Contacts { get; set; }
public DbSet<WAUser> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySql(
"server=127.0.0.1;port=3306;user=root;password=;database=So64391764",
b => b.ServerVersion("8.0.21-mysql")
.CharSetBehavior(CharSetBehavior.NeverAppend))
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<WAUser>()
.HasData(
new WAUser {Id = 1, Name = "John"},
new WAUser {Id = 2, Name = "Jane"},
new WAUser {Id = 3, Name = "Mike"});
modelBuilder.Entity<WAContact>()
.HasData(
new WAContact {Id = 11, Name = "John's First Contact", Phone = "12345", UserId = 1},
new WAContact {Id = 12, Name = "John's Second Contact", Phone = "23456", UserId = 1},
new WAContact {Id = 21, Name = "Jane's Only Contact", Phone = "09876", UserId = 2});
}
}
internal class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
// Return all WAContact entities. Because every entity must have a UserId,
// there is no need to actually query the WAUsers table.
// Just performs a SELECT, then switches to client-evaluation to group the returned
// data in memory:
// SELECT `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
// FROM `Contacts` AS `c`
var userIdsWithContactsWithoutJoin = context.Contacts
.AsEnumerable()
.GroupBy(c => c.UserId)
.ToList();
Debug.Assert(userIdsWithContactsWithoutJoin.Count == 2);
Debug.Assert(userIdsWithContactsWithoutJoin[0].Key == 1);
Debug.Assert(userIdsWithContactsWithoutJoin[0].Count() == 2);
Debug.Assert(userIdsWithContactsWithoutJoin[0].First().Name == "John's First Contact");
// Return all WAUser Ids only with related WAContact entities (full).
// First performs an INNER JOIN, then switches to client-evaluation to group the
// returned data in memory:
// SELECT `c`.`UserId`, `c`.`Id`, `c`.`Name`, `c`.`Phone`
// FROM `Users` AS `u`
// INNER JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
var userIdsWithContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId),
(u, c) => new
{
c.UserId,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.UserId, j => j.Contact)
.ToList();
Debug.Assert(userIdsWithContacts.Count == 2);
Debug.Assert(userIdsWithContacts[0].Key == 1);
Debug.Assert(userIdsWithContacts[0].Count() == 2);
Debug.Assert(userIdsWithContacts[0].First().Name == "John's First Contact");
// Return all WAUser entities (full) with or without related WAContact entities (full).
// First performs a LEFT JOIN, then switches to client-evaluation to group the returned
// data in memory:
// SELECT `u`.`Id`, `u`.`Name`, `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
// FROM `Users` AS `u`
// LEFT JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
var usersWithOrWithoutContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId)
.DefaultIfEmpty(),
(u, c) => new
{
User = u,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.User, j => j.Contact)
.ToList();
Debug.Assert(usersWithOrWithoutContacts.Count == 3);
Debug.Assert(usersWithOrWithoutContacts[0].Key.Name == "John");
Debug.Assert(usersWithOrWithoutContacts[0].Count() == 2);
Debug.Assert(usersWithOrWithoutContacts[0].First().Name == "John's First Contact");
}
}
}
您也可以 运行 这个 .NET Fiddle(但使用 SQL 服务器而不是 MySQL)。
更多信息
有关 GROUP BY
查询的一般信息,请查看 Complex Query Operators。
这是我要完成的查询:
var contacts = await dbContext.Contacts
.GroupBy(o => o.UserId)
.Select(group => new
{
UserId = group.Key,
Contacts = group.ToList()
}).ToListAsync();
这是联系人实体:
[Table("Contacts")]
public class WAContact
{
public int Id { get; set; }
public string Phone { get; set; }
public string Name { get; set; }
[NotNull]
public int UserId { get; set; }
public WAUser User { get; set; }
}
此代码抛出此异常:
.ToList()' could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync().
我看过执行 ToList() 检索组项目的示例,但不知道我的代码中发生了什么。
P.D。经过更多测试后,我注意到我在调用 First()、Last() 等时也遇到了同样的错误。但是 Count() 例如可以工作。奇怪!
这是个问题。 联系人 = group.ToList()
为什么不像
那样更改代码var grouprs= await dbContext.Contacts.Select(c=>c)
.GroupBy(o => o.UserId);
您收到此异常是因为 EF 无法将 LINQ 转换为等效的 SQL。
将您的查询更改为此
// Load the Contact from the DB
var contacts = await dbContext.Contacts.ToListAsync();
// Perform the group by in memory
var userContacts = contacts
.GroupBy(o => o.UserId)
.Select(group => new
{
UserId = group.Key,
Contacts = group.Select(contact => new
{
contact.Name,
contact.Phone,
contact.Id
}).ToList()
}).ToList();
现在 EF 将能够将 LINQ 转换为正确的 SQL。
此查询无法翻译成 SQL。 我已经为此类错误写了一个小答案,您的查询位于列表的顶部:LINQ to Database: how to group entities properly and GroupBy limitations
您可以通过多种方式实现您想要的查询,具体取决于您想要的结果:
A) Return 所有 WAContact 实体
因为每个实体都必须有一个 UserId,所以不需要实际查询 WAUsers table:
var userIdsWithContactsWithoutJoin = context.Contacts
.AsEnumerable()
.GroupBy(c => c.UserId)
.ToList();
代码仅执行 SELECT
,然后切换到 client-evaluation 以对内存中的 returned 数据进行分组:
SELECT `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
FROM `Contacts` AS `c`
B) Return 所有 WAUser ID 仅与相关的 WAContact 实体(完整)
var userIdsWithContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId),
(u, c) => new
{
c.UserId,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.UserId, j => j.Contact)
.ToList();
代码首先执行 INNER JOIN
,然后切换到 client-evaluation 以对内存中的 returned 数据进行分组:
SELECT `c`.`UserId`, `c`.`Id`, `c`.`Name`, `c`.`Phone`
FROM `Users` AS `u`
INNER JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
C) Return 所有 WAUser 实体(完整),有或没有相关的 WAContact 实体(完整)
var usersWithOrWithoutContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId)
.DefaultIfEmpty(),
(u, c) => new
{
User = u,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.User, j => j.Contact)
.ToList();
代码首先执行 LEFT JOIN,然后切换到 client-evaluation 以对内存中的 returned 数据进行分组:
SELECT `u`.`Id`, `u`.`Name`, `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
FROM `Users` AS `u`
LEFT JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
所有三个查询return尽可能少的数据然后使用AsEnumerable()
切换到client-evaluation在内存中执行实际分组。
示例程序
这是一个完整的示例项目,演示了查询(包括检查):
using System.ComponentModel.DataAnnotations.Schema;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using Pomelo.EntityFrameworkCore.MySql.Infrastructure;
namespace IssueConsoleTemplate
{
[Table("Contacts")]
public class WAContact
{
public int Id { get; set; }
public string Phone { get; set; }
public string Name { get; set; }
[NotNull]
public int UserId { get; set; }
public WAUser User { get; set; }
}
[Table("Users")]
public class WAUser
{
public int Id { get; set; }
public string Name { get; set; }
}
//
// DbContext:
//
public class Context : DbContext
{
public DbSet<WAContact> Contacts { get; set; }
public DbSet<WAUser> Users { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMySql(
"server=127.0.0.1;port=3306;user=root;password=;database=So64391764",
b => b.ServerVersion("8.0.21-mysql")
.CharSetBehavior(CharSetBehavior.NeverAppend))
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<WAUser>()
.HasData(
new WAUser {Id = 1, Name = "John"},
new WAUser {Id = 2, Name = "Jane"},
new WAUser {Id = 3, Name = "Mike"});
modelBuilder.Entity<WAContact>()
.HasData(
new WAContact {Id = 11, Name = "John's First Contact", Phone = "12345", UserId = 1},
new WAContact {Id = 12, Name = "John's Second Contact", Phone = "23456", UserId = 1},
new WAContact {Id = 21, Name = "Jane's Only Contact", Phone = "09876", UserId = 2});
}
}
internal class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
// Return all WAContact entities. Because every entity must have a UserId,
// there is no need to actually query the WAUsers table.
// Just performs a SELECT, then switches to client-evaluation to group the returned
// data in memory:
// SELECT `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
// FROM `Contacts` AS `c`
var userIdsWithContactsWithoutJoin = context.Contacts
.AsEnumerable()
.GroupBy(c => c.UserId)
.ToList();
Debug.Assert(userIdsWithContactsWithoutJoin.Count == 2);
Debug.Assert(userIdsWithContactsWithoutJoin[0].Key == 1);
Debug.Assert(userIdsWithContactsWithoutJoin[0].Count() == 2);
Debug.Assert(userIdsWithContactsWithoutJoin[0].First().Name == "John's First Contact");
// Return all WAUser Ids only with related WAContact entities (full).
// First performs an INNER JOIN, then switches to client-evaluation to group the
// returned data in memory:
// SELECT `c`.`UserId`, `c`.`Id`, `c`.`Name`, `c`.`Phone`
// FROM `Users` AS `u`
// INNER JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
var userIdsWithContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId),
(u, c) => new
{
c.UserId,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.UserId, j => j.Contact)
.ToList();
Debug.Assert(userIdsWithContacts.Count == 2);
Debug.Assert(userIdsWithContacts[0].Key == 1);
Debug.Assert(userIdsWithContacts[0].Count() == 2);
Debug.Assert(userIdsWithContacts[0].First().Name == "John's First Contact");
// Return all WAUser entities (full) with or without related WAContact entities (full).
// First performs a LEFT JOIN, then switches to client-evaluation to group the returned
// data in memory:
// SELECT `u`.`Id`, `u`.`Name`, `c`.`Id`, `c`.`Name`, `c`.`Phone`, `c`.`UserId`
// FROM `Users` AS `u`
// LEFT JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`
var usersWithOrWithoutContacts = context.Users
.SelectMany(
u => context.Contacts
.Where(c => u.Id == c.UserId)
.DefaultIfEmpty(),
(u, c) => new
{
User = u,
Contact = c
})
.AsEnumerable()
.GroupBy(j => j.User, j => j.Contact)
.ToList();
Debug.Assert(usersWithOrWithoutContacts.Count == 3);
Debug.Assert(usersWithOrWithoutContacts[0].Key.Name == "John");
Debug.Assert(usersWithOrWithoutContacts[0].Count() == 2);
Debug.Assert(usersWithOrWithoutContacts[0].First().Name == "John's First Contact");
}
}
}
您也可以 运行 这个 .NET Fiddle(但使用 SQL 服务器而不是 MySQL)。
更多信息
有关 GROUP BY
查询的一般信息,请查看 Complex Query Operators。