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

Fix Many to Many with optimistic concurrency tests #24560

Merged
merged 1 commit into from
Apr 1, 2021
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -44,6 +44,12 @@ public override Task Deleting_then_updating_the_same_entity_results_in_DbUpdateC
public override Task Deleting_then_updating_the_same_entity_results_in_DbUpdateConcurrencyException_which_can_be_resolved_with_store_values()
=> Task.CompletedTask;

public override Task Attempting_to_delete_same_relationship_twice_for_many_to_many_results_in_independent_association_exception()
=> Task.CompletedTask;

public override Task Attempting_to_add_same_relationship_twice_for_many_to_many_results_in_independent_association_exception()
=> Task.CompletedTask;

protected override IDbContextTransaction BeginTransaction(DatabaseFacade facade) => new FakeDbContextTransaction();

private class FakeDbContextTransaction : IDbContextTransaction
Original file line number Diff line number Diff line change
@@ -33,60 +33,68 @@ protected OptimisticConcurrencyInMemoryTestBase(TFixture fixture)

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Simple_concurrency_exception_can_be_resolved_with_store_values()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Simple_concurrency_exception_can_be_resolved_with_client_values()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Simple_concurrency_exception_can_be_resolved_with_new_values()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Simple_concurrency_exception_can_be_resolved_with_store_values_using_equivalent_of_accept_changes()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Simple_concurrency_exception_can_be_resolved_with_store_values_using_Reload()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Updating_then_deleting_the_same_entity_results_in_DbUpdateConcurrencyException()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task
Updating_then_deleting_the_same_entity_results_in_DbUpdateConcurrencyException_which_can_be_resolved_with_store_values()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task
Change_in_independent_association_after_change_in_different_concurrency_token_results_in_independent_association_exception()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Change_in_independent_association_results_in_independent_association_exception()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Two_concurrency_issues_in_one_to_many_related_entities_can_be_handled_by_dealing_with_dependent_first()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Two_concurrency_issues_in_one_to_one_related_entities_can_be_handled_by_dealing_with_dependent_first()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Throw DbUpdateException or DbUpdateConcurrencyException for in-memory database errors #23569")]
public override Task Adding_the_same_entity_twice_results_in_DbUpdateException()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Throw DbUpdateException or DbUpdateConcurrencyException for in-memory database errors #23569")]
public override Task Deleting_the_same_entity_twice_results_in_DbUpdateConcurrencyException()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Throw DbUpdateException or DbUpdateConcurrencyException for in-memory database errors #23569")]
public override Task Deleting_then_updating_the_same_entity_results_in_DbUpdateConcurrencyException()
=> Task.FromResult(true);
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Attempting_to_delete_same_relationship_twice_for_many_to_many_results_in_independent_association_exception()
=> Task.CompletedTask;

[ConditionalFact(Skip = "Optimistic Offline Lock #2195")]
public override Task Attempting_to_add_same_relationship_twice_for_many_to_many_results_in_independent_association_exception()
=> Task.CompletedTask;
}
}
54 changes: 41 additions & 13 deletions test/EFCore.Specification.Tests/OptimisticConcurrencyTestBase.cs
Original file line number Diff line number Diff line change
@@ -282,32 +282,41 @@ public virtual Task
null);
}

[ConditionalFact(Skip = "Issue#23411")]
[ConditionalFact]
public virtual Task Attempting_to_delete_same_relationship_twice_for_many_to_many_results_in_independent_association_exception()
{
return ConcurrencyTestAsync(
c =>
c.Teams.Single(t => t.Id == Team.McLaren).Sponsors.Add(c.Sponsors.Single(s => s.Name.Contains("Shell"))),
{
c.Teams.Include(e => e.Sponsors).Load();
c.Teams.Single(t => t.Id == Team.McLaren).Sponsors.Remove(c.Sponsors.Single(s => s.Name.Contains("FIA")));
},
(c, ex) =>
{
var entry = ex.Entries.Single();
Assert.IsAssignableFrom<Team>(entry.Entity);
Assert.IsAssignableFrom<TeamSponsor>(entry.Entity);
},
null);
}

[ConditionalFact(Skip = "Issue#23411")]
[ConditionalFact]
public virtual Task Attempting_to_add_same_relationship_twice_for_many_to_many_results_in_independent_association_exception()
{
return ConcurrencyTestAsync(
c =>
c.Teams.Single(t => t.Id == Team.McLaren).Sponsors.Remove(c.Sponsors.Single(s => s.Name.Contains("FIA"))),
return ConcurrencyTestAsync<DbUpdateException>(
Change,
Change,
(c, ex) =>
{
var entry = ex.Entries.Single();
Assert.IsAssignableFrom<Team>(entry.Entity);
Assert.IsAssignableFrom<TeamSponsor>(entry.Entity);
},
null);

void Change(F1Context c)
{
c.Teams.Include(e => e.Sponsors).Load();
c.Teams.Single(t => t.Id == Team.McLaren).Sponsors.Add(c.Sponsors.Single(s => s.Name.Contains("Shell")));
}
}

#endregion
@@ -707,11 +716,27 @@ private Task ConcurrencyTestAsync(
/// again. Finally, a new context is created and the validator is called so that the state of
/// the database at the end of the process can be validated.
/// </summary>
protected virtual async Task ConcurrencyTestAsync(
protected virtual Task ConcurrencyTestAsync(
Action<F1Context> storeChange,
Action<F1Context> clientChange,
Action<F1Context, DbUpdateConcurrencyException> resolver,
Action<F1Context> validator)
=> ConcurrencyTestAsync<DbUpdateConcurrencyException>(storeChange, clientChange, resolver, validator);

/// <summary>
/// Runs the two actions with two different contexts and calling
/// SaveChanges such that storeChange will succeed and the store will reflect this change, and
/// then clientChange will result in a concurrency exception.
/// After the exception is caught the resolver action is called, after which SaveChanges is called
/// again. Finally, a new context is created and the validator is called so that the state of
/// the database at the end of the process can be validated.
/// </summary>
protected virtual async Task ConcurrencyTestAsync<TException>(
Action<F1Context> storeChange,
Action<F1Context> clientChange,
Action<F1Context, TException> resolver,
Action<F1Context> validator)
where TException : DbUpdateException
{
using var c = CreateF1Context();
await c.Database.CreateExecutionStrategy().ExecuteAsync(
@@ -726,11 +751,14 @@ await c.Database.CreateExecutionStrategy().ExecuteAsync(
await innerContext.SaveChangesAsync();

var updateException =
await Assert.ThrowsAnyAsync<DbUpdateConcurrencyException>(() => context.SaveChangesAsync());
await Assert.ThrowsAnyAsync<TException>(() => context.SaveChangesAsync());

Assert.Equal(
LogLevel.Debug, Fixture.ListLoggerFactory.Log.Single(
l => l.Id == CoreEventId.OptimisticConcurrencyException).Level);
if (typeof(TException) == typeof(DbUpdateConcurrencyException))
{
Assert.Equal(
LogLevel.Debug, Fixture.ListLoggerFactory.Log.Single(
l => l.Id == CoreEventId.OptimisticConcurrencyException).Level);
}
Fixture.ListLoggerFactory.Clear();

resolver(context, updateException);