Skip to content

DbUpdateConcurrencyException when deleting an entity #23

Open
@ulrich-berkmueller

Description

@ulrich-berkmueller

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.

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.

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.

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,

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions