Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

EF Core 6 Insert using the Add method failing when table is linked with a trigger #2521

Closed
srivathsanvlb opened this issue Oct 11, 2022 · 9 comments
Labels
duplicate This issue or pull request already exists

Comments

@srivathsanvlb
Copy link

srivathsanvlb commented Oct 11, 2022

I have a postgresql table to which I have linked a before insert trigger and trigger function. In the trigger function based on the insert values, I create child tables using inheritance and insert the value to the child table. I return NULL value in the function so that the insert doesn't happen to the parent table.

When I perform insert using Insert query in pgadmin, it is working fine ie child table is created dynamically based on inheritance and value gets entered to the child table. When queried the parent using select, it scans parent and child and merges the records to give the output.

The insert works fine (as explained above) when I use execute in .NET using ADO.NET using the Npgsql library and execute the above insert query. I executed the same insert query using EF core 6 (Dbfirst approach, optionsBuilder,UseNpgsql is used in the context) using context.Database.ExecuteSqlRaw(sqlQuery) statement and it works fine too.

But when I try to use EF core 6 again and use the Add statement on the scaffolded database model of the parent table it fails with the error

Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
         at Npgsql.EntityFrameworkCore.PostgreSQL.Update.Internal.NpgsqlModificationCommandBatch.Consume(RelationalDataReader reader)
         at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection)
         at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable`1 commandBatches, IRelationalConnection connection)
         at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IList`1 entriesToSave)
         at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(StateManager stateManager, Boolean acceptAllChangesOnSuccess)
         at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.<>c__DisplayClass31_0`2.<Execute>b__0(DbContext context, TState state)
         at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.ExecuteImplementation[TState,TResult](Func`3 operation, Func`3 verifySucceeded, TState state)
         at Microsoft.EntityFrameworkCore.Storage.ExecutionStrategy.Execute[TState,TResult](TState state, Func`3 operation, Func`3 verifySucceeded)
         at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess)
         at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)

Let me know if you would like to see the scripts, I will send them privately.

@roji
Copy link
Member

roji commented Oct 11, 2022

In general, when you do SaveChanges, EF checks how many rows were affected, and if it's not the expected number, raises a DbUpdateConcurrencyException; this is a concurrency mechanism to detect if some e.g. deleted a row just before you are able to update it.

I'm not very familiar with the inner details of triggers, but if your trigger causes the INSERT to say that "0 rows were inserted", that would cause this. There may be a way to make the INSERT still return "1 row was inserted", even if that row isn't to the original table but to a different table (via the trigger).

Alternatively, dotnet/efcore#10443 tracks allowing you to disable the concurrency checks on the EF side; but this isn't implemented at this point.

/cc @ajcvickers

@srivathsanvlb
Copy link
Author

srivathsanvlb commented Oct 12, 2022 via email

@roji
Copy link
Member

roji commented Oct 12, 2022

@srivathsanvlb I'm not seeing any attached required details.

@srivathsanvlb
Copy link
Author

srivathsanvlb commented Oct 12, 2022 via email

@srivathsanvlb
Copy link
Author

@roji - Hope you were able to get the scripts via email. Kindly let me know if I have to share by any other means.

@roji
Copy link
Member

roji commented Mar 9, 2023

@srivathsanvlb apologies, I'm going over old issues and noticed this; I'm not sure whether I actually received the script or not.

If this issue is still relevant, can you please resend?

@srivathsanvlb
Copy link
Author

srivathsanvlb commented Mar 9, 2023 via email

@roji
Copy link
Member

roji commented Mar 9, 2023

@srivathsanvlb I received your message, and it seems like what I wrote in this above comment is more or less what's happening. EF checks how many rows PG reports to have been inserted, verifying that it's 1 rows and not zero. In addition, in your case it reads back the database-generated value for the key - this is why you can see a RETURNING clause appended at the end of the INSERT statement.

The trigger is interfering with this mechanism, causing nothing to get returned, as if the insert didn't actually insert anything, and that's why EF is throwing. There may be a way to tweak your trigger configuration so that it still returns the expected details, but I don't know offhand how one would do that. Otherwise, dotnet/efcore#10443 tracks allowing you to disable this check entirely.

Note that EF Core 7.0 allows mapping inserts (and updates/deletes) to stored procedures. That would allow you to call a PG procedure directly for inserting, rather than using a trigger which replaces the inserting. If you're interested, that would probably be a way to do this.

I'm going to go ahead and close this issue as a duplicate of dotnet/efcore#10443, but please feel free to post back and ask any further questions you have.

@roji roji closed this as not planned Won't fix, can't repro, duplicate, stale Mar 9, 2023
@roji
Copy link
Member

roji commented Mar 9, 2023

Duplicate of dotnet/efcore#10443

@roji roji marked this as a duplicate of dotnet/efcore#10443 Mar 9, 2023
@roji roji added the duplicate This issue or pull request already exists label Mar 9, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
duplicate This issue or pull request already exists
Projects
None yet
Development

No branches or pull requests

2 participants