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

Enable OptimisticConcurrencyTest on Cosmos #23326

Merged
merged 1 commit into from
Nov 25, 2020
Merged
Show file tree
Hide file tree
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
Expand Up @@ -438,7 +438,7 @@ private ItemRequestOptions CreateItemRequestOptions(IUpdateEntry entry)
return null;
}

var etag = entry.GetCurrentValue(etagProperty);
var etag = entry.GetOriginalValue(etagProperty);
var converter = etagProperty.GetTypeMapping().Converter;
if (converter != null)
{
Expand Down
54 changes: 54 additions & 0 deletions test/EFCore.Cosmos.FunctionalTests/F1CosmosFixture.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using Microsoft.EntityFrameworkCore.TestModels.ConcurrencyModel;
using Microsoft.EntityFrameworkCore.TestUtilities;

// ReSharper disable InconsistentNaming
namespace Microsoft.EntityFrameworkCore.Cosmos
{
public class F1CosmosFixture : F1FixtureBase
{
protected override ITestStoreFactory TestStoreFactory
=> CosmosTestStoreFactory.Instance;

public override TestHelpers TestHelpers
=> CosmosTestHelpers.Instance;

protected override void BuildModelExternal(ModelBuilder modelBuilder)
{
base.BuildModelExternal(modelBuilder);

modelBuilder.Entity<Engine>(
b =>
{
b.Property(e => e.EngineSupplierId).IsConcurrencyToken(false);
b.Property(e => e.Name).IsConcurrencyToken(false);
b.OwnsOne(
e => e.StorageLocation, lb =>
{
lb.Property(l => l.Latitude).IsConcurrencyToken(false);
lb.Property(l => l.Longitude).IsConcurrencyToken(false);
});
});

modelBuilder.Entity<Chassis>().Property<string>("Version").IsETagConcurrency();
modelBuilder.Entity<Driver>().Property<string>("Version").IsETagConcurrency();
modelBuilder.Entity<Team>().Property<string>("Version").IsETagConcurrency();

modelBuilder.Entity<Sponsor>(
eb =>
{
eb.Property<string>("Version").IsETagConcurrency();
eb.Property<int?>(Sponsor.ClientTokenPropertyName).IsConcurrencyToken(false);
});
modelBuilder.Entity<TitleSponsor>()
.OwnsOne(
s => s.Details, eb =>
{
eb.Property<string>("Version").IsETagConcurrency();
eb.Property<int?>(Sponsor.ClientTokenPropertyName);
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;

// ReSharper disable InconsistentNaming
namespace Microsoft.EntityFrameworkCore.Cosmos
{
public class OptimisticConcurrencyCosmosTest : OptimisticConcurrencyTestBase<F1CosmosFixture>
{
public OptimisticConcurrencyCosmosTest(F1CosmosFixture fixture)
: base(fixture)
{
fixture.Reseed();
}

// Non-persisted property in query
// Issue #17670
public override Task Calling_GetDatabaseValues_on_owned_entity_works(bool async)
=> Task.CompletedTask;

public override Task Calling_Reload_on_owned_entity_works(bool async)
=> Task.CompletedTask;

// Only ETag properties can be used as concurrency tokens
public override Task Concurrency_issue_where_the_FK_is_the_concurrency_token_can_be_handled()
=> Task.CompletedTask;

public override void Nullable_client_side_concurrency_token_can_be_used()
{
}

// ETag concurrency doesn't work after an item was deleted
public override Task Deleting_the_same_entity_twice_results_in_DbUpdateConcurrencyException()
=> Task.CompletedTask;

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

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

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

private class FakeDbContextTransaction : IDbContextTransaction
{
public Guid TransactionId => new Guid();

public void Commit()
{
}

public Task CommitAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;

public void Dispose()
{
}

public ValueTask DisposeAsync() => default;

public void Rollback()
{
}

public Task RollbackAsync(CancellationToken cancellationToken = default) => Task.CompletedTask;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,12 @@
// ReSharper disable InconsistentNaming
namespace Microsoft.EntityFrameworkCore
{
public abstract class PropertyEntryTestBase<TFixture> : IClassFixture<TFixture>
public abstract class OptimisticConcurrencyRelationalTestBase<TFixture> : OptimisticConcurrencyTestBase<TFixture>
where TFixture : F1FixtureBase, new()
{
protected PropertyEntryTestBase(TFixture fixture)
=> Fixture = fixture;

protected TFixture Fixture { get; }
protected OptimisticConcurrencyRelationalTestBase(TFixture fixture)
: base(fixture)
{ }

[ConditionalFact]
public virtual void Property_entry_original_value_is_set()
Expand All @@ -37,8 +36,5 @@ public virtual void Property_entry_original_value_is_set()
}
});
}

protected F1Context CreateF1Context()
=> Fixture.CreateContext();
}
}
2 changes: 2 additions & 0 deletions test/EFCore.Specification.Tests/DatabindingTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,13 +52,15 @@ protected void SetupContext(F1Context context)
drivers.Add(
new Driver
{
Id = 43,
Name = "Pedro de la Rosa",
TeamId = AddedTeam,
CarNumber = 13
});
drivers.Add(
new Driver
{
Id = 44,
Name = "Kamui Kobayashi",
TeamId = AddedTeam,
CarNumber = null
Expand Down
6 changes: 5 additions & 1 deletion test/EFCore.Specification.Tests/F1FixtureBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ protected virtual void BuildModelExternal(ModelBuilder modelBuilder)
modelBuilder.Entity<Engine>(
b =>
{
b.Property(e => e.Id).ValueGeneratedNever();
b.Property(e => e.EngineSupplierId).IsConcurrencyToken();
b.Property(e => e.Name).IsConcurrencyToken();
b.OwnsOne(
Expand All @@ -57,7 +58,7 @@ protected virtual void BuildModelExternal(ModelBuilder modelBuilder)
});
});

modelBuilder.Entity<EngineSupplier>();
modelBuilder.Entity<EngineSupplier>(b => b.HasKey(e => e.Name));

modelBuilder.Entity<Gearbox>();

Expand All @@ -75,7 +76,10 @@ protected virtual void BuildModelExternal(ModelBuilder modelBuilder)
b.HasOne(e => e.Chassis).WithOne(e => e.Team).HasForeignKey<Chassis>(e => e.TeamId);
});

modelBuilder.Entity<Driver>(b => b.Property(e => e.Id).ValueGeneratedNever());
modelBuilder.Entity<TestDriver>();

modelBuilder.Entity<Sponsor>(b => b.Property(e => e.Id).ValueGeneratedNever());
modelBuilder.Entity<TitleSponsor>()
.OwnsOne(s => s.Details);

Expand Down
33 changes: 20 additions & 13 deletions test/EFCore.Specification.Tests/OptimisticConcurrencyTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public virtual void Nullable_client_side_concurrency_token_can_be_used()
c.Database.CreateExecutionStrategy().Execute(
c, context =>
{
using var transaction = context.Database.BeginTransaction();
using var transaction = BeginTransaction(context.Database);
var sponsor = context.Sponsors.Single(s => s.Id == 1);
Assert.Null(context.Entry(sponsor).Property<int?>(Sponsor.ClientTokenPropertyName).CurrentValue);
originalName = sponsor.Name;
Expand Down Expand Up @@ -134,14 +134,16 @@ public virtual Task Two_concurrency_issues_in_one_to_one_related_entities_can_be
return ConcurrencyTestAsync(
c =>
{
var chassis = c.Set<Chassis>().Single(c => c.Name == "MP4-25");
var team = c.Teams.Single(t => t.Id == Team.McLaren);
team.Chassis.Name = "MP4-25b";
chassis.Name = "MP4-25b";
team.Principal = "Larry David";
},
c =>
{
var chassis = c.Set<Chassis>().Single(c => c.Name == "MP4-25");
var team = c.Teams.Single(t => t.Id == Team.McLaren);
team.Chassis.Name = "MP4-25c";
chassis.Name = "MP4-25c";
team.Principal = "Jerry Seinfeld";
},
(c, ex) =>
Expand Down Expand Up @@ -181,14 +183,16 @@ public virtual Task Two_concurrency_issues_in_one_to_many_related_entities_can_b
return ConcurrencyTestAsync(
c =>
{
var driver = c.Drivers.Single(d => d.Name == "Jenson Button");
var team = c.Teams.Single(t => t.Id == Team.McLaren);
team.Drivers.Single(d => d.Name == "Jenson Button").Poles = 1;
driver.Poles = 1;
team.Principal = "Larry David";
},
c =>
{
var driver = c.Drivers.Single(d => d.Name == "Jenson Button");
var team = c.Teams.Single(t => t.Id == Team.McLaren);
team.Drivers.Single(d => d.Name == "Jenson Button").Poles = 2;
driver.Poles = 2;
team.Principal = "Jerry Seinfeld";
},
(c, ex) =>
Expand Down Expand Up @@ -227,7 +231,7 @@ public virtual Task Concurrency_issue_where_the_FK_is_the_concurrency_token_can_
{
return ConcurrencyTestAsync(
c => c.Engines.Single(e => e.Name == "056").EngineSupplierId =
c.EngineSuppliers.Single(s => s.Name == "Cosworth").Id,
c.EngineSuppliers.Single(s => s.Name == "Cosworth").Name,
c => c.Engines.Single(e => e.Name == "056").EngineSupplier =
c.EngineSuppliers.Single(s => s.Name == "Renault"),
(c, ex) =>
Expand Down Expand Up @@ -335,7 +339,7 @@ public virtual async Task Adding_the_same_entity_twice_results_in_DbUpdateExcept
await c.Database.CreateExecutionStrategy().ExecuteAsync(
c, async context =>
{
using var transaction = context.Database.BeginTransaction();
using var transaction = BeginTransaction(context.Database);
context.Teams.Add(
new Team
{
Expand Down Expand Up @@ -456,7 +460,7 @@ public virtual async Task Calling_Reload_on_an_Added_entity_that_is_not_in_datab
await c.Database.CreateExecutionStrategy().ExecuteAsync(
c, async context =>
{
using (context.Database.BeginTransaction())
using (BeginTransaction(context.Database))
{
var entry = context.Drivers.Add(
new Driver { Name = "Larry David", TeamId = Team.Ferrari });
Expand Down Expand Up @@ -505,7 +509,7 @@ private async Task TestReloadGone(EntityState state, bool async)
await c.Database.CreateExecutionStrategy().ExecuteAsync(
c, async context =>
{
using (context.Database.BeginTransaction())
using (BeginTransaction(context.Database))
{
var entry = context.Drivers.Add(
new Driver
Expand Down Expand Up @@ -567,7 +571,7 @@ private async Task TestReloadPositive(EntityState state, bool async)
await c.Database.CreateExecutionStrategy().ExecuteAsync(
c, async context =>
{
using (context.Database.BeginTransaction())
using (BeginTransaction(context.Database))
{
var larry = context.Drivers.Single(d => d.Name == "Jenson Button");
larry.Name = "Rory Gilmore";
Expand Down Expand Up @@ -600,7 +604,7 @@ public virtual async Task Calling_GetDatabaseValues_on_owned_entity_works(bool a
await c.Database.CreateExecutionStrategy().ExecuteAsync(
c, async context =>
{
using var transaction = context.Database.BeginTransaction();
using var transaction = BeginTransaction(context.Database);
var titleSponsor = context.Set<TitleSponsor>().Single(t => t.Name == "Vodafone");

var ownerEntry = context.Entry(titleSponsor);
Expand Down Expand Up @@ -630,7 +634,7 @@ public virtual async Task Calling_Reload_on_owned_entity_works(bool async)
await c.Database.CreateExecutionStrategy().ExecuteAsync(
c, async context =>
{
using var transaction = context.Database.BeginTransaction();
using var transaction = BeginTransaction(context.Database);
var titleSponsor = context.Set<TitleSponsor>().Single(t => t.Name == "Vodafone");

var ownerEntry = context.Entry(titleSponsor);
Expand Down Expand Up @@ -711,7 +715,7 @@ protected virtual async Task ConcurrencyTestAsync(
await c.Database.CreateExecutionStrategy().ExecuteAsync(
c, async context =>
{
using var transaction = context.Database.BeginTransaction();
using var transaction = BeginTransaction(context.Database);
clientChange(context);

using var innerContext = CreateF1Context();
Expand Down Expand Up @@ -740,6 +744,9 @@ await c.Database.CreateExecutionStrategy().ExecuteAsync(
});
}

protected virtual IDbContextTransaction BeginTransaction(DatabaseFacade facade)
=> facade.BeginTransaction();

protected virtual void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
{
}
Expand Down
10 changes: 5 additions & 5 deletions test/EFCore.Specification.Tests/SerializationTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ public virtual void Can_round_trip_through_JSON(bool useNewtonsoft, bool ignoreL

var teamsMap = ignoreLoops ? null : new Dictionary<int, Team>();
var enginesMap = ignoreLoops ? null : new Dictionary<int, Engine>();
var engineSupplierMap = ignoreLoops ? null : new Dictionary<int, EngineSupplier>();
var engineSupplierMap = ignoreLoops ? null : new Dictionary<string, EngineSupplier>();

foreach (var team in teamsAgain)
{
Expand Down Expand Up @@ -102,19 +102,19 @@ private static void VerifyEngine(F1Context context, Engine engine, IDictionary<i
private static void VerifyEngineSupplier(
F1Context context,
EngineSupplier engineSupplier,
IDictionary<int, EngineSupplier> engineSupplierMap)
IDictionary<string, EngineSupplier> engineSupplierMap)
{
var trackedEngineSupplier = context.EngineSuppliers.Find(engineSupplier.Id);
var trackedEngineSupplier = context.EngineSuppliers.Find(engineSupplier.Name);
Assert.Equal(trackedEngineSupplier.Name, engineSupplier.Name);

if (engineSupplierMap != null)
{
if (engineSupplierMap.TryGetValue(engineSupplier.Id, out var mappedEngineSupplier))
if (engineSupplierMap.TryGetValue(engineSupplier.Name, out var mappedEngineSupplier))
{
Assert.Same(engineSupplier, mappedEngineSupplier);
}

engineSupplierMap[engineSupplier.Id] = engineSupplier;
engineSupplierMap[engineSupplier.Name] = engineSupplier;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public Engine(ILazyLoader loader, int id, string name)

public Location StorageLocation { get; set; }

public int EngineSupplierId { get; set; }
public string EngineSupplierId { get; set; }

public virtual EngineSupplier EngineSupplier
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,12 @@ public EngineSupplier()
{
}

private EngineSupplier(ILazyLoader loader, int id, string name)
private EngineSupplier(ILazyLoader loader, string name)
{
_loader = loader;
Id = id;
Name = name;
}

public int Id { get; set; }
public string Name { get; set; }

public virtual ICollection<Engine> Engines
Expand Down
Loading