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

Refactor UpdateSqlGenerator to separate select logic #27902

Merged
merged 1 commit into from
May 4, 2022

Conversation

roji
Copy link
Member

@roji roji commented Apr 27, 2022

This introduces a new SelectingUpdateSqlGenerator abstract class, which uses SELECT after the INSERT/UPDATE/DELETE to retrieve database-generated values or to perform concurrency checks. This moves all SELECT logic out of UpdateSqlGenerator, for providers which don't require it. SqlServerUpdateSqlGenerator inherits from SelectingUpdateSqlGenerator and uses the SELECT logic in various cases where triggers are defined (and OUTPUT cannot be used).

The design isn't amazing: SelectingUpdateSqlGenerator extends UpdateSqlGenerator, so that SqlServerUpdateSqlGenerator can call methods from both (it's a combination of both modes, depending on whether there are triggers or not). But it seems reasonable (am open for other ideas).

Note that most of SelectingUpdateSqlGenerator is covered for SQL Server tests which involve triggers.

Closes #10431

@roji roji requested a review from AndriySvyryd April 27, 2022 17:37
@roji roji force-pushed the UpdatePipeline/SelectAffected branch from 87411aa to cdae67e Compare April 28, 2022 10:04
@roji roji force-pushed the UpdatePipeline/SelectAffected branch from cdae67e to 36b1f98 Compare April 28, 2022 10:40
Copy link
Member

@AndriySvyryd AndriySvyryd left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I don't think it's a significant issue of having the Select logic in UpdateSqlGenerator

@roji
Copy link
Member Author

roji commented Apr 29, 2022

The main design smell here is that the select logic requires abstract methods in UpdateSqlGenerator, which are never called in the default implementation of that class (this is new in 7.0, since the default implementation now uses RETURNING/OUTPUT). Also, many (most?) implementations don't actually need selecting, and so these methods need to be overridden for nothing.

I agree it's not critical, more of a design smell. I'm OK either way - if you're against it we can leave it and close #10431.

@AndriySvyryd
Copy link
Member

If @cincuranet is happy with these changes then I'm ok.

@roji
Copy link
Member Author

roji commented Apr 30, 2022

@cincuranet could you take a look at this PR please, and let us know what you think?

@ErikEJ
Copy link
Contributor

ErikEJ commented Apr 30, 2022

@roji maybe relevant:

SqL Compact only supports a single statement per SqlCeCommand so to get identity values back, as following command execution on the same open connection is needed, something like Select @@identity

@roji
Copy link
Member Author

roji commented May 3, 2022

@ErikEJ you mean that there's not INSERT ... OUTPUT on SqlCe, right?

I think this change shouldn't cause any problems with this. Basically, before EF Core 7.0, the default UpdateSqlGenerator would never use the OUTPUT clause (or RETURNING in most databases), and would use INSERT/UPDATE/DELETE+SELECT by default to get any database-generated values (or concurrency checking, for UPDATE/DELETE).

In EF Core 7.0, the default behavior of UpdateSqlGenerator has been changed to do OUTPUT/RETURNING since that's better in various ways; but the INSERT/UPDATE/DELETE+SELECT pattern is still supported (SQL Server notably still requires it for when triggers are defined). All this PR does is factor out the latter logic into a separate subclass, so that providers which use the new, default OUTPUT/RETURNING logic won't be concerned with it.

Specifically about the single-statement-per-command aspect:

SqL Compact only supports a single statement per SqlCeCommand so to get identity values back, as following command execution on the same open connection is needed, something like Select @@identity

So you're saying it's not possible to have a single SqlCeCommand that has both the INSERT and the related SELECT? If so, that's indeed not supported at the moment (a single ModificationCommand that gets executed in two batches), but I also don't think it was supported before either... Looking at the 2.2.0 version of UpdateSqlGenerator and at the latest version of your provider's SqlCeUpdateSqlGenerator, it seems it was appending both statements inside a single command's CommandText - can you check that I'm not mistaken?

Splitting a single ModificationCommand into two batches would require an architectural change, and we haven't heard complaints from any provider which doesn't support it yet... /cc @AndriySvyryd

@ErikEJ
Copy link
Contributor

ErikEJ commented May 3, 2022

@roji
Copy link
Member Author

roji commented May 3, 2022

Ah yes, I see... That's definitely not ideal, but indeed EF Core 7.0 doesn't change anything around this, so that workaround should still be possible. If another provider pops up with this problem, we can look into a better way to support that.

@ErikEJ
Copy link
Contributor

ErikEJ commented May 3, 2022

Cool (no plans for an EF Core 7 SQLCE provider anyway)

@cincuranet
Copy link
Contributor

cincuranet commented May 4, 2022

@roji @AndriySvyryd LGTM

@roji roji merged commit 8d80ea9 into dotnet:main May 4, 2022
@roji roji deleted the UpdatePipeline/SelectAffected branch May 4, 2022 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Why are AppendIdentityWhereCondition and AppendRowsAffectedWhereCondition abstract and rest is virtual?
4 participants