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

Optimistic Offline Lock #2195

Open
Tracked by #22950 ...
bricelam opened this issue May 14, 2015 · 14 comments
Open
Tracked by #22950 ...

Optimistic Offline Lock #2195

bricelam opened this issue May 14, 2015 · 14 comments

Comments

@bricelam
Copy link
Contributor

bricelam commented May 14, 2015

Many databases (including PostgreSQL and SQLite) don't have an equivalent to SQL Server's rowversion type. We could improve our support for optimistic concurrency on those stores by implementing the Optimistic Offline Lock pattern in the framework.

This would be similar to having a rowversion property in the entity except that EF would manage the value. When the entity is saved, somewhere in the update pipeline, we would automatically increment the property's value. The DML would look something like this:

UPDATE Person
SET Name = "Brice", RowVersion = 24
WHERE RowVersion = 23;
@anpete
Copy link
Contributor

anpete commented May 14, 2015

+1

It's also common to have a convention for this. I.e. Look for a property called "Version", "LockVersion" etc.

@divega
Copy link
Contributor

divega commented May 14, 2015

Not sure it is a constrain but we don't need to express RowVersion = RowVersoin + 1 in the update pipeline, just passing the value 24 for the example above would work, correct?

@anpete
Copy link
Contributor

anpete commented May 14, 2015

Yes, it just means we need to be able to do the client side increment for all potential types.

@rowanmiller rowanmiller added the help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. label May 18, 2015
@rowanmiller rowanmiller added this to the Backlog milestone May 18, 2015
@roji
Copy link
Member

roji commented Sep 8, 2015

+1, would help for PostgreSQL which has no rowversion (although a workaround with generating values on update via triggers is probably possible)

@pantonis
Copy link

pantonis commented Mar 2, 2016

+1, We need it :) Not all databases are so productive working with them like sql server. PostgreSQL like roji mentioned does not support rowversion.

MetaFight added a commit to PerfectlyCromulentLtd/PC.Inventory.Backend that referenced this issue Apr 26, 2016
@roji
Copy link
Member

roji commented Apr 29, 2016

FYI I found out PostgreSQL does have an automatically-updated xmin column which could be used as a concurrency token, much like SqlServer's rowversion, so this may be less important for PostgreSQL. See npgsql/efcore.pg#19.

@bubibubi
Copy link

+1
If you implement this please keep in mind that properties marked as Timestamp are array of bytes.
Changing the value could be not only an increment.

@anpete
Copy link
Contributor

anpete commented Sep 7, 2017

@bricelam I implemented this in the Oracle sample provider by auto-generating triggers in Migrations when creating row version columns.

@divega divega added good first issue This issue should be relatively straightforward to fix. help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. and removed help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. good first issue This issue should be relatively straightforward to fix. labels May 31, 2019
@ajcvickers ajcvickers added good first issue This issue should be relatively straightforward to fix. and removed help wanted This issue involves technologies where we are not experts. Expert help would be appreciated. good first issue This issue should be relatively straightforward to fix. labels Aug 5, 2019
@LeVraiSylvain

This comment was marked as spam.

@ajcvickers

This comment was marked as spam.

@asbjornu
Copy link
Member

As reported in #32083, I'm very eager to see this feature implemented. Beyond waiting for it to be implemented in EF Core (which looks like it's going to take a while), is it possible to implement through some custom code? If so, how would that look like?

@roji
Copy link
Member

roji commented Oct 18, 2023

@asbjornu you can override SaveChanges and have code there which sets the value of the concurrency token to an incremented value. Or if you want this to happen in the database, you can set up triggers to do that there.

@ComptonAlvaro
Copy link

Hello,

I would like to present a scenario I am considering.

Suppose I have Order and OrderLine entities, and there's a rule that an order cannot have more than 5 items. I would follow these steps:

  1. The application layer has a method AddLineToOrder().
  2. In AddLineToOrder(), I retrieve the order, including all its lines, using a repository.
  3. I call the AddLine() method of the order object.
  4. If the order has fewer than 5 lines, the line is added.
  5. The AddLineToOrder() method calls the Commit() method of the repository, which uses Entity Core's SaveChanges().

My question is, how should I override the SaveChanges() in the repository? In my case, the Order entity does not have a property for concurrency because I consider this the responsibility of the repository. Therefore, I define a shadow property for concurrency in the repository using the fluent API for the Order entity, which is the root entity. This shadow property is a long property that should be incremented each time I update the order or modify some information of the aggregation, such as adding a new line.

However, when I add a new line, the order itself is not modified; it is only used to ensure data coherence. Thus, SaveChanges() will not detect any change in the Order entity and will not check for concurrency.

So, how should I override the SaveChanges() method? Should I iterate through all the entities in the context and increment the value of the entities that have a concurrency property?

Thanks so much.

@roji
Copy link
Member

roji commented Dec 8, 2023

@ComptonAlvaro first, to ensure your business rule of no more than 5 items per order, I'd recommend considering an insert trigger. That would allow you to ensure, at the database level, that your invariant is always preserved.

Second, it seems you're asking for how to manage a concurrency token - that doesn't seem really related to the 5-items-per-order rule above. In any case, yes - you can have a shadow property that's configured as a concurrency token with EF, and then override SaveChanges to enumerate over all modified entities in the context and update the concurrency token.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests