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

Change-tracking bulk update/delete operations #24176

Open
Tracked by #22959
roji opened this issue Feb 17, 2021 · 2 comments
Open
Tracked by #22959

Change-tracking bulk update/delete operations #24176

roji opened this issue Feb 17, 2021 · 2 comments

Comments

@roji
Copy link
Member

roji commented Feb 17, 2021

#795 tracks bulk update/delete operations which execute directly on the database and completely bypass the change tracker. This issue proposes similar bulk operations which do update the change tracker, to keep it in sync with the changes applied to the database.

Both update and delete include a predicate (i.e. which entities to update/delete); allowing arbitrary expressions in the predicate could quickly lead to a discrepancy between the change tracker and database, as functions or constructs are evaluated differently between the client and the server. As a result, the proposal is to only the predicate to check for relationships, e.g. delete all posts with a certain blog.

The changes to apply to matching entities for update need to be similarly restricted, otherwise a discrepancy can occur once again. For example, we could restrict the update action to only set matching entity properties to constants. We're not yet sure whether such a restricted bulk update is actually useful.

The bulk delete API would look something like the following:

ctx.Posts.RemoveByRelated(p => p.Blog, blog);

This should be functionally equivalent to:

foreach (var postEntry in ctx.ChangeTracker.Entries<Post>().Where(pe => pe.Entity.Blog == blog))
{
    postEntry.State = EntityState.Deleted;
}

... and also sending DELETE FROM [Posts] WHERE [BlogId] = @blogId at SaveChanges.

Some further notes/problems:

  • When does the bulk delete actually get executed? If it's deferred to SaveChanges, we have the ordering problem, i.e. what happens if a new instance is added after the bulk delete call? Similar problem with bulk update.
    • We could say that all bulk deletes are always done first, before other, non-bulk updates.
    • We could also avoid deferring and just execute immediately. That raises the problem of certain updates being deferred, and others not.
  • Needs to work with other related features: skip navigations, delete cascading (when that's implemented)
@svengeance
Copy link
Contributor

svengeance commented Feb 18, 2021

Glad to see these changes being proposed!

Me and my team have used Z Extensions in all of our projects, which offer immediate execution of database updates and deletes via UpdateFromQuery and DeleteFromQuery.
From our experience, having to manage the difference in deferred vs immediate execution has been relatively straight forward. It's actually kind of a blessing, really, not having to worry about the order of queued up operations. All that to say - even with the inclusion the ChangeTracker for these bulk operations, I think it could be clearer from both the implementation and the user were it immediate.

The only caveat might be in the name of these change-tracking related APIs. If the name RemoveByRelated is kept, it is certainly less clear that it is immediate as compared to Remove and RemoveRange.

@roji
Copy link
Member Author

roji commented Feb 18, 2021

@svengeance don't worry too much about the name - it really is only a very initial proposal, with naming explicitly not being final.

Please note that although we made progress on some API/design decisions, this still isn't a feature planned for the 6.0 release. Please upvote above if you find this useful.

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

3 participants