From 6fffce90a8b7cfdaaf7e0499c8934323b300f237 Mon Sep 17 00:00:00 2001 From: Lau Date: Sun, 1 Mar 2020 15:15:43 +0100 Subject: [PATCH] Upgrade scaffolding related files to 3.1.x. The Jet DatabaseModelFactory class needs to be checked and tested and its code similarity to the SQL Server provider should be increased. --- .../Scaffolding/Internal/JetCodeGenerator.cs | 19 +- .../Internal/JetDatabaseModelFactory.cs | 250 ++++++------------ 2 files changed, 101 insertions(+), 168 deletions(-) diff --git a/src/EFCore.Jet/Scaffolding/Internal/JetCodeGenerator.cs b/src/EFCore.Jet/Scaffolding/Internal/JetCodeGenerator.cs index 3f9431b..9f1c011 100644 --- a/src/EFCore.Jet/Scaffolding/Internal/JetCodeGenerator.cs +++ b/src/EFCore.Jet/Scaffolding/Internal/JetCodeGenerator.cs @@ -1,11 +1,19 @@ -// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Design; using Microsoft.EntityFrameworkCore.Scaffolding; namespace EntityFrameworkCore.Jet.Scaffolding.Internal { + /// + /// 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 class JetCodeGenerator : ProviderCodeGenerator { /// @@ -18,17 +26,18 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal } /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. + /// 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 override MethodCallCodeFragment GenerateUseProvider( string connectionString, MethodCallCodeFragment providerOptions) => new MethodCallCodeFragment( - nameof(JetDbContextOptionsExtensions.UseJet), + nameof(JetDbContextOptionsBuilderExtensions.UseJet), providerOptions == null ? new object[] { connectionString } : new object[] { connectionString, new NestedClosureCodeFragment("x", providerOptions) }); - } } diff --git a/src/EFCore.Jet/Scaffolding/Internal/JetDatabaseModelFactory.cs b/src/EFCore.Jet/Scaffolding/Internal/JetDatabaseModelFactory.cs index ec6a345..332f937 100644 --- a/src/EFCore.Jet/Scaffolding/Internal/JetDatabaseModelFactory.cs +++ b/src/EFCore.Jet/Scaffolding/Internal/JetDatabaseModelFactory.cs @@ -8,7 +8,6 @@ using System.Data.Jet; using System.Diagnostics; using System.Linq; using EntityFrameworkCore.Jet.Internal; -using EntityFrameworkCore.Jet.Metadata.Internal; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Diagnostics; @@ -16,7 +15,6 @@ using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; using Microsoft.EntityFrameworkCore.Scaffolding; using Microsoft.EntityFrameworkCore.Scaffolding.Metadata; -using Microsoft.EntityFrameworkCore.Scaffolding.Metadata.Internal; using EntityFrameworkCore.Jet.Utilities; namespace EntityFrameworkCore.Jet.Scaffolding.Internal @@ -25,43 +23,30 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// - public class JetDatabaseModelFactory : IDatabaseModelFactory + public class JetDatabaseModelFactory : DatabaseModelFactory { private DbConnection _connection; private Version _serverVersion; private HashSet _tablesToInclude; - private HashSet _schemasToInclude; - private HashSet _selectedSchemas; private HashSet _selectedTables; private DatabaseModel _databaseModel; private Dictionary _tables; private Dictionary _tableColumns; - /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. - /// - private static string SchemaQualifiedKey([NotNull] string name, [CanBeNull] string schema = null) => "[" + (schema ?? "") + "].[" + name + "]"; - - private static string TableKey(DatabaseTable table) => SchemaQualifiedKey(table.Name, table.Schema); - private static string ColumnKey(DatabaseTable table, string columnName) => TableKey(table) + ".[" + columnName + "]"; - - private static readonly ISet _dateTimePrecisionTypes = new HashSet { "datetimeoffset", "datetime2", "time" }; - - // see https://msdn.microsoft.com/en-us/library/ff878091.aspx - - private static readonly List _schemaPatterns = new List - { - "{schema}", - "[{schema}]" - }; + private static string ObjectKey([NotNull] string name) + => "[" + name + "]"; + + private static string TableKey(DatabaseTable table) + => TableKey(table.Name); + + private static string TableKey(String tableName) + => ObjectKey(tableName); + + private static string ColumnKey(DatabaseTable table, string columnName) + => TableKey(table) + "." + ObjectKey(columnName); private static readonly List _tablePatterns = new List { - "{schema}.{table}", - "[{schema}].[{table}]", - "{schema}.[{table}]", - "[{schema}].{table}", "{table}", "[{table}]" }; @@ -88,39 +73,37 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal _connection = null; _serverVersion = null; _selectedTables = new HashSet(StringComparer.OrdinalIgnoreCase); - _selectedSchemas = new HashSet(StringComparer.OrdinalIgnoreCase); _tablesToInclude = new HashSet(StringComparer.OrdinalIgnoreCase); - _schemasToInclude = new HashSet(StringComparer.OrdinalIgnoreCase); _databaseModel = new DatabaseModel(); _tables = new Dictionary(); _tableColumns = new Dictionary(StringComparer.OrdinalIgnoreCase); } /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. + /// 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 DatabaseModel Create(string connectionString, IEnumerable tables, IEnumerable schemas) + public override DatabaseModel Create(string connectionString, DatabaseModelFactoryOptions options) { Check.NotEmpty(connectionString, nameof(connectionString)); - Check.NotNull(tables, nameof(tables)); - Check.NotNull(schemas, nameof(schemas)); + Check.NotNull(options, nameof(options)); - using (var connection = new JetConnection(connectionString)) - { - return Create(connection, tables, schemas); - } + using var connection = new JetConnection(connectionString); + return Create(connection, options); } /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used - /// directly from your code. This API may change or be removed in future releases. + /// 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 DatabaseModel Create(DbConnection connection, IEnumerable tables, IEnumerable schemas) + public override DatabaseModel Create(DbConnection connection, DatabaseModelFactoryOptions options) { Check.NotNull(connection, nameof(connection)); - Check.NotNull(tables, nameof(tables)); - Check.NotNull(schemas, nameof(schemas)); + Check.NotNull(options, nameof(options)); ResetState(); @@ -131,23 +114,20 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal { _connection.Open(); } + try { - foreach (var schema in schemas) - { - _schemasToInclude.Add(schema); - } - - foreach (var table in tables) + foreach (var table in options.Tables) { _tablesToInclude.Add(table); } + _databaseModel.DefaultSchema = null; _databaseModel.DatabaseName = _connection.Database; + // CHECK: Is this actually set? Version.TryParse(_connection.ServerVersion, out _serverVersion); - GetDefaultSchema(); GetTables(); GetColumns(); GetPrimaryKeys(); @@ -170,30 +150,17 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal private void CheckSelectionsMatched() { - foreach (var schema in _schemasToInclude.Except(_selectedSchemas, StringComparer.OrdinalIgnoreCase)) - { - Logger.MissingSchemaWarning(schema); - } - foreach (var table in _tablesToInclude.Except(_selectedTables, StringComparer.OrdinalIgnoreCase)) { Logger.MissingTableWarning(table); } } - - private void GetDefaultSchema() - { - _databaseModel.DefaultSchema = "Jet"; - } - - - private void GetTables() { var command = _connection.CreateCommand(); - command.CommandText = - $"SHOW TABLES WHERE Name <> '{HistoryRepository.DefaultTableName}'"; + command.CommandText = $"SHOW TABLES WHERE Name <> '{HistoryRepository.DefaultTableName}'"; + using (var reader = command.ExecuteReader()) { while (reader.Read()) @@ -201,13 +168,13 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal var table = new DatabaseTable { Database = _databaseModel, - Schema = "Jet", + Schema = null, Name = reader.GetValueOrDefault("Name") }; - if (AllowsTable(table.Schema, table.Name)) + if (AllowsTable(table.Name)) { - Logger.TableFound(DisplayName(table.Schema, table.Name)); + Logger.TableFound(table.Name); _databaseModel.Tables.Add(table); _tables[TableKey(table)] = table; } @@ -215,27 +182,16 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal } } - private bool AllowsTable(string schema, string table) + private bool AllowsTable(string table) { - if (_schemasToInclude.Count == 0 - && _tablesToInclude.Count == 0) + if (_tablesToInclude.Count == 0) { return true; } - foreach (var schemaPattern in _schemaPatterns) - { - var key = schemaPattern.Replace("{schema}", schema); - if (_schemasToInclude.Contains(key)) - { - _selectedSchemas.Add(schema); - return true; - } - } - foreach (var tablePattern in _tablePatterns) { - var key = tablePattern.Replace("{schema}", schema).Replace("{table}", table); + var key = tablePattern.Replace("{table}", table); if (_tablesToInclude.Contains(key)) { _selectedTables.Add(key); @@ -260,41 +216,40 @@ ORDER BY { while (reader.Read()) { - var schemaName = "Jet"; var tableName = reader.GetValueOrDefault("Table"); var columnName = reader.GetValueOrDefault("Name"); var dataTypeName = reader.GetValueOrDefault("TypeName"); - var dataTypeSchemaName = "Jet"; var ordinal = reader.GetValueOrDefault("Ordinal"); var nullable = reader.GetValueOrDefault("IsNullable"); // ReSharper disable once UnusedVariable - var primaryKeyOrdinal = reader.GetValueOrDefault("IsKey") ? (int?)reader.GetValueOrDefault("Ordinal") : null; + var primaryKeyOrdinal = reader.GetValueOrDefault("IsKey") + ? (int?) reader.GetValueOrDefault("Ordinal") + : null; var defaultValue = reader.GetValueOrDefault("Default"); - var computedValue = (string)null; var precision = reader.GetValueOrDefault("Precision"); var scale = reader.GetValueOrDefault("Scale"); var maxLength = reader.GetValueOrDefault("MaxLength"); var isIdentity = reader.GetValueOrDefault("IsIdentity"); Logger.ColumnFound( - DisplayName(schemaName, tableName), + tableName, columnName, ordinal, - DisplayName(dataTypeSchemaName, dataTypeName), - maxLength, - precision, - scale, + dataTypeName, + maxLength ?? -1, + precision ?? 0, + scale ?? 0, nullable, isIdentity, defaultValue, - computedValue); + null); - if (!_tables.TryGetValue(SchemaQualifiedKey(tableName, schemaName), out var table)) + if (!_tables.TryGetValue(TableKey(tableName), out var table)) continue; - string storeType; - storeType = GetStoreType(dataTypeName, precision, scale, maxLength); + var storeType = GetStoreType(dataTypeName, precision, scale, maxLength); + // CHECK: Jet behavior. if (defaultValue == "(NULL)") { defaultValue = null; @@ -307,17 +262,18 @@ ORDER BY StoreType = storeType, IsNullable = nullable, DefaultValueSql = defaultValue, - ComputedColumnSql = computedValue, + ComputedColumnSql = null, ValueGenerated = isIdentity ? ValueGenerated.OnAdd - : (storeType) == "rowversion" + : storeType == "timestamp" ? ValueGenerated.OnAddOrUpdate : default(ValueGenerated?) }; - if (storeType == "rowversion") + if (storeType == "timestamp") { - column[ScaffoldingAnnotationNames.ConcurrencyToken] = true; + // Note: annotation name must match `ScaffoldingAnnotationNames.ConcurrencyToken` + column["ConcurrencyToken"] = true; } table.Columns.Add(column); @@ -328,36 +284,28 @@ ORDER BY private string GetStoreType(string dataTypeName, int? precision, int? scale, int? maxLength) { - if (dataTypeName == "decimal" - || dataTypeName == "numeric") + if (string.Equals(dataTypeName, "decimal", StringComparison.OrdinalIgnoreCase) || + string.Equals(dataTypeName, "numeric", StringComparison.OrdinalIgnoreCase)) { return $"{dataTypeName}({precision}, {scale.GetValueOrDefault(0)})"; } - if (_dateTimePrecisionTypes.Contains(dataTypeName) - && scale != null) - { - return $"{dataTypeName}({scale})"; - } - if (maxLength == -1) + + if (maxLength.HasValue) { - if (string.Equals(dataTypeName, "varchar", StringComparison.InvariantCultureIgnoreCase)) - return "text"; - else if (string.Equals(dataTypeName, "varbinary", StringComparison.InvariantCultureIgnoreCase)) - return "image"; - else - throw new InvalidOperationException("Unexpected type " + dataTypeName); - } + if (maxLength == -1) + { + if (string.Equals(dataTypeName, "varchar", StringComparison.OrdinalIgnoreCase)) + return "longchar"; - if (dataTypeName == "timestamp") - return "rowversion"; + if (string.Equals(dataTypeName, "varbinary", StringComparison.OrdinalIgnoreCase)) + return "longbinary"; - if (dataTypeName == "bit") - { - return "bit"; - } + throw new InvalidOperationException("Unexpected type " + dataTypeName); + } - if (maxLength.HasValue && maxLength > 0) - return $"{dataTypeName}({maxLength.Value})"; + if (maxLength > 0) + return $"{dataTypeName}({maxLength.Value})"; + } return dataTypeName; } @@ -378,10 +326,8 @@ ORDER BY DatabasePrimaryKey primaryKey = null; while (reader.Read()) { - var schemaName = "Jet"; var tableName = reader.GetValueOrDefault("TableName"); var indexName = reader.GetValueOrDefault("ConstraintName"); - var typeDesc = reader.GetValueOrDefault("ConstraintType"); var columnName = reader.GetValueOrDefault("ColumnName"); // ReSharper disable once UnusedVariable var indexOrdinal = reader.GetValueOrDefault("ColumnOrdinal"); @@ -390,16 +336,14 @@ ORDER BY if (primaryKey == null || primaryKey.Name != indexName // ReSharper disable once PossibleNullReferenceException - || primaryKey.Table.Name != tableName - || primaryKey.Table.Schema != schemaName) + || primaryKey.Table.Name != tableName) { - if (!_tables.TryGetValue(SchemaQualifiedKey(tableName, schemaName), out var table)) + if (!_tables.TryGetValue(TableKey(tableName), out var table)) { continue; } - Logger.PrimaryKeyFound( - indexName, DisplayName(schemaName, tableName)); + Logger.PrimaryKeyFound(indexName, tableName); primaryKey = new DatabasePrimaryKey { @@ -407,11 +351,6 @@ ORDER BY Name = indexName }; - if (typeDesc == "NONCLUSTERED") - { - primaryKey[JetAnnotationNames.Clustered] = false; - } - Debug.Assert(table.PrimaryKey == null); table.PrimaryKey = primaryKey; } @@ -440,10 +379,9 @@ ORDER BY DatabaseUniqueConstraint uniqueConstraint = null; while (reader.Read()) { - var schemaName = "Jet"; + var schemaName = "Jet"; // TODO: Change to `null`. var tableName = reader.GetValueOrDefault("TableName"); var indexName = reader.GetValueOrDefault("ConstraintName"); - var typeDesc = reader.GetValueOrDefault("ConstraintType"); var columnName = reader.GetValueOrDefault("ColumnName"); // ReSharper disable once UnusedVariable var indexOrdinal = reader.GetValueOrDefault("ColumnOrdinal"); @@ -455,12 +393,12 @@ ORDER BY || uniqueConstraint.Table.Name != tableName || uniqueConstraint.Table.Schema != schemaName) { - if (!_tables.TryGetValue(SchemaQualifiedKey(tableName, schemaName), out var table)) + if (!_tables.TryGetValue(TableKey(tableName), out var table)) { continue; } - Logger.UniqueConstraintFound(indexName, DisplayName(schemaName, tableName)); + Logger.UniqueConstraintFound(indexName, tableName); uniqueConstraint = new DatabaseUniqueConstraint { @@ -468,11 +406,6 @@ ORDER BY Name = indexName }; - if (typeDesc == "CLUSTERED") - { - uniqueConstraint[JetAnnotationNames.Clustered] = true; - } - table.UniqueConstraints.Add(uniqueConstraint); } @@ -502,11 +435,10 @@ ORDER BY DatabaseIndex index = null; while (reader.Read()) { - var schemaName = "Jet"; + var schemaName = "Jet"; // TODO: Change to `null`. var tableName = reader.GetValueOrDefault("Table"); var indexName = reader.GetValueOrDefault("Index"); var isUnique = reader.GetValueOrDefault("IsUnique"); - var typeDesc = "NON CLUSTERED"; var columnName = reader.GetValueOrDefault("Name"); // ReSharper disable once UnusedVariable var indexOrdinal = reader.GetValueOrDefault("Ordinal"); @@ -518,12 +450,12 @@ ORDER BY || index.Table.Name != tableName || index.Table.Schema != schemaName) { - if (!_tables.TryGetValue(SchemaQualifiedKey(tableName, schemaName), out var table)) + if (!_tables.TryGetValue(TableKey(tableName), out var table)) { continue; } - Logger.IndexFound(indexName, DisplayName(schemaName, tableName), isUnique); + Logger.IndexFound(indexName, tableName, isUnique); index = new DatabaseIndex { @@ -533,11 +465,6 @@ ORDER BY Filter = null }; - if (typeDesc == "CLUSTERED") - { - index[JetAnnotationNames.Clustered] = true; - } - table.Indexes.Add(index); } @@ -568,10 +495,10 @@ ORDER BY DatabaseForeignKey foreignKey = null; while (reader.Read()) { - var schemaName = "Jet"; + var schemaName = "Jet"; // TODO: Change to `null`. var tableName = reader.GetValueOrDefault("FromTable"); var constraintName = reader.GetValueOrDefault("ConstraintId"); - var principalTableSchemaName = "Jet"; + var principalTableSchemaName = "Jet"; // TODO: Change to `null`. var principalTableName = reader.GetValueOrDefault("ToTable"); var fromColumnName = reader.GetValueOrDefault("FromColumn"); var toColumnName = reader.GetValueOrDefault("ToColumn"); @@ -583,8 +510,8 @@ ORDER BY Logger.ForeignKeyFound( constraintName, - DisplayName(schemaName, tableName), - DisplayName(schemaName, principalTableName), + tableName, + principalTableName, deleteAction); if (foreignKey == null @@ -596,7 +523,7 @@ ORDER BY lastFkSchemaName = schemaName; lastFkTableName = tableName; - if (!_tables.TryGetValue(SchemaQualifiedKey(tableName, schemaName), out var table)) + if (!_tables.TryGetValue(TableKey(tableName), out var table)) { continue; } @@ -605,13 +532,13 @@ ORDER BY if (!string.IsNullOrEmpty(principalTableSchemaName) && !string.IsNullOrEmpty(principalTableName)) { - _tables.TryGetValue(SchemaQualifiedKey(principalTableName, principalTableSchemaName), out principalTable); + _tables.TryGetValue(TableKey(principalTableName), out principalTable); } if (principalTable == null) { Logger.ForeignKeyReferencesMissingPrincipalTableWarning( - constraintName, DisplayName(schemaName, tableName), DisplayName(principalTableSchemaName, principalTableName)); + constraintName, tableName, principalTableName); } foreignKey = new DatabaseForeignKey @@ -643,9 +570,6 @@ ORDER BY } } - private static string DisplayName(string schema, string name) - => (!string.IsNullOrEmpty(schema) ? schema + "." : "") + name; - private static ReferentialAction? ConvertToReferentialAction(string onDeleteAction) { switch (onDeleteAction.ToUpperInvariant()) @@ -670,4 +594,4 @@ ORDER BY } } } -} +} \ No newline at end of file