public async Task Given_SingleEntity_Should_TimeTravel() { var context = TestHelper.GetNewDbContext(); var appleId = Guid.Parse("00000002-8803-4263-ada6-cd12a33d8872"); context.Apples.Add(new Apple { Id = appleId, FruitStatus = FruitStatus.Ripe }); await context.SaveChangesAsync(); var ripeAppleTime = TestHelper.UtcNow; context = TestHelper.GetNewDbContext(); var ripeApple = await context.Apples.SingleAsync(a => a.Id == appleId); ripeApple.FruitStatus = FruitStatus.Rotten; await context.SaveChangesAsync(); context = TestHelper.GetNewDbContext(); var currentApple = await context.Apples.AsNoTracking().SingleAsync(a => a.Id == appleId); currentApple.FruitStatus.Should().Be(FruitStatus.Rotten); using (TemporalQuery.AsOf(ripeAppleTime)) { var timeTravelApple = await context.Apples.AsNoTracking().SingleAsync(a => a.Id == appleId); timeTravelApple.FruitStatus.Should().Be(FruitStatus.Ripe); } }
private static async Task AssertWormlessFruitStatusAtTime( FruitStatus expectedFruitStatus, DateTime appleTime, Guid appleId) { using (TemporalQuery.AsOf(appleTime)) { var apple = await GetApple(appleId); apple .Should() .BeEquivalentTo(new { FruitStatus = expectedFruitStatus, Worms = Array.Empty <Worm>() }, options => options.ExcludingMissingMembers()); } }
public async Task Given_IncludedCollection_Should_TimeTravelIncludedCollection() { var context = TestHelper.GetNewDbContext(); var appleId = Guid.Parse("00000003-8803-4263-ada6-cd12a33d8872"); var apple = new Apple { Id = appleId, FruitStatus = FruitStatus.Rotten }; apple.AddWorm("Gav"); context.Apples.Add(apple); await context.SaveChangesAsync(); var rottenAppleTime = TestHelper.UtcNow; context = TestHelper.GetNewDbContext(); apple = await context.Apples.SingleAsync(a => a.Id == appleId); apple.AddWorm("G-Dog"); apple.AddWorm("Gavin' A Laugh"); await context.SaveChangesAsync(); context = TestHelper.GetNewDbContext(); var currentApple = await context.Apples .Include(a => a.Worms) .SingleAsync(a => a.Id == appleId); currentApple.Worms.Count.Should().Be(3); using (TemporalQuery.AsOf(rottenAppleTime)) { // Let's use the same context for time travel var timeTravelApple = await context.Apples .Include(a => a.Worms) // All temporal queries must be .AsNoTracking() .AsNoTracking() .SingleAsync(a => a.Id == appleId); timeTravelApple.Worms.Count.Should().Be(1); } }
private static async Task AssertWormsNavigationPropertyAtRottenAppleTime(DateTime rottenAppleTime, Guid appleId) { using (TemporalQuery.AsOf(rottenAppleTime)) { using var localScope = ServiceProvider.CreateScope(); var context = localScope.ServiceProvider.GetService <ApplicationDbContext>(); var rottenAppleWorms = await context.Apples .Where(a => a.Id == appleId) .SelectMany(a => a.Worms) .Include(worm => worm.Weapons) .AsNoTracking().ToListAsync(); rottenAppleWorms .Should() .BeEquivalentTo(new[] { new { Name = "Moe", Weapons = new[] { new { Name = "Bazooka" } } } }, options => options.ExcludingMissingMembers()); } }
public async Task TemporalQuery_Should_AllowAllTemporalEntitiesToTimeTravel() { var(appleId, unripeAppleTime, ripeAppleTime, rottenAppleTime) = await SimulateLifecycleOfMyApple(); // Validate current state of my apple var currentApple = await GetApple(appleId); currentApple .Should() .BeEquivalentTo( new { FruitStatus = FruitStatus.Fuzzy, Worms = new object[] { new { Name = "Hairy", Weapons = new[] { new { Name = "Holy Hand Grenade" }, new { Name = "Super Banana Bomb" } }, FriendsNames = new[] { "Joan", "Starr", "Curly" } }, new { Name = "Curly" }, new { Name = "Moe", Weapons = new[] { new { Name = "Bazooka Pie" } }, FriendsNames = new[] { "John" } } } }, options => options.ExcludingMissingMembers()); // Query the state of my apple a prior "system time" when the apple was rotten using (TemporalQuery.AsOf(rottenAppleTime)) { var rottenApple = await GetApple(appleId); rottenApple .Should() .BeEquivalentTo( new { FruitStatus = FruitStatus.Rotten, Worms = new object[] { new { Name = "Moe", Weapons = new[] { new { Name = "Bazooka" } }, FriendsNames = new[] { "John", "Ringo" } } } }, options => options.ExcludingMissingMembers()); } await Task.WhenAll(new[] { AssertWormlessFruitStatusAtTime(FruitStatus.Ripe, ripeAppleTime, appleId), AssertWormlessFruitStatusAtTime(FruitStatus.Unripe, unripeAppleTime, appleId), AssertWormsNavigationPropertyAtRottenAppleTime(rottenAppleTime, appleId) }); }