以下查询将起作用。离开加入数据后,只需切换到客户端评估(使用 AsEnumerable() 或 ToList())并使用 Linq 进行实际分组。 EF Core 将无法自行进行 Linq 样式分组,并且无论如何都会检索数据,因此在客户端执行此操作没有缺点:
var products = (from p in context.Products
join v in
context.ProductVariations on p.Id equals v.ProductId
into g
from v in g.DefaultIfEmpty()
select new {product = p, variation = v})
.AsEnumerable() // <-- switch to client-evaluation
.GroupBy(g => g.product, g => g.variation)
.Select(g => new ProductwithVarientsGroupDto
{
Label = g.Key.ProductName,
ProductId = g.Key.Id,
Items = g.Select(v => new ProdcutVariantsdto()
{
Label = g.Key.ProductName + (v == null
? ""
: " (" + v.VariantName.ToString() + ")"),
ProductId = g.Key.Id,
ProdVariantId = v.Id > 0
? v.Id
: 0
}).ToList()
});
正如@PanagiotisKanavos 在 cmets 中已经提到的,您的原始代码首先从数据库中检索所有实体,然后在内存中对它们执行查询。如果您想继续这样做,只需将context.Products 替换为product.GetAllAsync().Result 并将context.ProductVariations 替换为_repositoryVariation.GetAllAsync().Result。然后您也可以删除AsEnumerable(),因为您已经在客户端处理查询。
这是我用于测试的完整的示例控制台项目:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class Product
{
public int Id { get; set; }
public string ProductName { get; set; }
}
public class ProductVariation
{
public int Id { get; set; }
public int ProductId { get; set; }
public string VariantName { get; set; }
}
public class ProductwithVarientsGroupDto
{
public string Label { get; set; }
public int ProductId { get; set; }
public List<ProdcutVariantsdto> Items = new List<ProdcutVariantsdto>();
}
public class ProdcutVariantsdto
{
public string Label { get; set; }
public int ProductId { get; set; }
public int? ProdVariantId { get; set; }
}
public class Context : DbContext
{
public DbSet<Product> Products { get; set; }
public DbSet<ProductVariation> ProductVariations { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63031344")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Product>().HasData(
new Product {Id = 1, ProductName = "Car"},
new Product {Id = 2, ProductName = "Bus"});
modelBuilder.Entity<ProductVariation>().HasData(
new ProductVariation {Id = 1, ProductId = 1, VariantName = "Minivan"},
new ProductVariation {Id = 2, ProductId = 1, VariantName = "Convertible"},
new ProductVariation {Id = 3, ProductId = 2, VariantName = "Public Transportation"},
new ProductVariation {Id = 4, ProductId = 2, VariantName = "Shuttle"});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var products = (from p in context.Products
join v in
context.ProductVariations on p.Id equals v.ProductId
into g
from v in g.DefaultIfEmpty()
select new {product = p, variation = v})
.AsEnumerable()
.GroupBy(g => g.product, g => g.variation)
.Select(g => new ProductwithVarientsGroupDto
{
Label = g.Key.ProductName,
ProductId = g.Key.Id,
Items = g.Select(v => new ProdcutVariantsdto()
{
Label = g.Key.ProductName + (v == null
? ""
: " (" + v.VariantName.ToString() + ")"),
ProductId = g.Key.Id,
ProdVariantId = v.Id > 0
? v.Id
: 0
}).ToList()
});
var result = products.ToList();
Debug.Assert(result.Count == 2);
Debug.Assert(result[0].ProductId == 1);
Debug.Assert(result[0].Items.Count == 2);
Debug.Assert(result[0].Items[0].ProdVariantId == 1);
}
}
}