-
Notifications
You must be signed in to change notification settings - Fork 3.3k
Description
When executing the following query an exception is thrown
var orderIds = new [] { Guid.NewGuid(), Guid.NewGuid() };
var orders = dbContext.Orders
.Where(c => dbContext.AsQueryable(orderIds).Contains(c.OrderId))
.Select(c => new
{
c.Project.Code,
c.Project.Revenue
})
.GroupBy(c => new
{
c.Code
})
.Select(c => new
{
c.Key.Code,
Sum = c.Sum(e => e.Revenue)
}).ToList();Microsoft.Data.SqlClient.SqlException (0x80131904): The multi-part identifier "s.Value" could not be bound.
exec sp_executesql N'SELECT [p].[Code], (
SELECT COALESCE(SUM([p1].[Revenue]), 0.0)
FROM [Orders] AS [o0]
INNER JOIN [Projects] AS [p0] ON [o0].[ProjectId] = [p0].[ProjectId]
INNER JOIN [Projects] AS [p1] ON [o0].[ProjectId] = [p1].[ProjectId]
WHERE EXISTS (
SELECT 1
FROM STRING_SPLIT(@__source_1, @__separator_2) AS [s0]
WHERE CONVERT(UNIQUEIDENTIFIER, [s0].[Value]) = [o0].[OrderId]) AND [p].[Code] = [p0].[Code]) AS [Sum]
FROM [Orders] AS [o]
INNER JOIN [Projects] AS [p] ON [o].[ProjectId] = [p].[ProjectId]
WHERE EXISTS (
SELECT 1
FROM STRING_SPLIT(@__source_1, @__separator_2) AS [s0]
WHERE CONVERT(UNIQUEIDENTIFIER, [s].[Value]) = [o].[OrderId])
GROUP BY [p].[Code]',N'@__source_1 nvarchar(4000),@__separator_2 nvarchar(4000)',@__source_1=N'7bd14254-eac8-413d-9328-2fda4725b690,e624c495-55dd-436a-8c62-6a7e49c8435d',@__separator_2=N','The incorrect part is
WHERE CONVERT(UNIQUEIDENTIFIER, [s].[Value]) = [o].[OrderId]The .AsQueriable() solves a cache plan pollution problem. The full solution is in the attachment.
Or
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
var optionsBuilder = new DbContextOptionsBuilder<TestContext>();
optionsBuilder.UseSqlServer("Server=TestDb;Database=CustomerDb;Trusted_Connection=True;Encrypt=False;");
using var dbContext = new TestContext(optionsBuilder.Options);
dbContext.Database.EnsureDeleted();
dbContext.Database.EnsureCreated();
var orderIds = new [] { Guid.NewGuid(), Guid.NewGuid() };
var orders = dbContext.Orders
.Where(c => dbContext.AsQueryable(orderIds).Contains(c.OrderId))
.Select(c => new
{
c.Project.Code,
c.Project.Revenue
})
.GroupBy(c => new
{
c.Code
})
.Select(c => new
{
c.Key.Code,
Sum = c.Sum(e => e.Revenue)
}).ToList();
public class Project
{
public Guid ProjectId { get; set; }
public decimal Revenue { get; set; }
public string Code { get; set; }
}
public class Order
{
public Guid OrderId { get; set; }
public Guid ProjectId { get; set; }
public Project Project { get; set; }
}
public class TestContext : DbContext
{
public TestContext(DbContextOptions options)
: base(options)
{
}
public DbSet<Project> Projects { get; set; }
public DbSet<Order> Orders { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.ConfigureDbFunctions();
}
[Keyless]
private class StringSplitResult
{
public string Value { get; set; }
}
[DbFunction(IsBuiltIn = true, Name = "STRING_SPLIT")]
private IQueryable<StringSplitResult> Split(string source, string separator)
=> FromExpression(() => Split(source, separator));
public IQueryable<Guid> AsQueryable(IEnumerable<Guid> source)
=> Split(string.Join(",", source.Select(x => Convert.ToString(x))), ",")
.Select(s => DbFunctionExtensions.ToGuid(s.Value)!.Value);
}
public static class DbFunctionExtensions
{
public static Guid? ToGuid(string value) => throw new NotImplementedException();
public static void ConfigureDbFunctions(this ModelBuilder modelBuilder)
{
var type = typeof(DbFunctionExtensions);
modelBuilder.HasDbFunction(type.GetMethod(nameof(ToGuid))!)
.HasTranslation(t => new SqlFunctionExpression(
functionName: "CONVERT",
arguments: new[] { new SqlFragmentExpression("UNIQUEIDENTIFIER"), t[0] },
nullable: true,
argumentsPropagateNullability: new[] { false, true },
typeof(Guid),
typeMapping: null))
.IsBuiltIn();
}
}Environment details:
EF Core: 7.0.0
Database provider: Microsoft.EntityFrameworkCore.SqlServer
Target framework: .NET 7.0
Operating system: Windows 10
IDE: Visual Studio 2022 17.4.1
superjulius and bachratyg