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

Force use of common value generator for all derived types #20205

Merged
merged 1 commit into from
Mar 19, 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
35 changes: 34 additions & 1 deletion src/EFCore.InMemory/Storage/Internal/IInMemoryTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ public interface IInMemoryTable
/// </summary>
IReadOnlyList<object[]> SnapshotRows();

/// <summary>
/// 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.
/// </summary>
IEnumerable<object[]> Rows { get; }

/// <summary>
/// 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
Expand Down Expand Up @@ -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.
/// </summary>
InMemoryIntegerValueGenerator<TProperty> GetIntegerValueGenerator<TProperty>([NotNull] IProperty property);
InMemoryIntegerValueGenerator<TProperty> GetIntegerValueGenerator<TProperty>(
[NotNull] IProperty property, [NotNull] IReadOnlyList<IInMemoryTable> tables);

/// <summary>
/// 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.
/// </summary>
void BumpValueGenerators([NotNull] object[] row);

/// <summary>
/// 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.
/// </summary>
IInMemoryTable BaseTable { get; }

/// <summary>
/// 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.
/// </summary>
IEntityType EntityType { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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.
/// </summary>
IInMemoryTable Create([NotNull] IEntityType entityType);
IInMemoryTable Create([NotNull] IEntityType entityType, [CanBeNull] IInMemoryTable baseTable);
}
}
26 changes: 18 additions & 8 deletions src/EFCore.InMemory/Storage/Internal/InMemoryStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,10 @@ public virtual InMemoryIntegerValueGenerator<TProperty> GetIntegerValueGenerator
lock (_lock)
{
var entityType = property.DeclaringEntityType;
var key = _useNameMatching ? (object)entityType.Name : entityType;

return EnsureTable(key, entityType).GetIntegerValueGenerator<TProperty>(property);
return EnsureTable(entityType).GetIntegerValueGenerator<TProperty>(
property,
entityType.GetDerivedTypesInclusive().Select(type => EnsureTable(type)).ToArray());
}
}

Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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];
}
}
}
49 changes: 41 additions & 8 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,6 @@ namespace Microsoft.EntityFrameworkCore.InMemory.Storage.Internal
public class InMemoryTable<TKey> : 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<TKey> _keyValueFactory;
private readonly bool _sensitiveLoggingEnabled;
private readonly Dictionary<TKey, object[]> _rows;
Expand All @@ -43,9 +42,10 @@ public class InMemoryTable<TKey> : 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.
/// </summary>
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<TKey>();
_sensitiveLoggingEnabled = sensitiveLoggingEnabled;
Expand Down Expand Up @@ -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.
/// </summary>
public virtual InMemoryIntegerValueGenerator<TProperty> GetIntegerValueGenerator<TProperty>(IProperty property)
public virtual IInMemoryTable BaseTable { get; }

/// <summary>
/// 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.
/// </summary>
public virtual IEntityType EntityType { get; }

/// <summary>
/// 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.
/// </summary>
public virtual InMemoryIntegerValueGenerator<TProperty> GetIntegerValueGenerator<TProperty>(
IProperty property, IReadOnlyList<IInMemoryTable> tables)
{
if (_integerGenerators == null)
{
Expand All @@ -97,15 +114,26 @@ public virtual InMemoryIntegerValueGenerator<TProperty> GetIntegerValueGenerator
generator = new InMemoryIntegerValueGenerator<TProperty>(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<TProperty>)generator;
}

/// <summary>
/// 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.
/// </summary>
public virtual IEnumerable<object[]> Rows => _rows.Values;

/// <summary>
/// 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
Expand All @@ -116,7 +144,7 @@ public virtual IReadOnlyList<object[]> 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++)
Expand Down Expand Up @@ -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)
Expand Down
17 changes: 9 additions & 8 deletions src/EFCore.InMemory/Storage/Internal/InMemoryTableFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ public class InMemoryTableFactory
{
private readonly bool _sensitiveLoggingEnabled;

private readonly ConcurrentDictionary<IEntityType, Func<IInMemoryTable>> _factories
= new ConcurrentDictionary<IEntityType, Func<IInMemoryTable>>();
private readonly ConcurrentDictionary<(IEntityType EntityType, IInMemoryTable BaseTable), Func<IInMemoryTable>> _factories
= new ConcurrentDictionary<(IEntityType, IInMemoryTable), Func<IInMemoryTable>>();

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -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.
/// </summary>
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<IInMemoryTable> CreateTable([NotNull] IEntityType entityType)
private Func<IInMemoryTable> CreateTable([NotNull] IEntityType entityType, IInMemoryTable baseTable)
=> (Func<IInMemoryTable>)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<IInMemoryTable> CreateFactory<TKey>(IEntityType entityType, bool sensitiveLoggingEnabled)
=> () => new InMemoryTable<TKey>(entityType, sensitiveLoggingEnabled);
private static Func<IInMemoryTable> CreateFactory<TKey>(
IEntityType entityType, IInMemoryTable baseTable, bool sensitiveLoggingEnabled)
=> () => new InMemoryTable<TKey>(entityType, baseTable, sensitiveLoggingEnabled);
}
}
Loading