抛出异常使用流利的API从组中获取项目 A返回所有WAContact实体 B仅返回所有带有相关WAContact实体的WAUser ID完整 C返回带有或不带有相关WAContact实体完整的所有WAUser实体完整示例程序其他信息

问题描述

这是我要完成的查询

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()'无法翻译。用表格重写查询 可以翻译,或通过以下方式明确转换为客户评估 插入对AsEnumerable(),AsAsyncEnumerable()的调用, ToList()或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)返回所有WAContact实体

因为每个实体都必须有一个UserId,所以不需要实际查询WAUsers表:

var userIdsWithContactsWithoutJoin = context.Contacts
    .AsEnumerable()
    .GroupBy(c => c.UserId)
    .ToList();

代码仅执行SELECT,然后切换到客户端评估以将返回的数据分组到内存中。

SELECT `c`.`Id`,`c`.`Name`,`c`.`Phone`,`c`.`UserId`
FROM `Contacts` AS `c`

B)仅返回所有带有相关WAContact实体的WAUser ID(完整)

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,然后切换到客户端评估以将返回的数据分组到内存中>

SELECT `c`.`UserId`,`c`.`Id`,`c`.`Phone`
FROM `Users` AS `u`
INNER JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`

C)返回带有或不带有相关WAContact实体(完整)的所有WAUser实体(完整)

var usersWithOrWithoutContacts = context.Users
    .SelectMany(
        u => context.Contacts
            .Where(c => u.Id == c.UserId)
            .DefaultIfEmpty(),c) => new
        {
            User = u,Contact = c
        })
    .AsEnumerable()
    .GroupBy(j => j.User,j => j.Contact)
    .ToList();

代码首先执行LEFT JOIN,然后切换到客户端评估以将返回的数据分组到内存中>

SELECT `u`.`Id`,`u`.`Name`,`c`.`UserId`
FROM `Users` AS `u`
LEFT JOIN `Contacts` AS `c` ON `u`.`Id` = `c`.`UserId`

所有三个查询都返回尽可能少的数据,然后使用AsEnumerable()切换到客户端评估以在内存中执行实际分组。


示例程序

这是一个可以正常运行的示例项目,它演示了查询(包括检查):

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",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`.`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`.`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),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`,`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(),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 Server而不是MySQL)。


其他信息

有关GROUP BY查询的一般信息,请查看Complex Query Operators

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...