// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.TestModels.ConcurrencyModel; using Xunit; // ReSharper disable InconsistentNaming namespace EntityFrameworkCore.Jet.FunctionalTests { public class OptimisticConcurrencyJetTest : OptimisticConcurrencyTestBase { public OptimisticConcurrencyJetTest(F1JetFixture fixture) : base(fixture) { } [ConditionalFact] public async Task Modifying_concurrency_token_only_is_noop() { using (var c = CreateF1Context()) { await c.Database.CreateExecutionStrategy().ExecuteAsync( c, async context => { using (var transaction = context.Database.BeginTransaction()) { var driver = context.Drivers.Single(d => d.CarNumber == 1); driver.Podiums = StorePodiums; var firstVersion = context.Entry(driver).Property("Version").CurrentValue; await context.SaveChangesAsync(); using (var innerContext = CreateF1Context()) { innerContext.Database.UseTransaction(transaction.GetDbTransaction()); driver = innerContext.Drivers.Single(d => d.CarNumber == 1); Assert.NotEqual(firstVersion, innerContext.Entry(driver).Property("Version").CurrentValue); Assert.Equal(StorePodiums, driver.Podiums); var secondVersion = innerContext.Entry(driver).Property("Version").CurrentValue; innerContext.Entry(driver).Property("Version").CurrentValue = firstVersion; await innerContext.SaveChangesAsync(); using (var validationContext = CreateF1Context()) { validationContext.Database.UseTransaction(transaction.GetDbTransaction()); driver = validationContext.Drivers.Single(d => d.CarNumber == 1); Assert.Equal(secondVersion, validationContext.Entry(driver).Property("Version").CurrentValue); Assert.Equal(StorePodiums, driver.Podiums); } } } }); } } [ConditionalFact] public Task Database_concurrency_token_value_is_discarded_for_non_conflicting_entities() { byte[] firstVersion = null; byte[] secondVersion = null; return ConcurrencyTestAsync( c => c.Drivers.Single(d => d.CarNumber == 2).Podiums = StorePodiums, c => { var driver = c.Drivers.Single(d => d.CarNumber == 1); driver.Podiums = ClientPodiums; firstVersion = c.Entry(driver).Property("Version").CurrentValue; var secondDriver = c.Drivers.Single(d => d.CarNumber == 2); secondDriver.Podiums = ClientPodiums; secondVersion = c.Entry(secondDriver).Property("Version").CurrentValue; }, (c, ex) => { Assert.IsType(ex); var firstDriverEntry = c.Entry(c.Drivers.Local.Single(d => d.CarNumber == 1)); Assert.Equal(firstVersion, firstDriverEntry.Property("Version").CurrentValue); var databaseValues = firstDriverEntry.GetDatabaseValues(); Assert.NotEqual(firstVersion, databaseValues["Version"]); firstDriverEntry.OriginalValues.SetValues(databaseValues); var secondDriverEntry = ex.Entries.Single(); Assert.Equal(secondVersion, secondDriverEntry.Property("Version").CurrentValue); secondDriverEntry.OriginalValues.SetValues(secondDriverEntry.GetDatabaseValues()); ResolveConcurrencyTokens(secondDriverEntry); }, c => Assert.Equal(ClientPodiums, c.Drivers.Single(d => d.CarNumber == 2).Podiums)); } [ConditionalFact] public async Task Database_concurrency_token_value_is_updated_for_all_sharing_entities() { using (var c = CreateF1Context()) { await c.Database.CreateExecutionStrategy().ExecuteAsync( c, async context => { using (var transaction = context.Database.BeginTransaction()) { var sponsor = context.Set().Single(); var sponsorEntry = c.Entry(sponsor); var detailsEntry = sponsorEntry.Reference(s => s.Details).TargetEntry; var sponsorVersion = sponsorEntry.Property("Version").CurrentValue; var detailsVersion = detailsEntry.Property("Version").CurrentValue; Assert.Null(sponsorEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue); sponsorEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue = 1; sponsor.Name = "Telecom"; Assert.Equal(sponsorVersion, detailsVersion); await context.SaveChangesAsync(); var newSponsorVersion = sponsorEntry.Property("Version").CurrentValue; var newDetailsVersion = detailsEntry.Property("Version").CurrentValue; Assert.Equal(newSponsorVersion, newDetailsVersion); Assert.NotEqual(sponsorVersion, newSponsorVersion); Assert.Equal(1, sponsorEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue); Assert.Equal(1, detailsEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue); } }); } } [ConditionalFact] public async Task Original_concurrency_token_value_is_used_when_replacing_owned_instance() { using (var c = CreateF1Context()) { await c.Database.CreateExecutionStrategy().ExecuteAsync( c, async context => { using (var transaction = context.Database.BeginTransaction()) { var sponsor = context.Set().Single(); var sponsorEntry = c.Entry(sponsor); var sponsorVersion = sponsorEntry.Property("Version").CurrentValue; Assert.Null(sponsorEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue); sponsorEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue = 1; sponsor.Details = new SponsorDetails { Days = 11, Space = 51m }; context.ChangeTracker.DetectChanges(); var detailsEntry = sponsorEntry.Reference(s => s.Details).TargetEntry; detailsEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue = 1; await context.SaveChangesAsync(); var newSponsorVersion = sponsorEntry.Property("Version").CurrentValue; var newDetailsVersion = detailsEntry.Property("Version").CurrentValue; Assert.Equal(newSponsorVersion, newDetailsVersion); Assert.NotEqual(sponsorVersion, newSponsorVersion); Assert.Equal(1, sponsorEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue); Assert.Equal(1, detailsEntry.Property(Sponsor.ClientTokenPropertyName).CurrentValue); } }); } } protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) => facade.UseTransaction(transaction.GetDbTransaction()); } }