尝试对已联接在一起的3个表进行分组时,在运行时出现错误。我知道我可以在sql中执行此操作,但如果可能的话,我会尝试保持EF Core。 EF Core抱怨说linq表达式无法翻译,或者我需要切换到客户端评估。目标是在sql服务器中运行这种简单的联接和分组。
from line in _context.OrderLines
join item in _context.Items on line.ItemId equals item.ItemId
join supplier in _context.Suppliers on item.SupplierId equals supplier.SupplierId
group line by supplier.Name
上面的分组在编译时是有效的查询表达式,如果没有随后的选择将失败,但将详细说明我的最终用途:我希望能够由供应商对我的行数量进行求和。分组后,这应该是简单的汇总。
from line in _context.OrderLines
join item in _context.Items on line.ItemId equals item.ItemId
join supplier in _context.Suppliers on item.SupplierId equals supplier.SupplierId
group line by supplier.Name into lineGrouping
select new { Name = lineGrouping.Key, Qty = lineGrouping.Sum(x => x.Qty) }
执行将导致以下错误:
System.InvalidOperationException:不支持客户端GroupBy。 在 Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.CustomShaperCompilingExpressionVisitor.VisitExtension(Expression extensionExpression)
答案 0 :(得分:2)
有关Complex Query Operators: GroupBy的常规信息,请查看EF Core官方文档。
由于没有数据库结构可以表示IGrouping,因此GroupBy运算符在大多数情况下没有转换。将聚合运算符应用于每个返回标量的组时,可以将其转换为关系数据库中的SQL GROUP BY。 SQL GROUP BY也有限制。它要求您仅按标量值分组。投影只能包含分组键列或应用于列的任何聚合。 EF Core识别此模式并将其转换为服务器[...]
现在,对于您的具体示例,它可以正常工作。这是一个示例控制台项目,以证明这一点:
using System.ComponentModel.DataAnnotations;
using System.Diagnostics;
using System.Linq;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
namespace IssueConsoleTemplate
{
public class OrderLine
{
[Key]
public int OrderLineId { get; set; }
public int ItemId { get; set; }
public int Qty { get; set; }
}
public class Supplier
{
[Key]
public int SupplierId { get; set; }
public string Name { get; set; }
}
public class Item
{
[Key]
public int ItemId { get; set; }
public int SupplierId { get; set; }
}
public class Context : DbContext
{
public DbSet<OrderLine> OrderLines { get; set; }
public DbSet<Supplier> Suppliers { get; set; }
public DbSet<Item> Items { get; set; }
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(@"Data Source=.\MSSQL14;Integrated Security=SSPI;Initial Catalog=So63040380")
.UseLoggerFactory(
LoggerFactory.Create(
b => b
.AddConsole()
.AddFilter(level => level >= LogLevel.Information)))
.EnableSensitiveDataLogging()
.EnableDetailedErrors();
}
protected override void OnModelCreating(ModelBuilder builder)
{
builder.Entity<OrderLine>()
.HasData(
new OrderLine {OrderLineId = 1, ItemId = 1, Qty = 100},
new OrderLine {OrderLineId = 2, ItemId = 2, Qty = 42},
new OrderLine {OrderLineId = 3, ItemId = 3, Qty = 21});
builder.Entity<Supplier>()
.HasData(
new Supplier {SupplierId = 1, Name = "Supplier A"},
new Supplier {SupplierId = 2, Name = "Supplier B"});
builder.Entity<Item>()
.HasData(
new Item {ItemId = 1, SupplierId = 1},
new Item {ItemId = 2, SupplierId = 1},
new Item {ItemId = 3, SupplierId = 2});
}
}
internal static class Program
{
private static void Main()
{
using var context = new Context();
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
var supplierNameAndQuantity = (from line in context.OrderLines
join item in context.Items on line.ItemId equals item.ItemId
join supplier in context.Suppliers on item.SupplierId equals supplier.SupplierId
group line by supplier.Name into lineGrouping
select new {Name = lineGrouping.Key, Qty = lineGrouping.Sum(x => x.Qty)})
.ToList();
Debug.Assert(supplierNameAndQuantity.Count == 2);
Debug.Assert(supplierNameAndQuantity[0].Name == "Supplier A");
Debug.Assert(supplierNameAndQuantity[0].Qty == 142);
Debug.Assert(supplierNameAndQuantity[1].Name == "Supplier B");
Debug.Assert(supplierNameAndQuantity[1].Qty == 21);
}
}
}
查询被转换为以下正确的SQL:
SELECT [s].[Name], SUM([o].[Qty]) AS [Qty]
FROM [OrderLines] AS [o]
INNER JOIN [Items] AS [i] ON [o].[ItemId] = [i].[ItemId]
INNER JOIN [Suppliers] AS [s] ON [i].[SupplierId] = [s].[SupplierId]
GROUP BY [s].[Name]