Skip to content

Commit

Permalink
Throw a meaningful exception when two objects represent the same docu…
Browse files Browse the repository at this point in the history
…ment in a session (#601)
  • Loading branch information
sebastienros authored Dec 9, 2024
1 parent afaebe5 commit 7e1a9ef
Show file tree
Hide file tree
Showing 2 changed files with 39 additions and 0 deletions.
9 changes: 9 additions & 0 deletions src/YesSql.Core/Session.cs
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,15 @@ public async Task SaveAsync(object entity, bool checkConcurrency = false, string

if (id > 0)
{
// If we got an object from a different identity map, without change tracking, the object reference could be different than
// the one in the identity map, and the previous state.IdentityMap.TryGetDocumentId would have returned false.
// In this case we need to assume it's an updated object and not try to "Add" it to the identity map.

if (state.IdentityMap.TryGetEntityById(id, out var _))
{
throw new InvalidOperationException("An object with the same identity is already part of this transaction. Reload it before doing any changes on it.");
}

state.IdentityMap.AddEntity(id, entity);
state.Updated.Add(entity);

Expand Down
30 changes: 30 additions & 0 deletions test/YesSql.Tests/CoreTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1014,6 +1014,36 @@ public async Task DetachAllRemoveAllEntitiesFromIdentityMap()
Assert.NotEqual(p3a, p3d);
}

[Fact]
public async Task SavingRefreshedDocument()
{
await using var session = _store.CreateSession();
var bill = new Person
{
Firstname = "Bill",
Lastname = "Gates"
};

await session.SaveAsync(bill);
await session.SaveChangesAsync();

var newBill1 = await session.GetAsync<Person>(bill.Id);
await session.SaveAsync(newBill1);
await session.SaveChangesAsync();

// IdentityMap is cleared after SaveChangesAsync so we should get a new instance
var newBill2 = await session.GetAsync<Person>(bill.Id);

// This should be no-op because newBill1 is tracked
await session.SaveAsync(newBill2);

// Saving an object whose id is already in the identity map should throw an exception
// since it shows that two objects representing the same document, with potentially
// different changes, would be stored, so some changes would be lost.

await Assert.ThrowsAsync<InvalidOperationException>(() => session.SaveAsync(newBill1));
}

[Fact]
public async Task ShouldUpdateAutoFlushedIndex()
{
Expand Down

0 comments on commit 7e1a9ef

Please sign in to comment.