diff --git a/src/EFCore.InMemory/Storage/Internal/IInMemoryTable.cs b/src/EFCore.InMemory/Storage/Internal/IInMemoryTable.cs index 7e440a4640c..f997dcd6fff 100644 --- a/src/EFCore.InMemory/Storage/Internal/IInMemoryTable.cs +++ b/src/EFCore.InMemory/Storage/Internal/IInMemoryTable.cs @@ -25,6 +25,14 @@ public interface IInMemoryTable /// IReadOnlyList SnapshotRows(); + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IEnumerable Rows { get; } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -55,6 +63,31 @@ public interface IInMemoryTable /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - InMemoryIntegerValueGenerator GetIntegerValueGenerator([NotNull] IProperty property); + InMemoryIntegerValueGenerator GetIntegerValueGenerator( + [NotNull] IProperty property, [NotNull] IReadOnlyList tables); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + void BumpValueGenerators([NotNull] object[] row); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IInMemoryTable BaseTable { get; } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IEntityType EntityType { get; } } } diff --git a/src/EFCore.InMemory/Storage/Internal/IInMemoryTableFactory.cs b/src/EFCore.InMemory/Storage/Internal/IInMemoryTableFactory.cs index 71454b60927..3623c4fb417 100644 --- a/src/EFCore.InMemory/Storage/Internal/IInMemoryTableFactory.cs +++ b/src/EFCore.InMemory/Storage/Internal/IInMemoryTableFactory.cs @@ -20,6 +20,6 @@ public interface IInMemoryTableFactory /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - IInMemoryTable Create([NotNull] IEntityType entityType); + IInMemoryTable Create([NotNull] IEntityType entityType, [CanBeNull] IInMemoryTable baseTable); } } diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs index 99ccf60af63..6d33e033f03 100644 --- a/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs +++ b/src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs @@ -54,9 +54,10 @@ public virtual InMemoryIntegerValueGenerator GetIntegerValueGenerator lock (_lock) { var entityType = property.DeclaringEntityType; - var key = _useNameMatching ? (object)entityType.Name : entityType; - return EnsureTable(key, entityType).GetIntegerValueGenerator(property); + return EnsureTable(entityType).GetIntegerValueGenerator( + property, + entityType.GetDerivedTypesInclusive().Select(type => EnsureTable(type)).ToArray()); } } @@ -170,8 +171,7 @@ public virtual int ExecuteTransaction( Check.DebugAssert(!entityType.IsAbstract(), "entityType is abstract"); - var key = _useNameMatching ? (object)entityType.Name : entityType; - var table = EnsureTable(key, entityType); + var table = EnsureTable(entityType); if (entry.SharedIdentityEntry != null) { @@ -206,19 +206,29 @@ public virtual int ExecuteTransaction( } // Must be called from inside the lock - private IInMemoryTable EnsureTable(object key, IEntityType entityType) + private IInMemoryTable EnsureTable(IEntityType entityType) { if (_tables == null) { _tables = CreateTables(); } - if (!_tables.TryGetValue(key, out var table)) + IInMemoryTable baseTable = null; + + var entityTypes = entityType.GetAllBaseTypesInclusive(); + foreach (var currentEntityType in entityTypes) { - _tables.Add(key, table = _tableFactory.Create(entityType)); + var key = _useNameMatching ? (object)currentEntityType.Name : currentEntityType; + if (!_tables.TryGetValue(key, out var table)) + { + _tables.Add(key, table = _tableFactory.Create(currentEntityType, baseTable)); + } + + baseTable = table; } - return table; + + return _tables[_useNameMatching ? (object)entityType.Name : entityType]; } } } diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs index 49ade113b8b..cf787bf8fdc 100644 --- a/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs +++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs @@ -28,7 +28,6 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Storage.Internal public class InMemoryTable : IInMemoryTable { // WARNING: The in-memory provider is using EF internal code here. This should not be copied by other providers. See #15096 - private readonly IEntityType _entityType; private readonly IPrincipalKeyValueFactory _keyValueFactory; private readonly bool _sensitiveLoggingEnabled; private readonly Dictionary _rows; @@ -43,9 +42,10 @@ public class InMemoryTable : IInMemoryTable /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public InMemoryTable([NotNull] IEntityType entityType, bool sensitiveLoggingEnabled) + public InMemoryTable([NotNull] IEntityType entityType, [CanBeNull] IInMemoryTable baseTable, bool sensitiveLoggingEnabled) { - _entityType = entityType; + EntityType = entityType; + BaseTable = baseTable; // WARNING: The in-memory provider is using EF internal code here. This should not be copied by other providers. See #15096 _keyValueFactory = entityType.FindPrimaryKey().GetPrincipalKeyValueFactory(); _sensitiveLoggingEnabled = sensitiveLoggingEnabled; @@ -83,7 +83,24 @@ public InMemoryTable([NotNull] IEntityType entityType, bool sensitiveLoggingEnab /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual InMemoryIntegerValueGenerator GetIntegerValueGenerator(IProperty property) + public virtual IInMemoryTable BaseTable { get; } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual IEntityType EntityType { get; } + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual InMemoryIntegerValueGenerator GetIntegerValueGenerator( + IProperty property, IReadOnlyList tables) { if (_integerGenerators == null) { @@ -97,15 +114,26 @@ public virtual InMemoryIntegerValueGenerator GetIntegerValueGenerator generator = new InMemoryIntegerValueGenerator(propertyIndex); _integerGenerators[propertyIndex] = generator; - foreach (var row in _rows.Values) + foreach (var table in tables) { - generator.Bump(row); + foreach (var row in table.Rows) + { + generator.Bump(row); + } } } return (InMemoryIntegerValueGenerator)generator; } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual IEnumerable Rows => _rows.Values; + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -116,7 +144,7 @@ public virtual IReadOnlyList SnapshotRows() { var rows = _rows.Values.ToList(); var rowCount = rows.Count; - var properties = _entityType.GetProperties().ToList(); + var properties = EntityType.GetProperties().ToList(); var propertyCount = properties.Count; for (var rowIndex = 0; rowIndex < rowCount; rowIndex++) @@ -266,8 +294,13 @@ public virtual void Update(IUpdateEntry entry) } } - private void BumpValueGenerators(object[] row) + public virtual void BumpValueGenerators(object[] row) { + if (BaseTable != null) + { + BaseTable.BumpValueGenerators(row); + } + if (_integerGenerators != null) { foreach (var generator in _integerGenerators.Values) diff --git a/src/EFCore.InMemory/Storage/Internal/InMemoryTableFactory.cs b/src/EFCore.InMemory/Storage/Internal/InMemoryTableFactory.cs index c2e01b8825e..2fc3c8ec7b4 100644 --- a/src/EFCore.InMemory/Storage/Internal/InMemoryTableFactory.cs +++ b/src/EFCore.InMemory/Storage/Internal/InMemoryTableFactory.cs @@ -24,8 +24,8 @@ public class InMemoryTableFactory { private readonly bool _sensitiveLoggingEnabled; - private readonly ConcurrentDictionary> _factories - = new ConcurrentDictionary>(); + private readonly ConcurrentDictionary<(IEntityType EntityType, IInMemoryTable BaseTable), Func> _factories + = new ConcurrentDictionary<(IEntityType, IInMemoryTable), Func>(); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -46,17 +46,18 @@ public InMemoryTableFactory([NotNull] ILoggingOptions loggingOptions) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual IInMemoryTable Create(IEntityType entityType) - => _factories.GetOrAdd(entityType, CreateTable)(); + public virtual IInMemoryTable Create(IEntityType entityType, IInMemoryTable baseTable) + => _factories.GetOrAdd((entityType, baseTable), e => CreateTable(e.EntityType, e.BaseTable))(); - private Func CreateTable([NotNull] IEntityType entityType) + private Func CreateTable([NotNull] IEntityType entityType, IInMemoryTable baseTable) => (Func)typeof(InMemoryTableFactory).GetTypeInfo() .GetDeclaredMethod(nameof(CreateFactory)) .MakeGenericMethod(GetKeyType(entityType.FindPrimaryKey())) - .Invoke(null, new object[] { entityType, _sensitiveLoggingEnabled }); + .Invoke(null, new object[] { entityType, baseTable, _sensitiveLoggingEnabled }); [UsedImplicitly] - private static Func CreateFactory(IEntityType entityType, bool sensitiveLoggingEnabled) - => () => new InMemoryTable(entityType, sensitiveLoggingEnabled); + private static Func CreateFactory( + IEntityType entityType, IInMemoryTable baseTable, bool sensitiveLoggingEnabled) + => () => new InMemoryTable(entityType, baseTable, sensitiveLoggingEnabled); } } diff --git a/test/EFCore.InMemory.FunctionalTests/IntegerValueGeneratorTest.cs b/test/EFCore.InMemory.FunctionalTests/IntegerValueGeneratorTest.cs index ed3f8991265..f0acdd06a30 100644 --- a/test/EFCore.InMemory.FunctionalTests/IntegerValueGeneratorTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/IntegerValueGeneratorTest.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; +using System.Linq; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -14,34 +15,34 @@ public class IntegerValueGeneratorTest [ConditionalFact] public void Each_property_gets_its_own_generator() { - var olives = new Olive[4]; + var macs = new Mac[4]; var toasts = new Toast[4]; using (var context = new PetsContext("Dance")) { - olives[0] = context.Add(new Olive()).Entity; + macs[0] = context.Add(new Mac()).Entity; toasts[0] = context.Add(new Toast()).Entity; - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); - olives[1] = context.Add(new Olive()).Entity; + macs[1] = context.Add(new Mac()).Entity; toasts[1] = context.Add(new Toast()).Entity; - Assert.Equal(2, olives[1].Id); + Assert.Equal(2, macs[1].Id); Assert.Equal(2, toasts[1].Id); context.SaveChanges(); - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); - Assert.Equal(2, olives[1].Id); + Assert.Equal(2, macs[1].Id); Assert.Equal(2, toasts[1].Id); - olives[2] = context.Add(new Olive()).Entity; + macs[2] = context.Add(new Mac()).Entity; toasts[2] = context.Add(new Toast()).Entity; - Assert.Equal(3, olives[2].Id); + Assert.Equal(3, macs[2].Id); Assert.Equal(3, toasts[2].Id); context.SaveChanges(); @@ -49,23 +50,127 @@ public void Each_property_gets_its_own_generator() using (var context = new PetsContext("Dance")) { - olives[3] = context.Add(new Olive()).Entity; + macs[3] = context.Add(new Mac()).Entity; toasts[3] = context.Add(new Toast()).Entity; - Assert.Equal(4, olives[3].Id); + Assert.Equal(4, macs[3].Id); Assert.Equal(4, toasts[3].Id); context.SaveChanges(); } - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); - Assert.Equal(2, olives[1].Id); + Assert.Equal(2, macs[1].Id); Assert.Equal(2, toasts[1].Id); - Assert.Equal(3, olives[2].Id); + Assert.Equal(3, macs[2].Id); Assert.Equal(3, toasts[2].Id); - Assert.Equal(4, olives[3].Id); + Assert.Equal(4, macs[3].Id); Assert.Equal(4, toasts[3].Id); + + using (var context = new PetsContext("Dance")) + { + macs = context.Macs.OrderBy(e => e.Id).ToArray(); + toasts = context.CookedBreads.OrderBy(e => e.Id).ToArray(); + } + + Assert.Equal(1, macs[0].Id); + Assert.Equal(1, toasts[0].Id); + Assert.Equal(2, macs[1].Id); + Assert.Equal(2, toasts[1].Id); + Assert.Equal(3, macs[2].Id); + Assert.Equal(3, toasts[2].Id); + Assert.Equal(4, macs[3].Id); + Assert.Equal(4, toasts[3].Id); + } + + [ConditionalFact] + public void Each_property_gets_its_own_generator_with_seeding() + { + var macs = new Mac[4]; + var toasts = new Toast[4]; + + using (var context = new PetsContextWithData("Pets II")) + { + context.Database.EnsureCreated(); + + var savedMacs = context.Macs.OrderBy(e => e.Id).ToList(); + var savedToasts = context.CookedBreads.OrderBy(e => e.Id).ToList(); + + Assert.Equal(2, savedMacs.Count); + Assert.Single(savedToasts); + + Assert.Equal(1, savedMacs[0].Id); + Assert.Equal(2, savedMacs[1].Id); + Assert.Equal(1, savedToasts[0].Id); + } + + using (var context = new PetsContextWithData("Pets II")) + { + macs[0] = context.Add(new Mac()).Entity; + toasts[0] = context.Add(new Toast()).Entity; + + Assert.Equal(3, macs[0].Id); + Assert.Equal(2, toasts[0].Id); + + macs[1] = context.Add(new Mac()).Entity; + toasts[1] = context.Add(new Toast()).Entity; + + Assert.Equal(4, macs[1].Id); + Assert.Equal(3, toasts[1].Id); + + context.SaveChanges(); + + Assert.Equal(3, macs[0].Id); + Assert.Equal(2, toasts[0].Id); + Assert.Equal(4, macs[1].Id); + Assert.Equal(3, toasts[1].Id); + + macs[2] = context.Add(new Mac()).Entity; + toasts[2] = context.Add(new Toast()).Entity; + + Assert.Equal(5, macs[2].Id); + Assert.Equal(4, toasts[2].Id); + + context.SaveChanges(); + } + + using (var context = new PetsContextWithData("Pets II")) + { + macs[3] = context.Add(new Mac()).Entity; + toasts[3] = context.Add(new Toast()).Entity; + + Assert.Equal(6, macs[3].Id); + Assert.Equal(5, toasts[3].Id); + + context.SaveChanges(); + } + + Assert.Equal(3, macs[0].Id); + Assert.Equal(2, toasts[0].Id); + Assert.Equal(4, macs[1].Id); + Assert.Equal(3, toasts[1].Id); + Assert.Equal(5, macs[2].Id); + Assert.Equal(4, toasts[2].Id); + Assert.Equal(6, macs[3].Id); + Assert.Equal(5, toasts[3].Id); + + using (var context = new PetsContextWithData("Pets II")) + { + var savedMacs = context.Macs.OrderBy(e => e.Id).ToList(); + var savedToasts = context.CookedBreads.OrderBy(e => e.Id).ToList(); + + Assert.Equal(6, savedMacs.Count); + Assert.Equal(5, savedToasts.Count); + + for (var i = 0; i < 5; i++) + { + Assert.Equal(i + 1, savedMacs[i].Id); + Assert.Equal(i + 1, savedToasts[i].Id); + } + + Assert.Equal(6, savedMacs[5].Id); + } } [ConditionalFact] @@ -81,15 +186,15 @@ public void Generators_are_associated_with_database_root() var root = new InMemoryDatabaseRoot(); - var olives = new Olive[2]; + var macs = new Mac[2]; var toasts = new Toast[2]; using (var context = new PetsContext("Drink", root, serviceProvider1)) { - olives[0] = context.Add(new Olive()).Entity; + macs[0] = context.Add(new Mac()).Entity; toasts[0] = context.Add(new Toast()).Entity; - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); context.SaveChanges(); @@ -97,71 +202,71 @@ public void Generators_are_associated_with_database_root() using (var context = new PetsContext("Drink", root, serviceProvider2)) { - olives[1] = context.Add(new Olive()).Entity; + macs[1] = context.Add(new Mac()).Entity; toasts[1] = context.Add(new Toast()).Entity; - Assert.Equal(2, olives[1].Id); + Assert.Equal(2, macs[1].Id); Assert.Equal(2, toasts[1].Id); context.SaveChanges(); } - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); - Assert.Equal(2, olives[1].Id); + Assert.Equal(2, macs[1].Id); Assert.Equal(2, toasts[1].Id); } [ConditionalFact] public void Mixing_explicit_values_with_generated_values_with_care_works() { - var olives = new Olive[4]; + var macs = new Mac[4]; var toasts = new Toast[4]; using var context = new PetsContext("Wercs"); - olives[0] = context.Add(new Olive { Id = 10 }).Entity; + macs[0] = context.Add(new Mac { Id = 10 }).Entity; toasts[0] = context.Add(new Toast { Id = 100 }).Entity; context.SaveChanges(); - olives[1] = context.Add(new Olive()).Entity; + macs[1] = context.Add(new Mac()).Entity; toasts[1] = context.Add(new Toast()).Entity; context.SaveChanges(); - Assert.Equal(10, olives[0].Id); + Assert.Equal(10, macs[0].Id); Assert.Equal(100, toasts[0].Id); - Assert.Equal(11, olives[1].Id); + Assert.Equal(11, macs[1].Id); Assert.Equal(101, toasts[1].Id); - olives[2] = context.Add(new Olive { Id = 20 }).Entity; + macs[2] = context.Add(new Mac { Id = 20 }).Entity; toasts[2] = context.Add(new Toast { Id = 200 }).Entity; context.SaveChanges(); - olives[3] = context.Add(new Olive()).Entity; + macs[3] = context.Add(new Mac()).Entity; toasts[3] = context.Add(new Toast()).Entity; context.SaveChanges(); - Assert.Equal(20, olives[2].Id); + Assert.Equal(20, macs[2].Id); Assert.Equal(200, toasts[2].Id); - Assert.Equal(21, olives[3].Id); + Assert.Equal(21, macs[3].Id); Assert.Equal(201, toasts[3].Id); } [ConditionalFact] public void Each_database_gets_its_own_generators() { - var olives = new List(); + var macs = new List(); var toasts = new List(); using (var context = new PetsContext("Nothing")) { - olives.Add(context.Add(new Olive()).Entity); + macs.Add(context.Add(new Mac()).Entity); toasts.Add(context.Add(new Toast()).Entity); - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); context.SaveChanges(); @@ -169,33 +274,33 @@ public void Each_database_gets_its_own_generators() using (var context = new PetsContext("Else")) { - olives.Add(context.Add(new Olive()).Entity); + macs.Add(context.Add(new Mac()).Entity); toasts.Add(context.Add(new Toast()).Entity); - Assert.Equal(1, olives[1].Id); + Assert.Equal(1, macs[1].Id); Assert.Equal(1, toasts[1].Id); context.SaveChanges(); } - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); - Assert.Equal(1, olives[1].Id); + Assert.Equal(1, macs[1].Id); Assert.Equal(1, toasts[1].Id); } [ConditionalFact] public void Each_root_gets_its_own_generators() { - var olives = new List(); + var macs = new List(); var toasts = new List(); using (var context = new PetsContext("To", new InMemoryDatabaseRoot())) { - olives.Add(context.Add(new Olive()).Entity); + macs.Add(context.Add(new Mac()).Entity); toasts.Add(context.Add(new Toast()).Entity); - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); context.SaveChanges(); @@ -203,33 +308,33 @@ public void Each_root_gets_its_own_generators() using (var context = new PetsContext("To", new InMemoryDatabaseRoot())) { - olives.Add(context.Add(new Olive()).Entity); + macs.Add(context.Add(new Mac()).Entity); toasts.Add(context.Add(new Toast()).Entity); - Assert.Equal(1, olives[1].Id); + Assert.Equal(1, macs[1].Id); Assert.Equal(1, toasts[1].Id); context.SaveChanges(); } - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); - Assert.Equal(1, olives[1].Id); + Assert.Equal(1, macs[1].Id); Assert.Equal(1, toasts[1].Id); } [ConditionalFact] public void EnsureDeleted_resets_generators() { - var olives = new List(); + var macs = new List(); var toasts = new List(); using (var context = new PetsContext("Do")) { - olives.Add(context.Add(new Olive()).Entity); + macs.Add(context.Add(new Mac()).Entity); toasts.Add(context.Add(new Toast()).Entity); - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); context.SaveChanges(); @@ -239,18 +344,18 @@ public void EnsureDeleted_resets_generators() { context.Database.EnsureDeleted(); - olives.Add(context.Add(new Olive()).Entity); + macs.Add(context.Add(new Mac()).Entity); toasts.Add(context.Add(new Toast()).Entity); - Assert.Equal(1, olives[1].Id); + Assert.Equal(1, macs[1].Id); Assert.Equal(1, toasts[1].Id); context.SaveChanges(); } - Assert.Equal(1, olives[0].Id); + Assert.Equal(1, macs[0].Id); Assert.Equal(1, toasts[0].Id); - Assert.Equal(1, olives[1].Id); + Assert.Equal(1, macs[1].Id); Assert.Equal(1, toasts[1].Id); } @@ -284,18 +389,66 @@ protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) } } + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity(); + modelBuilder.Entity(); + } + public DbSet CookedBreads { get; set; } public DbSet Olives { get; set; } + public DbSet Macs { get; set; } + public DbSet Smokeys { get; set; } + public DbSet Alices { get; set; } } - private class Toast + private class PetsContextWithData : PetsContext + { + public PetsContextWithData( + string databaseName, + InMemoryDatabaseRoot root = null, + IServiceProvider internalServiceProvider = null) + : base(databaseName, root, internalServiceProvider) + { + } + + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + modelBuilder.Entity().HasData(new Toast { Id = 1 }); + modelBuilder.Entity().HasData(new Mac { Id = 1 }, new Mac { Id = 2 }); + } + } + + private class Dog { public int Id { get; set; } } - private class Olive + private class Toast : Dog + { + } + + private class Olive : Dog + { + } + + private class Cat { public int Id { get; set; } } + + private class Mac: Cat + { + } + + private class Smokey: Cat + { + } + + private class Alice: Cat + { + } } }