-
Notifications
You must be signed in to change notification settings - Fork 7
Description
When using the InterBaseSql.EntityFrameworkCore.InterBase Version 10.0.2 we are unable to delete an entity.
Removing an entity and calling context.SaveChanges() always throws an Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException.
See unit test InterBaseSql.EntityFrameworkCore.InterBase.Tests.EndToEnd.Delete() as code snippet to reproduce. This test fails.
Lines 53 to 64 in 0015a71
| public void Delete() | |
| { | |
| using (var db = GetDbContext<DeleteContext>()) | |
| { | |
| db.Database.ExecuteSqlRaw("create table test_delete (id int not null, name varchar(20), primary key (ID))"); | |
| db.Database.ExecuteSqlRaw("insert into test_delete values (65, 'test')"); | |
| db.Database.ExecuteSqlRaw("insert into test_delete values (66, 'test')"); | |
| db.Database.ExecuteSqlRaw("insert into test_delete values (67, 'test')"); | |
| var entity = new DeleteEntity() { Id = 66 }; | |
| var entry = db.Attach(entity); | |
| entry.State = EntityState.Deleted; | |
| db.SaveChanges(); |
Calling SaveChanges() throws the exception within Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch::Consume(RelationalDataReader reader).
https://github.com/dotnet/efcore/blob/6119066c37b98f604a5c868d691da0405d53e1aa/src/EFCore.Relational/Update/AffectedCountModificationCommandBatch.cs#L121C33-L121C74
The reader inside has no rows, only the property reader.RecordsAffected == 1.
See ResultSetMapping == ResultSetMapping.LastInResultSet where EF tries to get a row which does not exist.
https://github.com/dotnet/efcore/blob/6119066c37b98f604a5c868d691da0405d53e1aa/src/EFCore.Relational/Update/AffectedCountModificationCommandBatch.cs#L62C1-L66C1
IBUpdateSqlGenerator.AppendDeleteOperation() sets the ResultSetMapping to ResultSetMapping.LastInResultSet.
Lines 87 to 98 in 0015a71
| public override ResultSetMapping AppendDeleteOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, int commandPosition, out bool requiresTransaction) | |
| { | |
| var name = command.TableName; | |
| var operations = command.ColumnModifications; | |
| var conditionOperations = operations.Where(o => o.IsCondition).ToList(); | |
| var inputOperations = GenerateParameters(conditionOperations); | |
| AppendDeleteCommandHeader(commandStringBuilder, name, null); | |
| AppendWhereClause(commandStringBuilder, conditionOperations); | |
| commandStringBuilder.Append(SqlGenerationHelper.StatementTerminator).AppendLine(); | |
| requiresTransaction = true; | |
| return ResultSetMapping.LastInResultSet; | |
| } |
The InterBaseSql.EntityFrameworkCore.InterBase.Update.Internal::AppendUpdateOperation sets the ResultSetMapping to ResultSetMapping.NoResults.
Lines 64 to 85 in 0015a71
| public override ResultSetMapping AppendUpdateOperation(StringBuilder commandStringBuilder, IReadOnlyModificationCommand command, int commandPosition, out bool requiresTransaction) | |
| { | |
| var name = command.TableName; | |
| var operations = command.ColumnModifications; | |
| var writeOperations = operations.Where(o => o.IsWrite).ToList(); | |
| var readOperations = operations.Where(o => o.IsRead).ToList(); | |
| var conditionOperations = operations.Where(o => o.IsCondition).ToList(); | |
| var anyRead = readOperations.Any(); | |
| AppendUpdateCommandHeader(commandStringBuilder, name, null, writeOperations); | |
| AppendWhereClause(commandStringBuilder, conditionOperations); | |
| commandStringBuilder.Append(SqlGenerationHelper.StatementTerminator).AppendLine(); | |
| //if (anyRead) | |
| //{ | |
| // var keyOperations = operations.Where(o => o.IsKey).ToList(); | |
| // return AppendSelectAffectedCommand(commandStringBuilder, name, null, readOperations, keyOperations, commandPosition); | |
| //} | |
| // commandStringBuilder.Append(SqlGenerationHelper.StatementTerminator).AppendLine(); | |
| requiresTransaction = true; | |
| return ResultSetMapping.NoResults; | |
| } |
Just to doublecheck, I modified AppendDeleteOperation to return ResultSetMapping.NoResults.
=> UnitTest Delete() passes.
Doing the same im my project and Remove/Delete works as expected.
Problem: Changing this behaviour means there is no UpdateConcurrency check any more (which would be appreciated for AppendUpdateOperation as well).
UnitTest InterBaseSql.EntityFrameworkCore.InterBase.Tests.EndToEnd::ConcurrencyDelete => fail
UnitTest InterBaseSql.EntityFrameworkCore.InterBase.Tests.EndToEnd::Delete => pass
Could you please help us with this issue? Delete functionality is essential.
Thanks and with kind regards,