efcore
efcore copied to clipboard
Unhandled exception when calling ExecuteUpdate on TPС hierarchy
Bug description
An unhandled exception occurs when calling ExecuteUpdateAsync on a TPC hierarchy
Your code
using Microsoft.EntityFrameworkCore;
namespace NullabilityProcessorBug;
class Program
{
static async Task Main(string[] args)
{
await using (var context = new BugDbContext())
{
await context.Database.MigrateAsync();
context.Set<TreeItem>().AddRange(_getSeedData());
await context.SaveChangesAsync();
}
await using (var context = new BugDbContext())
{
IQueryable<TreeItem> itemsQuery = context.Set<TreeItem>();
var list = await context
.Set<TreeItemA>()
.Select(x => new
{
Item = x,
Parent = itemsQuery.FirstOrDefault(parent => parent.Id == x.ParentId)
}
)
.Select(x => new
{
Item = x.Item,
Path = x.Item.ParentId.HasValue && x.Parent != null
? (LTree)(x.Parent.TreePath + "." + x.Item.Id.ToString().Replace("-", string.Empty).ToLower())
: (LTree)x.Item.Id.ToString().Replace("-", string.Empty).ToLower()
}
)
.ExecuteUpdateAsync(x => x
.SetProperty(
y => y.Item.TreePath,
y => y.Path
)
);
}
Console.WriteLine("Hello, World!");
}
private static IEnumerable<TreeItem> _getSeedData()
{
return
[
new TreeItemA
{
Id = Guid.CreateVersion7(),
Children =
[
new TreeItemB
{
Id = Guid.CreateVersion7(),
},
new TreeItemB
{
Id = Guid.CreateVersion7(),
},
new TreeItemA
{
Id = Guid.CreateVersion7(),
Children =
[
new TreeItemB
{
Id = Guid.CreateVersion7(),
},
new TreeItemB
{
Id = Guid.CreateVersion7(),
}
]
}
]
}
];
}
}
class BugDbContext: DbContext
{
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder.UseNpgsql("Host=localhost;Database=NullabilityProcessorBug;Username=postgres;Password=postgres;Port=5432");
}
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder
.Entity<TreeItem>()
.UseTpcMappingStrategy()
.HasMany(x => x.Children)
.WithOne()
.HasForeignKey(x => x.ParentId)
.IsRequired(false);
modelBuilder
.Entity<TreeItemA>();
modelBuilder.Entity<TreeItemA>();
modelBuilder.Entity<TreeItemB>();
}
}
abstract class TreeItem
{
public Guid Id { get; set; }
public Guid? ParentId { get; set; }
public LTree TreePath { get; set; } = new LTree("unsetted");
public IList<TreeItem> Children { get; set; } = new List<TreeItem>();
}
class TreeItemA: TreeItem;
class TreeItemB: TreeItem;
Stack traces
System.InvalidOperationException: Unhandled expression '[Microsoft.EntityFrameworkCore.Query.Internal.TpcTablesExpression]' of type 'Microsoft.EntityFrameworkCore.Query.Internal.TpcTablesExpression' encountered in 'SqlNullabilityProcessor'.
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(TableExpressionBase tableExpressionBase)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SelectExpression selectExpression, Boolean visitProjection)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SelectExpression selectExpression)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitExists(ExistsExpression existsExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean preserveColumnNullabilityInformation, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal.NpgsqlSqlNullabilityProcessor.VisitSqlBinary(SqlBinaryExpression sqlBinaryExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean preserveColumnNullabilityInformation, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitCase(CaseExpression caseExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean preserveColumnNullabilityInformation, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean allowOptimizedExpansion, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Visit(SqlExpression sqlExpression, Boolean& nullable)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.VisitUpdate(UpdateExpression updateExpression)
at Microsoft.EntityFrameworkCore.Query.SqlNullabilityProcessor.Process(Expression queryExpression, IReadOnlyDictionary`2 parameterValues, Boolean& canCache)
at Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal.NpgsqlParameterBasedSqlProcessor.ProcessSqlNullability(Expression selectExpression, IReadOnlyDictionary`2 parametersValues, Boolean& canCache)
at Microsoft.EntityFrameworkCore.Query.RelationalParameterBasedSqlProcessor.Optimize(Expression queryExpression, IReadOnlyDictionary`2 parametersValues, Boolean& canCache)
at Npgsql.EntityFrameworkCore.PostgreSQL.Query.Internal.NpgsqlParameterBasedSqlProcessor.Optimize(Expression queryExpression, IReadOnlyDictionary`2 parametersValues, Boolean& canCache)
at Microsoft.EntityFrameworkCore.Query.Internal.RelationalCommandCache.GetRelationalCommandTemplate(IReadOnlyDictionary`2 parameters)
at lambda_method60(Closure, IReadOnlyDictionary`2)
at Microsoft.EntityFrameworkCore.Internal.RelationalCommandResolverExtensions.RentAndPopulateRelationalCommand(RelationalCommandResolver relationalCommandResolver, RelationalQueryContext queryContext)
at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.<>c.<NonQueryResultAsync>b__32_0(DbContext _, ValueTuple`3 state, CancellationToken cancellationToken)
at Npgsql.EntityFrameworkCore.PostgreSQL.Storage.Internal.NpgsqlExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
at NullabilityProcessorBug.Program.Main(String[] args) in D:\_repos\NullabilityProcessorBug\NullabilityProcessorBug\Program.cs:line 20
at NullabilityProcessorBug.Program.Main(String[] args) in D:\_repos\NullabilityProcessorBug\NullabilityProcessorBug\Program.cs:line 42
at NullabilityProcessorBug.Program.<Main>(String[] args)
Verbose output
EF Core version
9.0.5
Database provider
Npgsql.EntityFrameworkCore.PostgreSQL
Target framework
.NET 9.0
Operating system
Windows 11
IDE
No response
ExecuteUpdateAsync does not currently support modifying more than one table (e.g. TPT, TPC). In most cases such scenarios are detected and a specific, informative exception is thrown, but you're trying to do something a bit more complicated than the usual (mix TPC with LTree etc.).
Putting on the backlog to investigate and correct the exception.
(previously opened as https://github.com/npgsql/efcore.pg/issues/3544)