Skip to content

Fix RunAndReturn methods returning stale tracked entity values#189

Merged
artiomchi merged 6 commits intoartiomchi:mainfrom
sdukehart-omnesoft:fix/runandreturn-stale-values
Dec 23, 2025
Merged

Fix RunAndReturn methods returning stale tracked entity values#189
artiomchi merged 6 commits intoartiomchi:mainfrom
sdukehart-omnesoft:fix/runandreturn-stale-values

Conversation

@sdukehart-omnesoft
Copy link
Copy Markdown
Contributor

When RunAndReturn/RunAndReturnAsync are called and entities are already being tracked by the DbContext, EF Core was returning the stale tracked values instead of fresh values from the database. This was especially problematic when using WhenMatched expressions with calculations like existing.Balance + inserted.Balance.

Changes:

  • Added .AsNoTracking() to both RunAndReturn() and RunAndReturnAsync() to ensure fresh database values are queried
  • Added AttachOrUpdateEntities() helper method to handle entity tracking:
    • If entity is already tracked (by PK), updates it with fresh values
    • If entity is not tracked, attaches it for future tracking
  • This maintains backward compatibility while fixing the correctness issue
  • Added regression tests to verify fresh values are returned

When RunAndReturn/RunAndReturnAsync are called and entities are already
being tracked by the DbContext, EF Core was returning the stale tracked
values instead of fresh values from the database. This was especially
problematic when using WhenMatched expressions with calculations like
existing.Balance + inserted.Balance.

Changes:
- Added .AsNoTracking() to both RunAndReturn() and RunAndReturnAsync()
  to ensure fresh database values are queried
- Added AttachOrUpdateEntities() helper method to handle entity tracking:
  - If entity is already tracked (by PK), updates it with fresh values
  - If entity is not tracked, attaches it for future tracking
- This maintains backward compatibility while fixing the correctness issue
- Added regression tests to verify fresh values are returned
@sdukehart-omnesoft
Copy link
Copy Markdown
Contributor Author

I am by no stretch of the imagination thoroughly versed in EF. I readily submit that there may be much better ways of accomplishing this.

.On(v => new { v.UserID, v.Date })
.WhenMatched((existing, inserted) => new PageVisit
{
Visits = existing.Visits + inserted.Visits, // 10 + 5 = 15
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I couldn't get changes like this to work with the existing code.

.On(v => new { v.UserID, v.Date })
.WhenMatched((existing, inserted) => new PageVisit
{
Visits = existing.Visits + inserted.Visits, // 10 + 5 = 15
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I couldn't get changes like this to work with the existing code.

@sdukehart-omnesoft
Copy link
Copy Markdown
Contributor Author

@artiomchi - does this sound reasonable? Or is there a more appropriate means of getting fresh results for situations like shown in the tests?

Thanks!

@artiomchi
Copy link
Copy Markdown
Owner

Hey @sdukehart-omnesoft! Thanks for making these changes, I think they're great, and your approach looks correct to me. I've taken the liberty to make a couple small changes (slight optimisation and refactor), but otherwise this looks good to go in!

@artiomchi artiomchi merged commit 9939409 into artiomchi:main Dec 23, 2025
2 checks passed
@sdukehart-omnesoft sdukehart-omnesoft deleted the fix/runandreturn-stale-values branch December 23, 2025 19:03
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.

2 participants