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

How to compare entity new values with current database values? #26839

Closed
Zefek opened this issue Nov 27, 2021 · 4 comments
Closed

How to compare entity new values with current database values? #26839

Zefek opened this issue Nov 27, 2021 · 4 comments
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported

Comments

@Zefek
Copy link

Zefek commented Nov 27, 2021

Hello,

I would like to ask about data comparison between entity and database state. Reason why I would ask about it is log some changes. For example: I have two users which load customer entity in same time from database. It means both users have customer with name Test. User A changes customer name to TestA and user B changes customer name to TestB. User A saves entity into database first so there is some audit record "User A changed customer name from Test to TestA".
When User B wants to save I compare old and new values from Tracker and I get audit record "User B changed customer name from Test to TestB". It is allright.
But is there possibility to get record for audit "User B changed customer name from TestA to TestB" by EF because TestA value is in database?
I saw some solution when entity has ConcurrencyToken property which was changed before save so SaveChanges end with ConcurrencyException. In catch block there was code that entity properties (state) were saved, entity was reloaded and then entity was recovered by saved properties but concurrency token property and then SaveChanges was called again.

I think solution with concurrency token could work but it is not good because nobody knows how it works after two years of development and it is very nonintuitive. Now there is requirement user wants to know there is another user which changed data before but concurrency token is used for another reason.
I know about system-versioned (temporal) tables on MSSQL (temporal tables are not approved by customer because of fear of database size and performance). But is there another possibility how to compare entity with database state before save changes to database through EF? Or should audit log be solved on database level?

Thank you.

@ajcvickers
Copy link
Contributor

@Zefek

I saw some solution when entity has ConcurrencyToken property which was changed before save so SaveChanges end with ConcurrencyException.

Optimistic concurrency like this is the typical pattern for dealing with this situation, regardless of whether you are using EF or not.

I think solution with concurrency token could work but it is not good because nobody knows how it works after two years of development and it is very nonintuitive.

Documentation is here: Handling Concurrency Conflicts. Can you provide specifics on the issues you are having or why you feel it is unintuitive?

@Zefek
Copy link
Author

Zefek commented Nov 28, 2021

Problem what I see is in solution which I describe. What I need is how to compare values in database with entity. How this solution do it is about change concurrency token property before SaveChanges. It throws ConcurrencyException because concurrency token property has different value from database. Then I save entity and reload it from database and restore state which I save before reloading but concurrency token property. It means I have entity with database state and I can create compare changes to database state and not when entity was loaded first time.
It is because when I compare entity old values and new values I will write into log UserA changed customer name from Test to TestA. UserB changed customer name from Test to TestB. But UserA save before UserB so UserB changes TestA to TestB in database. This solution is about reload entity before save to compare database value with changes made by user.

I don't think Optimistic Locking or Concurrency Conflicts in EF is not intuitive. I describe solution which use optimistic locking to compare changes with database value - it is, I think, nonintuitive use of concurrency token.
What I need to know is that if there is anybody who solved similar problem how to compare entity with record in database for log changes in database.

Is it more clear what I mean?

@ajcvickers
Copy link
Contributor

@Zefek The only way you can reliably do this is to start a transaction before reading the value for Customer.Name, and then update it and call SaveChanges inside that same transaction. (Theoretically, this could also be done with "select for update", which is the same concept but lighter-weight than a full transaction: see #26042.)

Anything else involves a race condition between the read and the update. This is why "optimistic concurrency" was invented. Instead of pessimistically doing everything in a transaction since something else might change it while we're working with it, we instead optimistically assume that we're the only one making a change, and then deal with the (hopefully) rare case where two changes are made concurrently.

@Zefek
Copy link
Author

Zefek commented Dec 7, 2021

@ajcvickers Thank you very much. I didn't know about SELECT FOR UPDATE. I know how optimistic concurrency works. But in solution what I mentioned concurrency token is changed each time before SaveChanges so concurrency exception is thrown even if no one changes record.
There could be deadlocking when transaction or pessimistic concurrency is used so optimistic concurrency should be advantage. But it depends on case.

@Zefek Zefek closed this as completed Dec 7, 2021
@ajcvickers ajcvickers added the closed-no-further-action The issue is closed and no further action is planned. label Dec 7, 2021
@ajcvickers ajcvickers reopened this Oct 16, 2022
@ajcvickers ajcvickers closed this as not planned Won't fix, can't repro, duplicate, stale Oct 16, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
closed-no-further-action The issue is closed and no further action is planned. customer-reported
Projects
None yet
Development

No branches or pull requests

2 participants