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.
pull/41/head
Lau 6 years ago
parent 70f2b880ca
commit 6fffce90a8

@ -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
{
/// <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 class JetCodeGenerator : ProviderCodeGenerator
{
/// <summary>
@ -18,17 +26,18 @@ namespace EntityFrameworkCore.Jet.Scaffolding.Internal
}
/// <summary>
/// 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.
/// </summary>
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) });
}
}

@ -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.
/// </summary>
public class JetDatabaseModelFactory : IDatabaseModelFactory
public class JetDatabaseModelFactory : DatabaseModelFactory
{
private DbConnection _connection;
private Version _serverVersion;
private HashSet<string> _tablesToInclude;
private HashSet<string> _schemasToInclude;
private HashSet<string> _selectedSchemas;
private HashSet<string> _selectedTables;
private DatabaseModel _databaseModel;
private Dictionary<string, DatabaseTable> _tables;
private Dictionary<string, DatabaseColumn> _tableColumns;
/// <summary>
/// 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.
/// </summary>
private static string SchemaQualifiedKey([NotNull] string name, [CanBeNull] string schema = null) => "[" + (schema ?? "") + "].[" + name + "]";
private static string ObjectKey([NotNull] string name)
=> "[" + 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 string TableKey(DatabaseTable table)
=> TableKey(table.Name);
private static readonly ISet<string> _dateTimePrecisionTypes = new HashSet<string> { "datetimeoffset", "datetime2", "time" };
private static string TableKey(String tableName)
=> ObjectKey(tableName);
// see https://msdn.microsoft.com/en-us/library/ff878091.aspx
private static readonly List<string> _schemaPatterns = new List<string>
{
"{schema}",
"[{schema}]"
};
private static string ColumnKey(DatabaseTable table, string columnName)
=> TableKey(table) + "." + ObjectKey(columnName);
private static readonly List<string> _tablePatterns = new List<string>
{
"{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<string>(StringComparer.OrdinalIgnoreCase);
_selectedSchemas = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_tablesToInclude = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_schemasToInclude = new HashSet<string>(StringComparer.OrdinalIgnoreCase);
_databaseModel = new DatabaseModel();
_tables = new Dictionary<string, DatabaseTable>();
_tableColumns = new Dictionary<string, DatabaseColumn>(StringComparer.OrdinalIgnoreCase);
}
/// <summary>
/// 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.
/// </summary>
public virtual DatabaseModel Create(string connectionString, IEnumerable<string> tables, IEnumerable<string> 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);
}
/// <summary>
/// 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.
/// </summary>
public virtual DatabaseModel Create(DbConnection connection, IEnumerable<string> tables, IEnumerable<string> 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<string>("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<string>("Table");
var columnName = reader.GetValueOrDefault<string>("Name");
var dataTypeName = reader.GetValueOrDefault<string>("TypeName");
var dataTypeSchemaName = "Jet";
var ordinal = reader.GetValueOrDefault<int>("Ordinal");
var nullable = reader.GetValueOrDefault<bool>("IsNullable");
// ReSharper disable once UnusedVariable
var primaryKeyOrdinal = reader.GetValueOrDefault<bool>("IsKey") ? (int?)reader.GetValueOrDefault<int>("Ordinal") : null;
var primaryKeyOrdinal = reader.GetValueOrDefault<bool>("IsKey")
? (int?) reader.GetValueOrDefault<int>("Ordinal")
: null;
var defaultValue = reader.GetValueOrDefault<string>("Default");
var computedValue = (string)null;
var precision = reader.GetValueOrDefault<int?>("Precision");
var scale = reader.GetValueOrDefault<int?>("Scale");
var maxLength = reader.GetValueOrDefault<int?>("MaxLength");
var isIdentity = reader.GetValueOrDefault<bool>("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)
if (maxLength.HasValue)
{
return $"{dataTypeName}({scale})";
}
if (maxLength == -1)
{
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 (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)
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<string>("TableName");
var indexName = reader.GetValueOrDefault<string>("ConstraintName");
var typeDesc = reader.GetValueOrDefault<string>("ConstraintType");
var columnName = reader.GetValueOrDefault<string>("ColumnName");
// ReSharper disable once UnusedVariable
var indexOrdinal = reader.GetValueOrDefault<int>("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<string>("TableName");
var indexName = reader.GetValueOrDefault<string>("ConstraintName");
var typeDesc = reader.GetValueOrDefault<string>("ConstraintType");
var columnName = reader.GetValueOrDefault<string>("ColumnName");
// ReSharper disable once UnusedVariable
var indexOrdinal = reader.GetValueOrDefault<int>("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<string>("Table");
var indexName = reader.GetValueOrDefault<string>("Index");
var isUnique = reader.GetValueOrDefault<bool>("IsUnique");
var typeDesc = "NON CLUSTERED";
var columnName = reader.GetValueOrDefault<string>("Name");
// ReSharper disable once UnusedVariable
var indexOrdinal = reader.GetValueOrDefault<int>("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<string>("FromTable");
var constraintName = reader.GetValueOrDefault<string>("ConstraintId");
var principalTableSchemaName = "Jet";
var principalTableSchemaName = "Jet"; // TODO: Change to `null`.
var principalTableName = reader.GetValueOrDefault<string>("ToTable");
var fromColumnName = reader.GetValueOrDefault<string>("FromColumn");
var toColumnName = reader.GetValueOrDefault<string>("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())

Loading…
Cancel
Save