CreateSharedTableEntryMapFactories( [NotNull] IModel model, [NotNull] IUpdateAdapter updateAdapter) { var tables = new Dictionary <(string Schema, string TableName), List <IEntityType> >(); foreach (var entityType in model.GetEntityTypes().Where(et => et.FindPrimaryKey() != null)) { var fullName = (entityType.Relational().Schema, entityType.Relational().TableName); if (!tables.TryGetValue(fullName, out var mappedEntityTypes)) { mappedEntityTypes = new List <IEntityType>(); tables.Add(fullName, mappedEntityTypes); } mappedEntityTypes.Add(entityType); } var sharedTablesMap = new Dictionary <(string Schema, string Name), SharedTableEntryMapFactory <TValue> >(); foreach (var tableMapping in tables) { if (tableMapping.Value.Count <= 1) { continue; } var factory = CreateSharedTableEntryMapFactory(tableMapping.Value, updateAdapter, tableMapping.Key.TableName, tableMapping.Key.Schema); sharedTablesMap.Add(tableMapping.Key, factory); } return(sharedTablesMap); }
public ICommandBatchPreparer CreateCommandBatchPreparer( IModificationCommandBatchFactory modificationCommandBatchFactory = null, IUpdateAdapter updateAdapter = null, bool sensitiveLogging = false) { modificationCommandBatchFactory ??= RelationalTestHelpers.Instance.CreateContextServices().GetRequiredService <IModificationCommandBatchFactory>(); var loggingOptions = new LoggingOptions(); if (sensitiveLogging) { loggingOptions.Initialize(new DbContextOptionsBuilder <DbContext>().EnableSensitiveDataLogging().Options); } return(new CommandBatchPreparer( new CommandBatchPreparerDependencies( modificationCommandBatchFactory, new ParameterNameGeneratorFactory(new ParameterNameGeneratorDependencies()), new ModificationCommandComparer(), new KeyValueIndexFactorySource(), new ModificationCommandFactory(), loggingOptions, new FakeDiagnosticsLogger <DbLoggerCategory.Update>(), new DbContextOptionsBuilder().Options))); }
/// <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 static SharedTableEntryMapFactory <TValue> CreateSharedTableEntryMapFactory( [NotNull] ITable table, [NotNull] IUpdateAdapter updateAdapter) => createElement => new SharedTableEntryMap <TValue>( table, updateAdapter, createElement);
public List <ModificationCommandBatch> CreateBatches( IUpdateEntry[] entries, IUpdateAdapter updateAdapter, bool sensitiveLogging = false) => CreateCommandBatchPreparer(updateAdapter: updateAdapter, sensitiveLogging: sensitiveLogging) .BatchCommands(entries, updateAdapter) .Select(t => t.Batch) .ToList();
/// <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 SharedTableEntryMap( [NotNull] ITable table, [NotNull] IUpdateAdapter updateAdapter) { _table = table; _updateAdapter = updateAdapter; _comparer = new EntryComparer(table); }
/// <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 SharedTableEntryMap( [NotNull] ITable table, [NotNull] IUpdateAdapter updateAdapter, [NotNull] SharedTableEntryValueFactory <TValue> createElement) { _table = table; _updateAdapter = updateAdapter; _createElement = createElement; _comparer = new EntryComparer(table); }
/// <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 static SharedTableEntryMapFactory <TValue> CreateSharedTableEntryMapFactory( [NotNull] IReadOnlyList <IEntityType> entityTypes, [NotNull] IUpdateAdapter updateAdapter, [NotNull] string tableName, [NotNull] string schema) { var principals = new Dictionary <IEntityType, IReadOnlyList <IEntityType> >(entityTypes.Count); var dependents = new Dictionary <IEntityType, IReadOnlyList <IEntityType> >(entityTypes.Count); foreach (var entityType in entityTypes.Where(t => t.FindPrimaryKey() != null)) { var principalList = new List <IEntityType>(); foreach (var foreignKey in entityType.FindForeignKeys(entityType.FindPrimaryKey().Properties)) { if (foreignKey.PrincipalKey.IsPrimaryKey() && entityTypes.Contains(foreignKey.PrincipalEntityType) && !foreignKey.IsIntraHierarchical()) { principalList.Add(foreignKey.PrincipalEntityType); } } principals[entityType] = principalList; var dependentList = new List <IEntityType>(); foreach (var referencingForeignKey in entityType.FindPrimaryKey().GetReferencingForeignKeys()) { if (referencingForeignKey.PrincipalEntityType.IsAssignableFrom(entityType) && entityTypes.Contains(referencingForeignKey.DeclaringEntityType) && !referencingForeignKey.IsIntraHierarchical() && (PropertyListComparer.Instance.Compare( referencingForeignKey.DeclaringEntityType.FindPrimaryKey().Properties, referencingForeignKey.Properties) == 0)) { dependentList.Add(referencingForeignKey.DeclaringEntityType); } } dependents[entityType] = dependentList; } return(createElement => new SharedTableEntryMap <TValue>( updateAdapter, principals, dependents, tableName, schema, createElement)); }
/// <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 SharedTableEntryMap( [NotNull] IUpdateAdapter updateAdapter, [NotNull] IReadOnlyDictionary <IEntityType, IReadOnlyList <IEntityType> > principals, [NotNull] IReadOnlyDictionary <IEntityType, IReadOnlyList <IEntityType> > dependents, [NotNull] string name, [CanBeNull] string schema, [NotNull] SharedTableEntryValueFactory <TValue> createElement) { _updateAdapter = updateAdapter; _principals = principals; _dependents = dependents; _name = name; _schema = schema; _createElement = createElement; _comparer = new EntryComparer(principals); }
CreateSharedTableEntryMapFactories( [NotNull] IModel model, [NotNull] IUpdateAdapter updateAdapter) { var sharedTablesMap = new Dictionary <(string, string), SharedTableEntryMapFactory <TValue> >(); foreach (var table in model.GetRelationalModel().Tables) { if (!table.IsSplit) { continue; } var factory = CreateSharedTableEntryMapFactory(table, updateAdapter); sharedTablesMap.Add((table.Name, table.Schema), factory); } return(sharedTablesMap); }
/// <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 <ModificationCommandBatch> BatchCommands( IList <IUpdateEntry> entries, IUpdateAdapter updateAdapter) { var parameterNameGenerator = _parameterNameGeneratorFactory.Create(); var commands = CreateModificationCommands(entries, updateAdapter, parameterNameGenerator.GenerateNext); var sortedCommandSets = TopologicalSort(commands); // TODO: Enable batching of dependent commands by passing through the dependency graph foreach (var independentCommandSet in sortedCommandSets) { independentCommandSet.Sort(_modificationCommandComparer); var batch = _modificationCommandBatchFactory.Create(); foreach (var modificationCommand in independentCommandSet) { if (!batch.AddCommand(modificationCommand)) { if (batch.ModificationCommands.Count == 1 || batch.ModificationCommands.Count >= _minBatchSize) { if (batch.ModificationCommands.Count > 1) { Dependencies.UpdateLogger.BatchReadyForExecution( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count); } yield return(batch); } else { Dependencies.UpdateLogger.BatchSmallerThanMinBatchSize( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count, _minBatchSize); foreach (var command in batch.ModificationCommands) { yield return(StartNewBatch(parameterNameGenerator, command)); } } batch = StartNewBatch(parameterNameGenerator, modificationCommand); } } if (batch.ModificationCommands.Count == 1 || batch.ModificationCommands.Count >= _minBatchSize) { if (batch.ModificationCommands.Count > 1) { Dependencies.UpdateLogger.BatchReadyForExecution( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count); } yield return(batch); } else { Dependencies.UpdateLogger.BatchSmallerThanMinBatchSize( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count, _minBatchSize); foreach (var command in batch.ModificationCommands) { yield return(StartNewBatch(parameterNameGenerator, command)); } } } }
/// <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> protected virtual IEnumerable <ModificationCommand> CreateModificationCommands( [NotNull] IList <IUpdateEntry> entries, [NotNull] IUpdateAdapter updateAdapter, [NotNull] Func <string> generateParameterName) { var commands = new List <ModificationCommand>(); if (_sharedTableEntryMapFactories == null) { _sharedTableEntryMapFactories = SharedTableEntryMap <ModificationCommand> .CreateSharedTableEntryMapFactories(updateAdapter.Model, updateAdapter); } Dictionary <(string Name, string Schema), SharedTableEntryMap <ModificationCommand> > sharedTablesCommandsMap = null; foreach (var entry in entries) { if (entry.SharedIdentityEntry != null && entry.EntityState == EntityState.Deleted) { continue; } var entityType = entry.EntityType; var table = entityType.GetTableName(); var schema = entityType.GetSchema(); var tableKey = (table, schema); ModificationCommand command; var isMainEntry = true; if (_sharedTableEntryMapFactories.TryGetValue(tableKey, out var commandIdentityMapFactory)) { if (sharedTablesCommandsMap == null) { sharedTablesCommandsMap = new Dictionary <(string, string), SharedTableEntryMap <ModificationCommand> >(); } if (!sharedTablesCommandsMap.TryGetValue(tableKey, out var sharedCommandsMap)) { sharedCommandsMap = commandIdentityMapFactory( (n, s, c) => new ModificationCommand( n, s, generateParameterName, _sensitiveLoggingEnabled, c)); sharedTablesCommandsMap.Add(tableKey, sharedCommandsMap); } command = sharedCommandsMap.GetOrAddValue(entry); isMainEntry = sharedCommandsMap.IsMainEntityType(entry.EntityType.GetRootType()); } else { command = new ModificationCommand( table, schema, generateParameterName, _sensitiveLoggingEnabled, comparer: null); } command.AddEntry(entry, isMainEntry); commands.Add(command); } if (sharedTablesCommandsMap != null) { AddUnchangedSharingEntries(sharedTablesCommandsMap.Values, entries); } return(commands.Where( c => c.EntityState != EntityState.Modified || c.ColumnModifications.Any(m => m.IsWrite))); }
/// <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 <ModificationCommandBatch> BatchCommands( IList <IUpdateEntry> entries, IUpdateAdapter updateAdapter) { var parameterNameGenerator = _parameterNameGeneratorFactory.Create(); var commands = CreateModificationCommands(entries, updateAdapter, parameterNameGenerator.GenerateNext); var sortedCommandSets = TopologicalSort(commands); foreach (var independentCommandSet in sortedCommandSets) { independentCommandSet.Sort(_modificationCommandComparer); var batch = _modificationCommandBatchFactory.Create(); foreach (var modificationCommand in independentCommandSet) { modificationCommand.AssertColumnsNotInitialized(); if (modificationCommand.EntityState == EntityState.Modified && !modificationCommand.ColumnModifications.Any(m => m.IsWrite)) { continue; } if (!batch.AddCommand(modificationCommand)) { if (batch.ModificationCommands.Count == 1 || batch.ModificationCommands.Count >= _minBatchSize) { if (batch.ModificationCommands.Count > 1) { Dependencies.UpdateLogger.BatchReadyForExecution( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count); } yield return(batch); } else { Dependencies.UpdateLogger.BatchSmallerThanMinBatchSize( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count, _minBatchSize); foreach (var command in batch.ModificationCommands) { yield return(StartNewBatch(parameterNameGenerator, command)); } } batch = StartNewBatch(parameterNameGenerator, modificationCommand); } } if (batch.ModificationCommands.Count == 1 || batch.ModificationCommands.Count >= _minBatchSize) { if (batch.ModificationCommands.Count > 1) { Dependencies.UpdateLogger.BatchReadyForExecution( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count); } yield return(batch); } else { Dependencies.UpdateLogger.BatchSmallerThanMinBatchSize( batch.ModificationCommands.SelectMany(c => c.Entries), batch.ModificationCommands.Count, _minBatchSize); foreach (var command in batch.ModificationCommands) { yield return(StartNewBatch(parameterNameGenerator, command)); } } } }
/// <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> protected virtual IEnumerable <ModificationCommand> CreateModificationCommands( IList <IUpdateEntry> entries, IUpdateAdapter updateAdapter, Func <string> generateParameterName) { var commands = new List <ModificationCommand>(); Dictionary <(string Name, string?Schema), SharedTableEntryMap <ModificationCommand> >?sharedTablesCommandsMap = null; foreach (var entry in entries) { if (entry.SharedIdentityEntry != null && entry.EntityState == EntityState.Deleted) { continue; } var mappings = (IReadOnlyCollection <ITableMapping>)entry.EntityType.GetTableMappings(); var mappingCount = mappings.Count; ModificationCommand?firstCommand = null; foreach (var mapping in mappings) { var table = mapping.Table; var tableKey = (table.Name, table.Schema); ModificationCommand command; var isMainEntry = true; if (table.IsShared) { if (sharedTablesCommandsMap == null) { sharedTablesCommandsMap = new Dictionary <(string, string?), SharedTableEntryMap <ModificationCommand> >(); } if (!sharedTablesCommandsMap.TryGetValue(tableKey, out var sharedCommandsMap)) { sharedCommandsMap = new SharedTableEntryMap <ModificationCommand>(table, updateAdapter); sharedTablesCommandsMap.Add(tableKey, sharedCommandsMap); } command = sharedCommandsMap.GetOrAddValue( entry, (n, s, c) => new ModificationCommand(n, s, generateParameterName, _sensitiveLoggingEnabled, c, Dependencies.UpdateLogger)); isMainEntry = sharedCommandsMap.IsMainEntry(entry); } else { command = new ModificationCommand( table.Name, table.Schema, generateParameterName, _sensitiveLoggingEnabled, comparer: null, Dependencies.UpdateLogger); } command.AddEntry(entry, isMainEntry); commands.Add(command); if (firstCommand == null) { Check.DebugAssert(firstCommand == null, "firstCommand == null"); firstCommand = command; } } if (firstCommand == null) { throw new InvalidOperationException(RelationalStrings.ReadonlyEntitySaved(entry.EntityType.DisplayName())); } } if (sharedTablesCommandsMap != null) { AddUnchangedSharingEntries(sharedTablesCommandsMap.Values, entries); } return(commands); }
/// <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> protected virtual IEnumerable <ModificationCommand> CreateModificationCommands( [NotNull] IList <IUpdateEntry> entries, [NotNull] IUpdateAdapter modelData, [NotNull] Func <string> generateParameterName) { var commands = new List <ModificationCommand>(); if (_sharedTableEntryMapFactories == null) { _sharedTableEntryMapFactories = SharedTableEntryMap <ModificationCommand> .CreateSharedTableEntryMapFactories(modelData.Model, modelData); } Dictionary <(string Schema, string Name), SharedTableEntryMap <ModificationCommand> > sharedTablesCommandsMap = null; foreach (var entry in entries) { if (entry.SharedIdentityEntry != null && entry.EntityState == EntityState.Deleted) { continue; } var entityType = entry.EntityType; var relationalExtensions = entityType.Relational(); var table = relationalExtensions.TableName; var schema = relationalExtensions.Schema; var tableKey = (schema, table); ModificationCommand command; if (_sharedTableEntryMapFactories.TryGetValue(tableKey, out var commandIdentityMapFactory)) { if (sharedTablesCommandsMap == null) { sharedTablesCommandsMap = new Dictionary <(string Schema, string Name), SharedTableEntryMap <ModificationCommand> >(); } if (!sharedTablesCommandsMap.TryGetValue(tableKey, out var sharedCommandsMap)) { sharedCommandsMap = commandIdentityMapFactory( (t, s, c) => new ModificationCommand( t, s, generateParameterName, _sensitiveLoggingEnabled, c)); sharedTablesCommandsMap.Add((schema, table), sharedCommandsMap); } command = sharedCommandsMap.GetOrAddValue(entry); } else { command = new ModificationCommand( table, schema, generateParameterName, _sensitiveLoggingEnabled, comparer: null); } command.AddEntry(entry); commands.Add(command); } if (sharedTablesCommandsMap != null) { Validate(sharedTablesCommandsMap); AddUnchangedSharingEntries(sharedTablesCommandsMap, entries); } return(commands.Where( c => c.EntityState != EntityState.Modified || c.ColumnModifications.Any(m => m.IsWrite))); }
/// <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> protected virtual IEnumerable <ModificationCommand> CreateModificationCommands( [NotNull] IList <IUpdateEntry> entries, [NotNull] IUpdateAdapter updateAdapter, [NotNull] Func <string> generateParameterName) { var commands = new List <ModificationCommand>(); if (_sharedTableEntryMapFactories == null) { _sharedTableEntryMapFactories = SharedTableEntryMap <ModificationCommand> .CreateSharedTableEntryMapFactories(updateAdapter.Model, updateAdapter); } Dictionary <(string Name, string Schema), SharedTableEntryMap <ModificationCommand> > sharedTablesCommandsMap = null; foreach (var entry in entries) { if (entry.SharedIdentityEntry != null && entry.EntityState == EntityState.Deleted) { continue; } var mappings = (IReadOnlyCollection <ITableMapping>)entry.EntityType.GetTableMappings(); var mappingCount = mappings.Count; ModificationCommand mainCommand = null; var relatedCommands = mappingCount > 1 ? new List <ModificationCommand>(mappingCount - 1) : null; foreach (var mapping in mappings) { var table = mapping.Table; var tableKey = (table.Name, table.Schema); ModificationCommand command; var isMainEntry = true; if (_sharedTableEntryMapFactories.TryGetValue(tableKey, out var commandIdentityMapFactory)) { if (sharedTablesCommandsMap == null) { sharedTablesCommandsMap = new Dictionary <(string, string), SharedTableEntryMap <ModificationCommand> >(); } if (!sharedTablesCommandsMap.TryGetValue(tableKey, out var sharedCommandsMap)) { sharedCommandsMap = commandIdentityMapFactory( (n, s, c) => new ModificationCommand( n, s, generateParameterName, _sensitiveLoggingEnabled, c)); sharedTablesCommandsMap.Add(tableKey, sharedCommandsMap); } command = sharedCommandsMap.GetOrAddValue(entry); isMainEntry = sharedCommandsMap.IsMainEntry(entry); } else { command = new ModificationCommand( table.Name, table.Schema, generateParameterName, _sensitiveLoggingEnabled, comparer: null); } command.AddEntry(entry, isMainEntry); commands.Add(command); if (mapping.IsMainTableMapping) { Check.DebugAssert(mainCommand == null, "mainCommand == null"); mainCommand = command; } else if (relatedCommands != null) { relatedCommands.Add(command); } } if (mainCommand == null) { throw new InvalidOperationException(RelationalStrings.ReadonlyEntitySaved(entry.EntityType.DisplayName())); } if (relatedCommands != null) { foreach (var relatedCommand in relatedCommands) { relatedCommand.Predecessor = mainCommand; } } } if (sharedTablesCommandsMap != null) { AddUnchangedSharingEntries(sharedTablesCommandsMap.Values, entries); } return(commands); }