diff --git a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
index f1cd40e6e97..e605dea2bac 100644
--- a/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
+++ b/src/EFCore.Relational/Migrations/Internal/MigrationsModelDiffer.cs
@@ -2238,6 +2238,7 @@ private static IEntityType GetRootType(ITable table)
/// 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.
///
+ [Obsolete]
public static IProperty[] GetMappedProperties([NotNull] ITable table, [NotNull] string[] names)
{
var properties = new IProperty[names.Length];
diff --git a/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs b/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs
index 4f3ad1b55d4..67362331745 100644
--- a/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs
+++ b/src/EFCore.Relational/Migrations/MigrationsSqlGenerator.cs
@@ -10,6 +10,7 @@
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;
using Microsoft.EntityFrameworkCore.Metadata;
+using Microsoft.EntityFrameworkCore.Migrations.Internal;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.Update;
@@ -82,6 +83,11 @@ public MigrationsSqlGenerator([NotNull] MigrationsSqlGeneratorDependencies depen
Check.NotNull(dependencies, nameof(dependencies));
Dependencies = dependencies;
+
+ if (dependencies.LoggingOptions.IsSensitiveDataLoggingEnabled)
+ {
+ SensitiveLoggingEnabled = true;
+ }
}
///
@@ -89,6 +95,8 @@ public MigrationsSqlGenerator([NotNull] MigrationsSqlGeneratorDependencies depen
///
protected virtual MigrationsSqlGeneratorDependencies Dependencies { get; }
+ private bool SensitiveLoggingEnabled { get; }
+
///
/// The .
///
@@ -912,7 +920,7 @@ protected virtual void Generate(
Check.NotNull(builder, nameof(builder));
var sqlBuilder = new StringBuilder();
- foreach (var modificationCommand in operation.GenerateModificationCommands(model))
+ foreach (var modificationCommand in GenerateModificationCommands(operation, model))
{
SqlGenerator.AppendInsertOperation(
sqlBuilder,
@@ -928,6 +936,55 @@ protected virtual void Generate(
}
}
+ ///
+ /// Generates the commands that correspond to the given operation.
+ ///
+ /// The data operation to generate commands for.
+ /// The model.
+ /// The commands that correspond to the given operation.
+ protected virtual IEnumerable GenerateModificationCommands(
+ [NotNull] InsertDataOperation operation,
+ [CanBeNull] IModel model)
+ {
+ if (operation.Columns.Length != operation.Values.GetLength(1))
+ {
+ throw new InvalidOperationException(RelationalStrings.InsertDataOperationValuesCountMismatch(
+ operation.Values.GetLength(1), operation.Columns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.ColumnTypes != null
+ && operation.Columns.Length != operation.ColumnTypes.Length)
+ {
+ throw new InvalidOperationException(RelationalStrings.InsertDataOperationTypesCountMismatch(
+ operation.ColumnTypes.Length, operation.Columns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.ColumnTypes == null
+ && model == null)
+ {
+ throw new InvalidOperationException(RelationalStrings.InsertDataOperationNoModel(
+ FormatTable(operation.Table, operation.Schema)));
+ }
+
+ var properties = operation.ColumnTypes == null
+ ? GetMappedProperties(operation.Columns, operation.Table, operation.Schema, model)
+ : null;
+
+ for (var i = 0; i < operation.Values.GetLength(0); i++)
+ {
+ var modifications = new ColumnModification[operation.Columns.Length];
+ for (var j = 0; j < operation.Columns.Length; j++)
+ {
+ modifications[j] = new ColumnModification(
+ operation.Columns[j], originalValue: null, value: operation.Values[i, j], property: properties?[j],
+ columnType: operation.ColumnTypes?[j], isRead: false, isWrite: true, isKey: true, isCondition: false,
+ sensitiveLoggingEnabled: SensitiveLoggingEnabled);
+ }
+
+ yield return new ModificationCommand(operation.Table, operation.Schema, modifications, sensitiveLoggingEnabled: SensitiveLoggingEnabled);
+ }
+ }
+
///
/// Builds commands for the given by making calls on the given
/// , and then terminates the final command.
@@ -944,7 +1001,7 @@ protected virtual void Generate(
Check.NotNull(builder, nameof(builder));
var sqlBuilder = new StringBuilder();
- foreach (var modificationCommand in operation.GenerateModificationCommands(model))
+ foreach (var modificationCommand in GenerateModificationCommands(operation, model))
{
SqlGenerator.AppendDeleteOperation(
sqlBuilder,
@@ -956,6 +1013,55 @@ protected virtual void Generate(
EndStatement(builder);
}
+ ///
+ /// Generates the commands that correspond to the given operation.
+ ///
+ /// The data operation to generate commands for.
+ /// The model.
+ /// The commands that correspond to the given operation.
+ protected virtual IEnumerable GenerateModificationCommands(
+ [NotNull] DeleteDataOperation operation,
+ [CanBeNull] IModel model)
+ {
+ if (operation.KeyColumns.Length != operation.KeyValues.GetLength(1))
+ {
+ throw new InvalidOperationException(RelationalStrings.DeleteDataOperationValuesCountMismatch(
+ operation.KeyValues.GetLength(1), operation.KeyColumns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.KeyColumnTypes != null
+ && operation.KeyColumns.Length != operation.KeyColumnTypes.Length)
+ {
+ throw new InvalidOperationException(RelationalStrings.DeleteDataOperationTypesCountMismatch(
+ operation.KeyColumnTypes.Length, operation.KeyColumns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.KeyColumnTypes == null
+ && model == null)
+ {
+ throw new InvalidOperationException(RelationalStrings.DeleteDataOperationNoModel(
+ FormatTable(operation.Table, operation.Schema)));
+ }
+
+ var properties = operation.KeyColumnTypes == null
+ ? GetMappedProperties(operation.KeyColumns, operation.Table, operation.Schema, model)
+ : null;
+
+ for (var i = 0; i < operation.KeyValues.GetLength(0); i++)
+ {
+ var modifications = new ColumnModification[operation.KeyColumns.Length];
+ for (var j = 0; j < operation.KeyColumns.Length; j++)
+ {
+ modifications[j] = new ColumnModification(
+ operation.KeyColumns[j], originalValue: null, value: operation.KeyValues[i, j], property: properties?[j],
+ columnType: operation.KeyColumnTypes?[j], isRead: false, isWrite: true, isKey: true, isCondition: true,
+ sensitiveLoggingEnabled: SensitiveLoggingEnabled);
+ }
+
+ yield return new ModificationCommand(operation.Table, operation.Schema, modifications, sensitiveLoggingEnabled: SensitiveLoggingEnabled);
+ }
+ }
+
///
/// Builds commands for the given by making calls on the given
/// , and then terminates the final command.
@@ -972,7 +1078,7 @@ protected virtual void Generate(
Check.NotNull(builder, nameof(builder));
var sqlBuilder = new StringBuilder();
- foreach (var modificationCommand in operation.GenerateModificationCommands(model))
+ foreach (var modificationCommand in GenerateModificationCommands(operation, model))
{
SqlGenerator.AppendUpdateOperation(
sqlBuilder,
@@ -984,6 +1090,115 @@ protected virtual void Generate(
EndStatement(builder);
}
+ ///
+ /// Generates the commands that correspond to the given operation.
+ ///
+ /// The data operation to generate commands for.
+ /// The model.
+ /// The commands that correspond to the given operation.
+ protected virtual IEnumerable GenerateModificationCommands(
+ [NotNull] UpdateDataOperation operation, [CanBeNull] IModel model)
+ {
+ if (operation.KeyColumns.Length != operation.KeyValues.GetLength(1))
+ {
+ throw new InvalidOperationException(RelationalStrings.UpdateDataOperationKeyValuesCountMismatch(
+ operation.KeyValues.GetLength(1), operation.KeyColumns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.Columns.Length != operation.Values.GetLength(1))
+ {
+ throw new InvalidOperationException(RelationalStrings.UpdateDataOperationValuesCountMismatch(
+ operation.Values.GetLength(1), operation.Columns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.KeyValues.GetLength(0) != operation.Values.GetLength(0))
+ {
+ throw new InvalidOperationException(RelationalStrings.UpdateDataOperationRowCountMismatch(
+ operation.Values.GetLength(0), operation.KeyValues.GetLength(0), FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.KeyColumnTypes != null
+ && operation.KeyColumns.Length != operation.KeyColumnTypes.Length)
+ {
+ throw new InvalidOperationException(RelationalStrings.UpdateDataOperationKeyTypesCountMismatch(
+ operation.KeyColumnTypes.Length, operation.KeyColumns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.ColumnTypes != null
+ && operation.Columns.Length != operation.ColumnTypes.Length)
+ {
+ throw new InvalidOperationException(RelationalStrings.UpdateDataOperationTypesCountMismatch(
+ operation.ColumnTypes.Length, operation.Columns.Length, FormatTable(operation.Table, operation.Schema)));
+ }
+
+ if (operation.KeyColumnTypes == null
+ && model == null)
+ {
+ throw new InvalidOperationException(RelationalStrings.UpdateDataOperationNoModel(
+ FormatTable(operation.Table, operation.Schema)));
+ }
+
+ var keyProperties = operation.KeyColumnTypes == null
+ ? GetMappedProperties(operation.KeyColumns, operation.Table, operation.Schema, model)
+ : null;
+ var properties = operation.ColumnTypes == null
+ ? GetMappedProperties(operation.Columns, operation.Table, operation.Schema, model)
+ : null;
+
+ for (var i = 0; i < operation.KeyValues.GetLength(0); i++)
+ {
+ var keys = new ColumnModification[operation.KeyColumns.Length];
+ for (var j = 0; j < operation.KeyColumns.Length; j++)
+ {
+ keys[j] = new ColumnModification(
+ operation.KeyColumns[j], originalValue: null, value: operation.KeyValues[i, j], property: keyProperties?[j],
+ columnType: operation.KeyColumnTypes?[j], isRead: false, isWrite: false, isKey: true, isCondition: true,
+ sensitiveLoggingEnabled: SensitiveLoggingEnabled);
+ }
+
+ var modifications = new ColumnModification[operation.Columns.Length];
+ for (var j = 0; j < operation.Columns.Length; j++)
+ {
+ modifications[j] = new ColumnModification(
+ operation.Columns[j], originalValue: null, value: operation.Values[i, j], property: properties?[j],
+ columnType: operation.ColumnTypes?[j], isRead: false, isWrite: true, isKey: true, isCondition: false,
+ sensitiveLoggingEnabled: SensitiveLoggingEnabled);
+ }
+
+ yield return new ModificationCommand(operation.Table, operation.Schema, keys.Concat(modifications).ToArray(), sensitiveLoggingEnabled: SensitiveLoggingEnabled);
+ }
+ }
+
+ private static string FormatTable(string table, string schema)
+ => schema == null ? table : schema + "." + table;
+
+ private static IProperty[] GetMappedProperties(
+ [NotNull] string[] names, [NotNull] string tableName, [CanBeNull] string schema, [NotNull] IModel model)
+ {
+ var table = model.GetRelationalModel().FindTable(tableName, schema);
+ if (table == null)
+ {
+ throw new InvalidOperationException(RelationalStrings.DataOperationNoTable(
+ FormatTable(tableName, schema)));
+ }
+
+ var properties = new IProperty[names.Length];
+ for (var i = 0; i < names.Length; i++)
+ {
+ var name = names[i];
+ var column = table.FindColumn(name);
+ if (column == null)
+ {
+ throw new InvalidOperationException(RelationalStrings.DataOperationNoProperty(
+ FormatTable(tableName, schema), name));
+ }
+
+ properties[i] = column.PropertyMappings.First().Property;
+ }
+
+ return properties;
+ }
+
///
/// Generates a SQL fragment configuring a sequence in a .
///
@@ -1086,6 +1301,13 @@ protected virtual void CreateTableColumns(
for (var i = 0; i < operation.Columns.Count; i++)
{
var column = operation.Columns[i];
+ if (column.Table != operation.Name
+ || column.Schema != operation.Schema)
+ {
+ throw new InvalidOperationException(RelationalStrings.MigrationColumnTableMismatch(
+ column.Name, FormatTable(column.Table, column.Schema), FormatTable(operation.Name, operation.Schema)));
+ }
+
ColumnDefinition(column, model, builder);
if (i != operation.Columns.Count - 1)
diff --git a/src/EFCore.Relational/Migrations/MigrationsSqlGeneratorDependencies.cs b/src/EFCore.Relational/Migrations/MigrationsSqlGeneratorDependencies.cs
index edbec001b71..189922912e3 100644
--- a/src/EFCore.Relational/Migrations/MigrationsSqlGeneratorDependencies.cs
+++ b/src/EFCore.Relational/Migrations/MigrationsSqlGeneratorDependencies.cs
@@ -62,6 +62,7 @@ public MigrationsSqlGeneratorDependencies(
[NotNull] ISqlGenerationHelper sqlGenerationHelper,
[NotNull] IRelationalTypeMappingSource typeMappingSource,
[NotNull] ICurrentDbContext currentContext,
+ [NotNull] ILoggingOptions loggingOptions,
[NotNull] IDiagnosticsLogger logger)
{
Check.NotNull(commandBuilderFactory, nameof(commandBuilderFactory));
@@ -69,6 +70,7 @@ public MigrationsSqlGeneratorDependencies(
Check.NotNull(sqlGenerationHelper, nameof(sqlGenerationHelper));
Check.NotNull(typeMappingSource, nameof(typeMappingSource));
Check.NotNull(currentContext, nameof(currentContext));
+ Check.NotNull(loggingOptions, nameof(loggingOptions));
Check.NotNull(logger, nameof(logger));
CommandBuilderFactory = commandBuilderFactory;
@@ -76,6 +78,7 @@ public MigrationsSqlGeneratorDependencies(
UpdateSqlGenerator = updateSqlGenerator;
TypeMappingSource = typeMappingSource;
CurrentContext = currentContext;
+ LoggingOptions = loggingOptions;
Logger = logger;
}
@@ -105,7 +108,12 @@ public MigrationsSqlGeneratorDependencies(
public ICurrentDbContext CurrentContext { get; }
///
- /// A logger.
+ /// The logging options.
+ ///
+ public ILoggingOptions LoggingOptions { get; }
+
+ ///
+ /// The database command logger.
///
public IDiagnosticsLogger Logger { get; }
@@ -121,6 +129,7 @@ public MigrationsSqlGeneratorDependencies With([NotNull] IRelationalCommandBuild
SqlGenerationHelper,
TypeMappingSource,
CurrentContext,
+ LoggingOptions,
Logger);
///
@@ -135,6 +144,7 @@ public MigrationsSqlGeneratorDependencies With([NotNull] IUpdateSqlGenerator upd
SqlGenerationHelper,
TypeMappingSource,
CurrentContext,
+ LoggingOptions,
Logger);
///
@@ -149,6 +159,7 @@ public MigrationsSqlGeneratorDependencies With([NotNull] ISqlGenerationHelper sq
sqlGenerationHelper,
TypeMappingSource,
CurrentContext,
+ LoggingOptions,
Logger);
///
@@ -163,6 +174,7 @@ public MigrationsSqlGeneratorDependencies With([NotNull] IRelationalTypeMappingS
SqlGenerationHelper,
typeMappingSource,
CurrentContext,
+ LoggingOptions,
Logger);
///
@@ -177,6 +189,22 @@ public MigrationsSqlGeneratorDependencies With([NotNull] ICurrentDbContext curre
SqlGenerationHelper,
TypeMappingSource,
currentContext,
+ LoggingOptions,
+ Logger);
+
+ ///
+ /// Clones this dependency parameter object with one service replaced.
+ ///
+ /// A replacement for the current dependency of this type.
+ /// A new parameter object with the given service replaced.
+ public MigrationsSqlGeneratorDependencies With([NotNull] ILoggingOptions loggingOptions)
+ => new MigrationsSqlGeneratorDependencies(
+ CommandBuilderFactory,
+ UpdateSqlGenerator,
+ SqlGenerationHelper,
+ TypeMappingSource,
+ CurrentContext,
+ loggingOptions,
Logger);
///
@@ -191,6 +219,7 @@ public MigrationsSqlGeneratorDependencies With([NotNull] IDiagnosticsLogger
public virtual string[] KeyColumns { get; [param: NotNull] set; }
+ ///
+ /// A list of column store types for the columns that will be used to identify
+ /// the rows that should be deleted.
+ ///
+ public virtual string[] KeyColumnTypes { get; [param: NotNull] set; }
+
///
/// The rows to be deleted, represented as a list of key value arrays where each
/// value in the array corresponds to a column in the property.
@@ -43,6 +50,7 @@ public class DeleteDataOperation : MigrationOperation
/// Generates the commands that correspond to this operation.
///
/// The commands that correspond to this operation.
+ [Obsolete]
public virtual IEnumerable GenerateModificationCommands([CanBeNull] IModel model)
{
Check.DebugAssert(
@@ -61,10 +69,11 @@ public virtual IEnumerable GenerateModificationCommands([Ca
{
modifications[j] = new ColumnModification(
KeyColumns[j], originalValue: null, value: KeyValues[i, j], property: properties?[j],
- isRead: false, isWrite: true, isKey: true, isCondition: true, sensitiveLoggingEnabled: true);
+ columnType: KeyColumnTypes?[j], isRead: false, isWrite: true, isKey: true, isCondition: true,
+ sensitiveLoggingEnabled: false);
}
- yield return new ModificationCommand(Table, Schema, modifications, sensitiveLoggingEnabled: true);
+ yield return new ModificationCommand(Table, Schema, modifications, sensitiveLoggingEnabled: false);
}
}
}
diff --git a/src/EFCore.Relational/Migrations/Operations/InsertDataOperation.cs b/src/EFCore.Relational/Migrations/Operations/InsertDataOperation.cs
index 8f627436a7e..fc91f3b37ed 100644
--- a/src/EFCore.Relational/Migrations/Operations/InsertDataOperation.cs
+++ b/src/EFCore.Relational/Migrations/Operations/InsertDataOperation.cs
@@ -1,6 +1,7 @@
// 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using JetBrains.Annotations;
@@ -32,6 +33,11 @@ public class InsertDataOperation : MigrationOperation
///
public virtual string[] Columns { get; [param: NotNull] set; }
+ ///
+ /// A list of column store types for the columns into which data will be inserted.
+ ///
+ public virtual string[] ColumnTypes { get; [param: NotNull] set; }
+
///
/// The data to be inserted, represented as a list of value arrays where each
/// value in the array corresponds to a column in the property.
@@ -42,6 +48,7 @@ public class InsertDataOperation : MigrationOperation
/// Generates the commands that correspond to this operation.
///
/// The commands that correspond to this operation.
+ [Obsolete]
public virtual IEnumerable GenerateModificationCommands([CanBeNull] IModel model)
{
Check.DebugAssert(
@@ -60,10 +67,11 @@ public virtual IEnumerable GenerateModificationCommands([Ca
{
modifications[j] = new ColumnModification(
Columns[j], originalValue: null, value: Values[i, j], property: properties?[j],
- isRead: false, isWrite: true, isKey: true, isCondition: false, sensitiveLoggingEnabled: true);
+ columnType: ColumnTypes?[j], isRead: false, isWrite: true, isKey: true, isCondition: false,
+ sensitiveLoggingEnabled: false);
}
- yield return new ModificationCommand(Table, Schema, modifications, sensitiveLoggingEnabled: true);
+ yield return new ModificationCommand(Table, Schema, modifications, sensitiveLoggingEnabled: false);
}
}
}
diff --git a/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs b/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs
index 34f990fe2ba..d2499a4c152 100644
--- a/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs
+++ b/src/EFCore.Relational/Migrations/Operations/UpdateDataOperation.cs
@@ -1,6 +1,7 @@
// 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 System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
@@ -34,6 +35,12 @@ public class UpdateDataOperation : MigrationOperation
///
public virtual string[] KeyColumns { get; [param: NotNull] set; }
+ ///
+ /// A list of column store types for the columns that will be used to identify
+ /// the rows that should be updated.
+ ///
+ public virtual string[] KeyColumnTypes { get; [param: NotNull] set; }
+
///
/// The rows to be updated, represented as a list of key value arrays where each
/// value in the array corresponds to a column in the property.
@@ -45,6 +52,11 @@ public class UpdateDataOperation : MigrationOperation
///
public virtual string[] Columns { get; [param: NotNull] set; }
+ ///
+ /// A list of column store types for the columns in which data will be updated.
+ ///
+ public virtual string[] ColumnTypes { get; [param: NotNull] set; }
+
///
/// The data to be updated, represented as a list of value arrays where each
/// value in the array corresponds to a column in the property.
@@ -55,6 +67,7 @@ public class UpdateDataOperation : MigrationOperation
/// Generates the commands that correspond to this operation.
///
/// The commands that correspond to this operation.
+ [Obsolete]
public virtual IEnumerable GenerateModificationCommands([CanBeNull] IModel model)
{
Check.DebugAssert(
@@ -82,7 +95,8 @@ public virtual IEnumerable GenerateModificationCommands([Ca
{
keys[j] = new ColumnModification(
KeyColumns[j], originalValue: null, value: KeyValues[i, j], property: keyProperties?[j],
- isRead: false, isWrite: false, isKey: true, isCondition: true, sensitiveLoggingEnabled: true);
+ columnType: KeyColumnTypes?[j], isRead: false, isWrite: false, isKey: true, isCondition: true,
+ sensitiveLoggingEnabled: false);
}
var modifications = new ColumnModification[Columns.Length];
@@ -90,10 +104,11 @@ public virtual IEnumerable GenerateModificationCommands([Ca
{
modifications[j] = new ColumnModification(
Columns[j], originalValue: null, value: Values[i, j], property: properties?[j],
- isRead: false, isWrite: true, isKey: true, isCondition: false, sensitiveLoggingEnabled: true);
+ columnType: ColumnTypes?[j], isRead: false, isWrite: true, isKey: true, isCondition: false,
+ sensitiveLoggingEnabled: false);
}
- yield return new ModificationCommand(Table, Schema, keys.Concat(modifications).ToArray(), sensitiveLoggingEnabled: true);
+ yield return new ModificationCommand(Table, Schema, keys.Concat(modifications).ToArray(), sensitiveLoggingEnabled: false);
}
}
}
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
index 0f30abe2f92..d8f3881a012 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
+++ b/src/EFCore.Relational/Properties/RelationalStrings.Designer.cs
@@ -640,6 +640,134 @@ public static string DbFunctionInvalidIQueryableOwnedReturnType([CanBeNull] obje
GetString("DbFunctionInvalidIQueryableOwnedReturnType", nameof(function), nameof(type)),
function, type);
+ ///
+ /// There is no property mapped to the column '{table}.{column}' used in a data operation. Either add a property mapped to this column or specify the column types in the data operation.
+ ///
+ public static string DataOperationNoProperty([CanBeNull] object table, [CanBeNull] object column)
+ => string.Format(
+ GetString("DataOperationNoProperty", nameof(table), nameof(column)),
+ table, column);
+
+ ///
+ /// There is no entity type mapped to the table '{table}' used in a data operation. Either add the corresponding entity type to the model or specify the column types in the data operation.
+ ///
+ public static string DataOperationNoTable([CanBeNull] object table)
+ => string.Format(
+ GetString("DataOperationNoTable", nameof(table)),
+ table);
+
+ ///
+ /// The data deletion operation on '{table}' is not associated with a model. Either add a model to the migration or specify the column types in all data operations.
+ ///
+ public static string DeleteDataOperationNoModel([CanBeNull] object table)
+ => string.Format(
+ GetString("DeleteDataOperationNoModel", nameof(table)),
+ table);
+
+ ///
+ /// The number of key column types ({typesCount}) doesn't match the number of key columns ({columnsCount}) for the data deletion operation on '{table}'.
+ ///
+ public static string DeleteDataOperationTypesCountMismatch([CanBeNull] object typesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("DeleteDataOperationTypesCountMismatch", nameof(typesCount), nameof(columnsCount), nameof(table)),
+ typesCount, columnsCount, table);
+
+ ///
+ /// The number of key values ({valuesCount}) doesn't match the number of key columns ({columnsCount}) for the data deletion operation on '{table}'.
+ ///
+ public static string DeleteDataOperationValuesCountMismatch([CanBeNull] object valuesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("DeleteDataOperationValuesCountMismatch", nameof(valuesCount), nameof(columnsCount), nameof(table)),
+ valuesCount, columnsCount, table);
+
+ ///
+ /// The data insertion operation on '{table}' is not associated with a model. Either add a model to the migration or specify the column types in all data operations.
+ ///
+ public static string InsertDataOperationNoModel([CanBeNull] object table)
+ => string.Format(
+ GetString("InsertDataOperationNoModel", nameof(table)),
+ table);
+
+ ///
+ /// The number of column types ({typesCount}) doesn't match the number of columns ({columnsCount}) for the data insertion operation on '{table}'.
+ ///
+ public static string InsertDataOperationTypesCountMismatch([CanBeNull] object typesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("InsertDataOperationTypesCountMismatch", nameof(typesCount), nameof(columnsCount), nameof(table)),
+ typesCount, columnsCount, table);
+
+ ///
+ /// The number of values ({valuesCount}) doesn't match the number of columns ({columnsCount}) for the data insertion operation on '{table}'.
+ ///
+ public static string InsertDataOperationValuesCountMismatch([CanBeNull] object valuesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("InsertDataOperationValuesCountMismatch", nameof(valuesCount), nameof(columnsCount), nameof(table)),
+ valuesCount, columnsCount, table);
+
+ ///
+ /// The operation for the column '{column}' targets the '{columnTable}' table, but is part of the create '{table}' operation. Make both operations use the same name.
+ ///
+ public static string MigrationColumnTableMismatch([CanBeNull] object column, [CanBeNull] object columnTable, [CanBeNull] object table)
+ => string.Format(
+ GetString("MigrationColumnTableMismatch", nameof(column), nameof(columnTable), nameof(table)),
+ column, columnTable, table);
+
+ ///
+ /// The number of key column types ({typesCount}) doesn't match the number of key columns ({columnsCount}) for the data modification operation on '{table}'.
+ ///
+ public static string UpdateDataOperationKeyTypesCountMismatch([CanBeNull] object typesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("UpdateDataOperationKeyTypesCountMismatch", nameof(typesCount), nameof(columnsCount), nameof(table)),
+ typesCount, columnsCount, table);
+
+ ///
+ /// The number of key values ({valuesCount}) doesn't match the number of key columns ({columnsCount}) for the data modification operation on '{table}'.
+ ///
+ public static string UpdateDataOperationKeyValuesCountMismatch([CanBeNull] object valuesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("UpdateDataOperationKeyValuesCountMismatch", nameof(valuesCount), nameof(columnsCount), nameof(table)),
+ valuesCount, columnsCount, table);
+
+ ///
+ /// The data modification operation on '{table}' is not associated with a model. Either add a model to the migration or specify the column types in all data operations.
+ ///
+ public static string UpdateDataOperationNoModel([CanBeNull] object table)
+ => string.Format(
+ GetString("UpdateDataOperationNoModel", nameof(table)),
+ table);
+
+ ///
+ /// The number of value rows ({valuesCount}) doesn't match the number of key rows ({keyCount}) for the data modification operation on '{table}'.
+ ///
+ public static string UpdateDataOperationRowCountMismatch([CanBeNull] object valuesCount, [CanBeNull] object keyCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("UpdateDataOperationRowCountMismatch", nameof(valuesCount), nameof(keyCount), nameof(table)),
+ valuesCount, keyCount, table);
+
+ ///
+ /// The number of column types ({typesCount}) doesn't match the number of columns ({columnsCount}) for the data modification operation on '{table}'.
+ ///
+ public static string UpdateDataOperationTypesCountMismatch([CanBeNull] object typesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("UpdateDataOperationTypesCountMismatch", nameof(typesCount), nameof(columnsCount), nameof(table)),
+ typesCount, columnsCount, table);
+
+ ///
+ /// The number of values ({valuesCount}) doesn't match the number of columns ({columnsCount}) for the data modification operation on '{table}'.
+ ///
+ public static string UpdateDataOperationValuesCountMismatch([CanBeNull] object valuesCount, [CanBeNull] object columnsCount, [CanBeNull] object table)
+ => string.Format(
+ GetString("UpdateDataOperationValuesCountMismatch", nameof(valuesCount), nameof(columnsCount), nameof(table)),
+ valuesCount, columnsCount, table);
+
+ ///
+ /// The store type '{type}' is not supported by the current provider.
+ ///
+ public static string UnsupportedStoreType([CanBeNull] object type)
+ => string.Format(
+ GetString("UnsupportedStoreType", nameof(type)),
+ type);
+
private static string GetString(string name, params string[] formatterNames)
{
var value = _resourceManager.GetString(name);
diff --git a/src/EFCore.Relational/Properties/RelationalStrings.resx b/src/EFCore.Relational/Properties/RelationalStrings.resx
index c9ddd1a83a1..27ce3b125f1 100644
--- a/src/EFCore.Relational/Properties/RelationalStrings.resx
+++ b/src/EFCore.Relational/Properties/RelationalStrings.resx
@@ -521,4 +521,52 @@
The DbFunction '{function}' has an invalid return type '{type}'. Owned entity types cannot be used as the return type of a DbFunction.
+
+ There is no property mapped to the column '{table}.{column}' used in a data operation. Either add a property mapped to this column or specify the column types in the data operation.
+
+
+ There is no entity type mapped to the table '{table}' used in a data operation. Either add the corresponding entity type to the model or specify the column types in the data operation.
+
+
+ The data deletion operation on '{table}' is not associated with a model. Either add a model to the migration or specify the column types in all data operations.
+
+
+ The number of key column types ({typesCount}) doesn't match the number of key columns ({columnsCount}) for the data deletion operation on '{table}'.
+
+
+ The number of key values ({valuesCount}) doesn't match the number of key columns ({columnsCount}) for the data deletion operation on '{table}'.
+
+
+ The data insertion operation on '{table}' is not associated with a model. Either add a model to the migration or specify the column types in all data operations.
+
+
+ The number of column types ({typesCount}) doesn't match the number of columns ({columnsCount}) for the data insertion operation on '{table}'.
+
+
+ The number of values ({valuesCount}) doesn't match the number of columns ({columnsCount}) for the data insertion operation on '{table}'.
+
+
+ The operation for the column '{column}' targets the '{columnTable}' table, but is part of the create '{table}' operation. Make both operations use the same name.
+
+
+ The number of key column types ({typesCount}) doesn't match the number of key columns ({columnsCount}) for the data modification operation on '{table}'.
+
+
+ The number of key values ({valuesCount}) doesn't match the number of key columns ({columnsCount}) for the data modification operation on '{table}'.
+
+
+ The data modification operation on '{table}' is not associated with a model. Either add a model to the migration or specify the column types in all data operations.
+
+
+ The number of value rows ({valuesCount}) doesn't match the number of key rows ({keyCount}) for the data modification operation on '{table}'.
+
+
+ The number of column types ({typesCount}) doesn't match the number of columns ({columnsCount}) for the data modification operation on '{table}'.
+
+
+ The number of values ({valuesCount}) doesn't match the number of columns ({columnsCount}) for the data modification operation on '{table}'.
+
+
+ The store type '{type}' is not supported by the current provider.
+
\ No newline at end of file
diff --git a/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs b/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs
index 0070d3a6700..f76cf2c25fe 100644
--- a/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs
+++ b/src/EFCore.Relational/Storage/RelationalTypeMappingSourceExtensions.cs
@@ -104,7 +104,52 @@ public static RelationalTypeMapping GetMapping(
return mapping;
}
- throw new InvalidOperationException(RelationalStrings.UnsupportedType(typeName));
+ throw new InvalidOperationException(RelationalStrings.UnsupportedStoreType(typeName));
+ }
+
+ ///
+ ///
+ /// Finds the type mapping for a given and additional facets, throwing if no mapping is found.
+ ///
+ ///
+ /// Note: Only call this method if there is no available, otherwise
+ /// call
+ ///
+ ///
+ /// The type mapping source.
+ /// The CLR type.
+ /// The database type name.
+ /// If true, then a special mapping for a key or index may be returned.
+ /// Specifies Unicode or Ansi mapping, or null for default.
+ /// Specifies a size for the mapping, or null for default.
+ /// Specifies a row-version, or null for default.
+ /// Specifies a fixed length mapping, or null for default.
+ /// Specifies a precision for the mapping, or null for default.
+ /// Specifies a scale for the mapping, or null for default.
+ /// The type mapping, or null if none was found.
+ public static RelationalTypeMapping GetMapping(
+ [NotNull] this IRelationalTypeMappingSource typeMappingSource,
+ [NotNull] Type type,
+ [CanBeNull] string storeTypeName,
+ bool keyOrIndex = false,
+ bool? unicode = null,
+ int? size = null,
+ bool? rowVersion = null,
+ bool? fixedLength = null,
+ int? precision = null,
+ int? scale = null)
+ {
+ Check.NotNull(typeMappingSource, nameof(typeMappingSource));
+ Check.NotNull(type, nameof(type));
+
+ var mapping = typeMappingSource.FindMapping(
+ type, storeTypeName, keyOrIndex, unicode, size, rowVersion, fixedLength, precision, scale);
+ if (mapping != null)
+ {
+ return mapping;
+ }
+
+ throw new InvalidOperationException(RelationalStrings.UnsupportedType(type));
}
}
}
diff --git a/src/EFCore.Relational/Update/ColumnModification.cs b/src/EFCore.Relational/Update/ColumnModification.cs
index 73dd9ea5b1f..9651452c6ce 100644
--- a/src/EFCore.Relational/Update/ColumnModification.cs
+++ b/src/EFCore.Relational/Update/ColumnModification.cs
@@ -59,6 +59,7 @@ public ColumnModification(
originalValue: null,
value: null,
property: property,
+ null,
isRead: isRead,
isWrite: isWrite,
isKey: isKey,
@@ -82,6 +83,7 @@ public ColumnModification(
/// The original value of the property mapped to this column.
/// Gets or sets the current value of the property mapped to this column.
/// The property that maps to the column.
+ /// The database type of the column.
/// Indicates whether or not a value must be read from the database for the column.
/// Indicates whether or not a value must be written to the database for the column.
/// Indicates whether or not the column part of a primary or alternate key.
@@ -92,6 +94,7 @@ public ColumnModification(
[CanBeNull] object originalValue,
[CanBeNull] object value,
[CanBeNull] IProperty property,
+ [CanBeNull] string columnType,
bool isRead,
bool isWrite,
bool isKey,
@@ -104,6 +107,7 @@ public ColumnModification(
_originalValue = originalValue;
_value = value;
Property = property;
+ ColumnType = columnType;
IsRead = isRead;
IsWrite = isWrite;
IsKey = isKey;
@@ -173,6 +177,11 @@ public virtual string OriginalParameterName
///
public virtual string ColumnName { get; }
+ ///
+ /// The database type of the column.
+ ///
+ public virtual string ColumnType { get; }
+
///
/// The original value of the property mapped to this column.
///
diff --git a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
index f9a85d26d57..92a30321c88 100644
--- a/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
+++ b/src/EFCore.Relational/Update/Internal/CommandBatchPreparer.cs
@@ -201,7 +201,7 @@ protected virtual IEnumerable CreateModificationCommands(
}
command = sharedCommandsMap.GetOrAddValue(entry);
- isMainEntry = sharedCommandsMap.IsMainEntityType(entry.EntityType.GetRootType());
+ isMainEntry = sharedCommandsMap.IsMainEntry(entry);
}
else
{
@@ -245,8 +245,7 @@ private void AddUnchangedSharingEntries(
entry.EntityState = EntityState.Modified;
- var isMainEntry = sharedCommandsMap.IsMainEntityType(entry.EntityType.GetRootType());
- command.AddEntry(entry, isMainEntry);
+ command.AddEntry(entry, sharedCommandsMap.IsMainEntry(entry));
entries.Add(entry);
}
}
diff --git a/src/EFCore.Relational/Update/Internal/SharedTableEntryMap.cs b/src/EFCore.Relational/Update/Internal/SharedTableEntryMap.cs
index c9606345b11..5e9d7f0b224 100644
--- a/src/EFCore.Relational/Update/Internal/SharedTableEntryMap.cs
+++ b/src/EFCore.Relational/Update/Internal/SharedTableEntryMap.cs
@@ -118,11 +118,11 @@ public virtual TValue GetOrAddValue([NotNull] IUpdateEntry entry)
/// 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 bool IsMainEntityType([NotNull] IEntityType entityType) => !_table.GetInternalForeignKeys(entityType).Any();
+ public virtual bool IsMainEntry([NotNull] IUpdateEntry entry) => !_table.GetInternalForeignKeys(entry.EntityType).Any();
private IUpdateEntry GetMainEntry(IUpdateEntry entry)
{
- var entityType = entry.EntityType.GetRootType();
+ var entityType = entry.EntityType;
var foreignKeys = _table.GetInternalForeignKeys(entityType);
foreach (var foreignKey in foreignKeys)
{
diff --git a/src/EFCore.Relational/Update/UpdateSqlGenerator.cs b/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
index d809e6259a3..217114483f8 100644
--- a/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
+++ b/src/EFCore.Relational/Update/UpdateSqlGenerator.cs
@@ -333,7 +333,7 @@ protected virtual void AppendUpdateCommandHeader(
sb.Append(" = ");
if (!o.UseCurrentValueParameter)
{
- AppendSqlLiteral(sb, o.Value, o.Property);
+ AppendSqlLiteral(sb, o);
}
else
{
@@ -423,7 +423,7 @@ protected virtual void AppendValues(
{
if (!o.UseCurrentValueParameter)
{
- AppendSqlLiteral(sb, o.Value, o.Property);
+ AppendSqlLiteral(sb, o);
}
else
{
@@ -541,7 +541,7 @@ protected virtual void AppendWhereCondition(
if (!columnModification.UseCurrentValueParameter
&& !columnModification.UseOriginalValueParameter)
{
- AppendSqlLiteral(commandStringBuilder, columnModification.Value, columnModification.Property);
+ AppendSqlLiteral(commandStringBuilder, columnModification);
}
else
{
@@ -596,12 +596,14 @@ public virtual void AppendNextSequenceValueOperation(StringBuilder commandString
SqlGenerationHelper.DelimitIdentifier(commandStringBuilder, Check.NotNull(name, nameof(name)), schema);
}
- private void AppendSqlLiteral(StringBuilder commandStringBuilder, object value, IProperty property)
+ private void AppendSqlLiteral(StringBuilder commandStringBuilder, ColumnModification modification)
{
- var mapping = property != null
- ? Dependencies.TypeMappingSource.FindMapping(property)
- : null;
- mapping ??= Dependencies.TypeMappingSource.GetMappingForValue(value);
+ var value = modification.Value;
+ var mapping = modification.Property != null
+ ? (RelationalTypeMapping)modification.Property.GetTypeMapping()
+ : value != null
+ ? Dependencies.TypeMappingSource.GetMapping(value.GetType(), modification.ColumnType)
+ : Dependencies.TypeMappingSource.GetMapping(modification.ColumnType);
commandStringBuilder.Append(mapping.GenerateProviderValueSqlLiteral(value));
}
}
diff --git a/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerNetTopologySuiteTypeMappingSourcePlugin.cs b/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerNetTopologySuiteTypeMappingSourcePlugin.cs
index 8e9ed5c110b..99b3b784810 100644
--- a/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerNetTopologySuiteTypeMappingSourcePlugin.cs
+++ b/src/EFCore.SqlServer.NTS/Storage/Internal/SqlServerNetTopologySuiteTypeMappingSourcePlugin.cs
@@ -56,14 +56,14 @@ public SqlServerNetTopologySuiteTypeMappingSourcePlugin([NotNull] NtsGeometrySer
///
public virtual RelationalTypeMapping FindMapping(in RelationalTypeMappingInfo mappingInfo)
{
- var clrType = mappingInfo.ClrType ?? typeof(Geometry);
+ var clrType = mappingInfo.ClrType;
var storeTypeName = mappingInfo.StoreTypeName;
return typeof(Geometry).IsAssignableFrom(clrType)
|| (storeTypeName != null
&& _spatialStoreTypes.Contains(storeTypeName))
? (RelationalTypeMapping)Activator.CreateInstance(
- typeof(SqlServerGeometryTypeMapping<>).MakeGenericType(clrType),
+ typeof(SqlServerGeometryTypeMapping<>).MakeGenericType(clrType ?? typeof(Geometry)),
_geometryServices,
storeTypeName ?? "geography")
: null;
diff --git a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
index 2b49b8d32a5..fd7dbba0163 100644
--- a/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
+++ b/src/EFCore.SqlServer/Migrations/SqlServerMigrationsSqlGenerator.cs
@@ -1232,7 +1232,7 @@ protected override void Generate(
var sqlBuilder = new StringBuilder();
((SqlServerUpdateSqlGenerator)Dependencies.UpdateSqlGenerator).AppendBulkInsertOperation(
sqlBuilder,
- operation.GenerateModificationCommands(model).ToList(),
+ GenerateModificationCommands(operation, model).ToList(),
0);
builder.Append(sqlBuilder.ToString());
diff --git a/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs b/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs
index 500f9973e05..b54af001bb5 100644
--- a/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs
+++ b/src/EFCore.Sqlite.Core/Migrations/SqliteMigrationsSqlGenerator.cs
@@ -228,7 +228,7 @@ protected override void Generate(
/// The command builder to use to build the commands.
protected override void Generate(RenameIndexOperation operation, IModel model, MigrationCommandListBuilder builder)
{
- var index = model.GetRelationalModel().FindTable(operation.Table, operation.Schema)
+ var index = model?.GetRelationalModel().FindTable(operation.Table, operation.Schema)
?.Indexes.FirstOrDefault(i => i.Name == operation.NewName);
if (index == null)
{
diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductTableView.cs b/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductTableView.cs
new file mode 100644
index 00000000000..73b87797d60
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductTableView.cs
@@ -0,0 +1,9 @@
+// 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.
+
+namespace Microsoft.EntityFrameworkCore.TestModels.UpdatesModel
+{
+ public class ProductTableView : Product
+ {
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductView.cs b/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductView.cs
new file mode 100644
index 00000000000..a0287a83b9b
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductView.cs
@@ -0,0 +1,9 @@
+// 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.
+
+namespace Microsoft.EntityFrameworkCore.TestModels.UpdatesModel
+{
+ public class ProductTableWithView : Product
+ {
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductViewTable.cs b/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductViewTable.cs
new file mode 100644
index 00000000000..f66ce29281e
--- /dev/null
+++ b/test/EFCore.Relational.Specification.Tests/TestModels/Updates/ProductViewTable.cs
@@ -0,0 +1,9 @@
+// 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.
+
+namespace Microsoft.EntityFrameworkCore.TestModels.UpdatesModel
+{
+ public class ProductViewTable : Product
+ {
+ }
+}
diff --git a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalFixture.cs b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalFixture.cs
index fd8cb23a6e9..a5ed1b63705 100644
--- a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalFixture.cs
+++ b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalFixture.cs
@@ -14,6 +14,10 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
{
base.OnModelCreating(modelBuilder, context);
+ modelBuilder.Entity().HasBaseType((string)null).ToTable("ProductView");
+ modelBuilder.Entity().HasBaseType((string)null).ToView("ProductView").ToTable("ProductTable");
+ modelBuilder.Entity().HasBaseType((string)null).ToView("ProductTable");
+
modelBuilder
.Entity<
LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWorkingCorrectlyDetails
diff --git a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs
index 6ef0b795dbe..05bb93cd390 100644
--- a/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs
+++ b/test/EFCore.Relational.Specification.Tests/UpdatesRelationalTestBase.cs
@@ -1,9 +1,12 @@
// 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 System;
+using System.Linq;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
+using Microsoft.EntityFrameworkCore.TestModels.UpdatesModel;
using Xunit;
// ReSharper disable InconsistentNaming
@@ -17,6 +20,63 @@ protected UpdatesRelationalTestBase(TFixture fixture)
{
}
+ [ConditionalFact]
+ public virtual void SaveChanges_works_for_entities_also_mapped_to_view()
+ {
+ ExecuteWithStrategyInTransaction(
+ context =>
+ {
+ var category = context.Categories.Single();
+
+ context.Add(
+ new ProductTableWithView
+ {
+ Id = Guid.NewGuid(),
+ Name = "Pear Cider",
+ Price = 1.39M,
+ DependentId = category.Id
+ });
+ context.Add(
+ new ProductViewTable
+ {
+ Id = Guid.NewGuid(),
+ Name = "Pear Cobler",
+ Price = 2.39M,
+ DependentId = category.Id
+ });
+
+ context.SaveChanges();
+ },
+ context =>
+ {
+ var viewProduct = context.Set().Single();
+ var tableProduct = context.Set().Single();
+
+ Assert.Equal("Pear Cider", tableProduct.Name);
+ Assert.Equal("Pear Cobler", viewProduct.Name);
+ });
+ }
+
+ [ConditionalFact]
+ public virtual void SaveChanges_throws_for_entities_only_mapped_to_view()
+ {
+ ExecuteWithStrategyInTransaction(
+ context =>
+ {
+ var category = context.Categories.Single();
+ context.Add(
+ new ProductTableView
+ {
+ Id = Guid.NewGuid(),
+ Name = "Pear Cider",
+ Price = 1.39M,
+ DependentId = category.Id
+ });
+
+ Assert.Throws(() => context.SaveChanges());
+ });
+ }
+
[ConditionalFact]
public abstract void Identifiers_are_generated_correctly();
diff --git a/test/EFCore.Relational.Tests/Migrations/MigrationCommandListBuilderTest.cs b/test/EFCore.Relational.Tests/Migrations/MigrationCommandListBuilderTest.cs
index 87ff10c91d8..c38ebe02d2a 100644
--- a/test/EFCore.Relational.Tests/Migrations/MigrationCommandListBuilderTest.cs
+++ b/test/EFCore.Relational.Tests/Migrations/MigrationCommandListBuilderTest.cs
@@ -138,6 +138,7 @@ private MigrationCommandListBuilder CreateBuilder()
generationHelper,
typeMappingSource,
new CurrentDbContext(new FakeDbContext()),
+ new LoggingOptions(),
logger));
}
diff --git a/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs b/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs
index 1cb744fe88c..acd5a7fef46 100644
--- a/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs
+++ b/test/EFCore.Relational.Tests/Migrations/MigrationSqlGeneratorTestBase.cs
@@ -3,6 +3,9 @@
using System;
using System.Linq;
+using System.Reflection;
+using Microsoft.EntityFrameworkCore.Diagnostics;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Conventions;
using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure;
@@ -10,6 +13,7 @@
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.TestUtilities;
using Microsoft.Extensions.DependencyInjection;
+using NetTopologySuite.Geometries;
using Xunit;
namespace Microsoft.EntityFrameworkCore.Migrations
@@ -20,6 +24,36 @@ public abstract class MigrationSqlGeneratorTestBase
protected virtual string Sql { get; set; }
+ [ConditionalFact]
+ public void All_tests_must_be_overriden()
+ {
+ var baseTests = GetType().GetMethods(BindingFlags.Instance | BindingFlags.Public)
+ .Where(method => method.IsVirtual && !method.IsFinal && method.DeclaringType == typeof(MigrationSqlGeneratorTestBase))
+ .ToList();
+
+ Assert.True(baseTests.Count == 0, $"{GetType().ShortDisplayName()} should override the following methods to assert the generated SQL:" + EOL
+ + string.Join(EOL, baseTests.Select(m => m.Name)));
+ }
+
+ [ConditionalFact]
+ public void CreateTableOperation_throws_for_column_mismatch()
+ => Assert.Equal(RelationalStrings.MigrationColumnTableMismatch("Id", "Blog", "Post"),
+ Assert.Throws(() =>
+ Generate(
+ new CreateTableOperation
+ {
+ Name = "Post",
+ Columns =
+ {
+ new AddColumnOperation
+ {
+ Name = "Id",
+ Table = "Blog",
+ ClrType = typeof(int)
+ }
+ }
+ })).Message);
+
[ConditionalFact]
public virtual void AddColumnOperation_without_column_type()
=> Generate(
@@ -166,12 +200,487 @@ public virtual void SqlOperation()
=> Generate(
new SqlOperation { Sql = "-- I <3 DDL" });
+ private static readonly LineString _lineString1 = new LineString(
+ new[] { new Coordinate(1.1, 2.2), new Coordinate(2.2, 2.2), new Coordinate(2.2, 1.1), new Coordinate(7.1, 7.2) })
+ {
+ SRID = 4326
+ };
+
+ private static readonly LineString _lineString2 = new LineString(
+ new[] { new Coordinate(7.1, 7.2), new Coordinate(20.2, 20.2), new Coordinate(20.20, 1.1), new Coordinate(70.1, 70.2) })
+ {
+ SRID = 4326
+ };
+
+ private static readonly MultiPoint _multiPoint = new MultiPoint(
+ new[] { new Point(1.1, 2.2), new Point(2.2, 2.2), new Point(2.2, 1.1) })
+ { SRID = 4326 };
+
+ private static readonly Polygon _polygon1 = new Polygon(
+ new LinearRing(
+ new[] { new Coordinate(1.1, 2.2), new Coordinate(2.2, 2.2), new Coordinate(2.2, 1.1), new Coordinate(1.1, 2.2) }))
+ {
+ SRID = 4326
+ };
+
+ private static readonly Polygon _polygon2 = new Polygon(
+ new LinearRing(
+ new[] { new Coordinate(10.1, 20.2), new Coordinate(20.2, 20.2), new Coordinate(20.2, 10.1), new Coordinate(10.1, 20.2) }))
+ {
+ SRID = 4326
+ };
+
+ private static readonly Point _point1 = new Point(1.1, 2.2, 3.3) { SRID = 4326 };
+
+ private static readonly MultiLineString _multiLineString = new MultiLineString(
+ new[] { _lineString1, _lineString2 })
+ { SRID = 4326 };
+
+ private static readonly MultiPolygon _multiPolygon = new MultiPolygon(
+ new[] { _polygon2, _polygon1 })
+ { SRID = 4326 };
+
+ private static readonly GeometryCollection _geometryCollection = new GeometryCollection(
+ new Geometry[] { _lineString1, _lineString2, _multiPoint, _polygon1, _polygon2, _point1, _multiLineString, _multiPolygon })
+ {
+ SRID = 4326
+ };
+
+ [ConditionalFact]
+ public virtual void InsertDataOperation_all_args_spatial()
+ => Generate(
+ new InsertDataOperation
+ {
+ Schema = "dbo",
+ Table = "People",
+ Columns = new[] { "Id", "Full Name", "Geometry" },
+ ColumnTypes = new[] { "int", "varchar(40)", GetGeometryCollectionStoreType() },
+ Values = new object[,]
+ {
+ { 0, null, null },
+ { 1, "Daenerys Targaryen", null },
+ { 2, "John Snow", null },
+ { 3, "Arya Stark", null },
+ { 4, "Harry Strickland", null },
+ { 5, "The Imp", null },
+ { 6, "The Kingslayer", null },
+ { 7, "Aemon Targaryen", _geometryCollection }
+ }
+ });
+
+ protected abstract string GetGeometryCollectionStoreType();
+
+ [ConditionalFact]
+ public virtual void InsertDataOperation_required_args()
+ => Generate(
+ CreateGotModel,
+ new InsertDataOperation
+ {
+ Table = "People",
+ Columns = new[] { "First Name" },
+ Values = new object[,] { { "John" } }
+ });
+
+ [ConditionalFact]
+ public virtual void InsertDataOperation_required_args_composite()
+ => Generate(
+ CreateGotModel,
+ new InsertDataOperation
+ {
+ Table = "People",
+ Columns = new[] { "First Name", "Last Name" },
+ Values = new object[,] { { "John", "Snow" } }
+ });
+
+ [ConditionalFact]
+ public virtual void InsertDataOperation_required_args_multiple_rows()
+ => Generate(
+ CreateGotModel,
+ new InsertDataOperation
+ {
+ Table = "People",
+ Columns = new[] { "First Name" },
+ Values = new object[,] { { "John" }, { "Daenerys" } }
+ });
+
+ [ConditionalFact]
+ public void InsertDataOperation_throws_for_missing_column_types()
+ => Assert.Equal(RelationalStrings.InsertDataOperationNoModel("dbo.People"),
+ Assert.Throws(() =>
+ Generate(
+ new InsertDataOperation
+ {
+ Table = "People",
+ Schema = "dbo",
+ Columns = new[] { "First Name" },
+ Values = new object[,] { { "John" } }
+ })).Message);
+
+ [ConditionalFact]
+ public virtual void InsertDataOperation_throws_for_unsupported_column_types()
+ => Assert.Equal(RelationalStrings.UnsupportedStoreType("char[]"),
+ Assert.Throws(() =>
+ Generate(
+ new InsertDataOperation
+ {
+ Table = "People",
+ Schema = "dbo",
+ Columns = new[] { "First Name" },
+ ColumnTypes = new[] { "char[]" },
+ Values = new object[,] { { null } }
+ })).Message);
+
+ [ConditionalFact]
+ public void InsertDataOperation_throws_for_values_count_mismatch()
+ => Assert.Equal(RelationalStrings.InsertDataOperationValuesCountMismatch(1, 2, "People"),
+ Assert.Throws(() =>
+ Generate(
+ CreateGotModel,
+ new InsertDataOperation
+ {
+ Table = "People",
+ Columns = new[] { "First Name", "Last Name" },
+ Values = new object[,] { { "John" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void InsertDataOperation_throws_for_types_count_mismatch()
+ => Assert.Equal(RelationalStrings.InsertDataOperationTypesCountMismatch(2, 1, "People"),
+ Assert.Throws(() =>
+ Generate(
+ new InsertDataOperation
+ {
+ Table = "People",
+ Columns = new[] { "First Name" },
+ ColumnTypes = new[] { "string", "string" },
+ Values = new object[,] { { "John" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void InsertDataOperation_throws_for_missing_entity_type()
+ => Assert.Equal(RelationalStrings.DataOperationNoTable("dbo.People"),
+ Assert.Throws(() =>
+ Generate(
+ CreateGotModel,
+ new InsertDataOperation
+ {
+ Table = "People",
+ Schema = "dbo",
+ Columns = new[] { "First Name" },
+ Values = new object[,] { { "John" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void InsertDataOperation_throws_for_missing_property()
+ => Assert.Equal(RelationalStrings.DataOperationNoProperty("People", "Name"),
+ Assert.Throws(() =>
+ Generate(
+ CreateGotModel,
+ new InsertDataOperation
+ {
+ Table = "People",
+ Columns = new[] { "Name" },
+ Values = new object[,] { { "John" } }
+ })).Message);
+
+ [ConditionalFact]
+ public virtual void DeleteDataOperation_all_args()
+ => Generate(
+ CreateGotModel,
+ new DeleteDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Hodor" }, { "Daenerys" }, { "John" }, { "Arya" }, { "Harry" } }
+ });
+
+ [ConditionalFact]
+ public virtual void DeleteDataOperation_all_args_composite()
+ => Generate(
+ CreateGotModel,
+ new DeleteDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,]
+ {
+ { "Hodor", null }, { "Daenerys", "Targaryen" }, { "John", "Snow" }, { "Arya", "Stark" }, { "Harry", "Strickland" }
+ }
+ });
+
+ [ConditionalFact]
+ public virtual void DeleteDataOperation_required_args()
+ => Generate(
+ CreateGotModel,
+ new DeleteDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "Last Name" },
+ KeyValues = new object[,] { { "Snow" } }
+ });
+
+ [ConditionalFact]
+ public virtual void DeleteDataOperation_required_args_composite()
+ => Generate(
+ CreateGotModel,
+ new DeleteDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,] { { "John", "Snow" } }
+ });
+
+ [ConditionalFact]
+ public void DeleteDataOperation_throws_for_missing_column_types()
+ => Assert.Equal(RelationalStrings.DeleteDataOperationNoModel("People"),
+ Assert.Throws(() =>
+ Generate(
+ new DeleteDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "John" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void DeleteDataOperation_throws_for_values_count_mismatch()
+ => Assert.Equal(RelationalStrings.DeleteDataOperationValuesCountMismatch(1, 2, "People"),
+ Assert.Throws(() =>
+ Generate(
+ CreateGotModel,
+ new DeleteDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,] { { "John" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void DeleteDataOperation_throws_for_types_count_mismatch()
+ => Assert.Equal(RelationalStrings.DeleteDataOperationTypesCountMismatch(2, 1, "People"),
+ Assert.Throws(() =>
+ Generate(
+ new DeleteDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyColumnTypes = new[] { "string", "string" },
+ KeyValues = new object[,] { { "John" } }
+ })).Message);
+
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_all_args()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Hodor" }, { "Daenerys" } },
+ Columns = new[] { "Birthplace", "House Allegiance", "Culture" },
+ Values = new object[,] { { "Winterfell", "Stark", "Northmen" }, { "Dragonstone", "Targaryen", "Valyrian" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_all_args_composite()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,] { { "Hodor", null }, { "Daenerys", "Targaryen" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Stark" }, { "Targaryen" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_all_args_composite_multi()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,] { { "Hodor", null }, { "Daenerys", "Targaryen" } },
+ Columns = new[] { "Birthplace", "House Allegiance", "Culture" },
+ Values = new object[,] { { "Winterfell", "Stark", "Northmen" }, { "Dragonstone", "Targaryen", "Valyrian" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_all_args_multi()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "Birthplace", "House Allegiance", "Culture" },
+ Values = new object[,] { { "Dragonstone", "Targaryen", "Valyrian" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_required_args()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Targaryen" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_required_args_multiple_rows()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Hodor" }, { "Daenerys" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Stark" }, { "Targaryen" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_required_args_composite()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,] { { "Daenerys", "Targaryen" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Targaryen" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_required_args_composite_multi()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,] { { "Daenerys", "Targaryen" } },
+ Columns = new[] { "Birthplace", "House Allegiance", "Culture" },
+ Values = new object[,] { { "Dragonstone", "Targaryen", "Valyrian" } }
+ });
+
+ [ConditionalFact]
+ public virtual void UpdateDataOperation_required_args_multi()
+ => Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "Birthplace", "House Allegiance", "Culture" },
+ Values = new object[,] { { "Dragonstone", "Targaryen", "Valyrian" } }
+ });
+
+ [ConditionalFact]
+ public void UpdateDataOperation_throws_for_missing_column_types()
+ => Assert.Equal(RelationalStrings.UpdateDataOperationNoModel("People"),
+ Assert.Throws(() =>
+ Generate(
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Targaryen" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void UpdateDataOperation_throws_for_row_count_mismatch()
+ => Assert.Equal(RelationalStrings.UpdateDataOperationRowCountMismatch(1, 2, "People"),
+ Assert.Throws(() =>
+ Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyColumnTypes = new[] { "string" },
+ KeyValues = new object[,] { { "Daenerys" }, { "John" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Targaryen" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void UpdateDataOperation_throws_for_key_values_count_mismatch()
+ => Assert.Equal(RelationalStrings.UpdateDataOperationKeyValuesCountMismatch(1, 2, "People"),
+ Assert.Throws(() =>
+ Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name", "Last Name" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Targaryen" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void UpdateDataOperation_throws_for_key_types_count_mismatch()
+ => Assert.Equal(RelationalStrings.UpdateDataOperationKeyTypesCountMismatch(2, 1, "People"),
+ Assert.Throws(() =>
+ Generate(
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyColumnTypes = new[] { "string", "string" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "House Allegiance" },
+ Values = new object[,] { { "Targaryen" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void UpdateDataOperation_throws_for_values_count_mismatch()
+ => Assert.Equal(RelationalStrings.UpdateDataOperationValuesCountMismatch(1, 2, "People"),
+ Assert.Throws(() =>
+ Generate(
+ CreateGotModel,
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "House Allegiance", "Culture" },
+ Values = new object[,] { { "Targaryen" } }
+ })).Message);
+
+ [ConditionalFact]
+ public void UpdateDataOperation_throws_for_types_count_mismatch()
+ => Assert.Equal(RelationalStrings.UpdateDataOperationTypesCountMismatch(2, 1, "People"),
+ Assert.Throws(() =>
+ Generate(
+ new UpdateDataOperation
+ {
+ Table = "People",
+ KeyColumns = new[] { "First Name" },
+ KeyValues = new object[,] { { "Daenerys" } },
+ Columns = new[] { "House Allegiance" },
+ ColumnTypes = new[] { "string", "string" },
+ Values = new object[,] { { "Targaryen" } }
+ })).Message);
+
[ConditionalTheory]
[InlineData(false)]
[InlineData(true)]
public virtual void DefaultValue_with_line_breaks(bool isUnicode)
- {
- Generate(
+ => Generate(
new CreateTableOperation
{
Name = "TestLineBreaks",
@@ -181,40 +690,69 @@ public virtual void DefaultValue_with_line_breaks(bool isUnicode)
new AddColumnOperation
{
Name = "TestDefaultValue",
- Table = "Test",
+ Table = "TestLineBreaks",
+ Schema = "dbo",
ClrType = typeof(string),
DefaultValue = "\r\nVarious Line\rBreaks\n",
IsUnicode = isUnicode
}
}
});
+
+
+ private static void CreateGotModel(ModelBuilder b)
+ {
+ b.Entity("Person", pb =>
+ {
+ pb.ToTable("People");
+ pb.Property("FirstName").HasColumnName("First Name");
+ pb.Property("LastName").HasColumnName("Last Name");
+ pb.Property("Birthplace").HasColumnName("Birthplace");
+ pb.Property("Allegiance").HasColumnName("House Allegiance");
+ pb.Property("Culture").HasColumnName("Culture");
+ pb.HasKey("FirstName", "LastName");
+ });
}
protected TestHelpers TestHelpers { get; }
+ protected DbContextOptions ContextOptions { get; }
+ protected IServiceCollection CustomServices { get; }
- protected MigrationSqlGeneratorTestBase(TestHelpers testHelpers)
+ protected MigrationSqlGeneratorTestBase(
+ TestHelpers testHelpers, IServiceCollection customServices = null, DbContextOptions options = null)
{
TestHelpers = testHelpers;
+ CustomServices = customServices;
+ ContextOptions = options;
}
protected virtual void Generate(params MigrationOperation[] operation)
- => Generate(_ => { }, operation);
+ => Generate(null, operation);
protected virtual void Generate(Action buildAction, params MigrationOperation[] operation)
{
- var modelBuilder = TestHelpers.CreateConventionBuilder();
- modelBuilder.Model.RemoveAnnotation(CoreAnnotationNames.ProductVersion);
- buildAction(modelBuilder);
+ var services = ContextOptions != null
+ ? TestHelpers.CreateContextServices(CustomServices, ContextOptions)
+ : TestHelpers.CreateContextServices(CustomServices);
+
+ IModel model = null;
+ if (buildAction != null)
+ {
+ var modelBuilder = TestHelpers.CreateConventionBuilder();
+ modelBuilder.Model.RemoveAnnotation(CoreAnnotationNames.ProductVersion);
+ buildAction(modelBuilder);
+
+ model = modelBuilder.Model;
+ var conventionSet = services.GetRequiredService().CreateConventionSet();
- var services = TestHelpers.CreateContextServices();
+ var typeMappingConvention = conventionSet.ModelFinalizingConventions.OfType().FirstOrDefault();
+ typeMappingConvention.ProcessModelFinalizing(((IConventionModel)model).Builder, null);
- IModel model = modelBuilder.Model;
- var conventionSet = services.GetRequiredService().CreateConventionSet();
- var relationalModelConvention = conventionSet.ModelFinalizedConventions.OfType().First();
- model = relationalModelConvention.ProcessModelFinalized((IConventionModel)model);
- model = ((IMutableModel)model).FinalizeModel();
+ var relationalModelConvention = conventionSet.ModelFinalizedConventions.OfType().First();
+ model = relationalModelConvention.ProcessModelFinalized((IConventionModel)model);
+ }
- var batch = services.GetRequiredService().Generate(operation, modelBuilder.Model);
+ var batch = services.GetRequiredService().Generate(operation, model);
Sql = string.Join(
"GO" + EOL + EOL,
diff --git a/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerFixture.cs b/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerFixture.cs
index 9b4651386ad..04af68dcee0 100644
--- a/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerFixture.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/UpdatesSqlServerFixture.cs
@@ -16,6 +16,13 @@ protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext con
modelBuilder.Entity()
.Property(p => p.Price).HasColumnType("decimal(18,2)");
+
+ modelBuilder.Entity()
+ .Property(p => p.Price).HasColumnType("decimal(18,2)");
+ modelBuilder.Entity()
+ .Property(p => p.Price).HasColumnType("decimal(18,2)");
+ modelBuilder.Entity()
+ .Property(p => p.Price).HasColumnType("decimal(18,2)");
modelBuilder
.Entity<
LoginEntityTypeWithAnExtremelyLongAndOverlyConvolutedNameThatIsUsedToVerifyThatTheStoreIdentifierGenerationLengthLimitIsWorkingCorrectly
diff --git a/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs b/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs
index c1d0481fc11..02326069cdf 100644
--- a/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs
+++ b/test/EFCore.SqlServer.Tests/Migrations/SqlServerMigrationSqlGeneratorTest.cs
@@ -3,12 +3,14 @@
using System;
using System.IO;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.SqlServer.Internal;
using Microsoft.EntityFrameworkCore.SqlServer.Metadata.Internal;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.EntityFrameworkCore.Migrations
@@ -54,6 +56,24 @@ public override void AddColumnOperation_with_unicode_no_model()
");
}
+ public override void AddColumnOperation_with_fixed_length_no_model()
+ {
+ base.AddColumnOperation_with_fixed_length_no_model();
+
+ AssertSql(
+ @"ALTER TABLE [Person] ADD [Name] char(100) NULL;
+");
+ }
+
+ public override void AddColumnOperation_with_maxLength_no_model()
+ {
+ base.AddColumnOperation_with_maxLength_no_model();
+
+ AssertSql(
+ @"ALTER TABLE [Person] ADD [Name] nvarchar(30) NULL;
+");
+ }
+
public override void AddColumnOperation_with_maxLength_overridden()
{
base.AddColumnOperation_with_maxLength_overridden();
@@ -142,6 +162,15 @@ FROM [sys].[default_constraints] [d]
");
}
+ public override void AddForeignKeyOperation_without_principal_columns()
+ {
+ base.AddForeignKeyOperation_without_principal_columns();
+
+ AssertSql(
+ @"ALTER TABLE [People] ADD FOREIGN KEY ([SpouseId]) REFERENCES [People];
+");
+ }
+
[ConditionalFact]
public virtual void AlterColumnOperation_with_identity_legacy()
{
@@ -502,6 +531,15 @@ public override void RenameTableOperation_legacy()
");
}
+ public override void RenameTableOperation()
+ {
+ base.RenameTableOperation();
+
+ AssertSql(
+ @"EXEC sp_rename N'[dbo].[People]', N'Person';
+");
+ }
+
[ConditionalFact]
public virtual void SqlOperation_handles_backslash()
{
@@ -560,6 +598,291 @@ public virtual void SqlOperation_ignores_non_go()
AssertSql(
@"-- I GO 2
+");
+ }
+
+ public override void SqlOperation()
+ {
+ base.SqlOperation();
+
+ AssertSql(
+ @"-- I <3 DDL
+");
+ }
+
+ public override void InsertDataOperation_all_args_spatial()
+ {
+ base.InsertDataOperation_all_args_spatial();
+
+ AssertSql(
+ @"IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Full Name', N'Geometry') AND [object_id] = OBJECT_ID(N'[dbo].[People]'))
+ SET IDENTITY_INSERT [dbo].[People] ON;
+INSERT INTO [dbo].[People] ([Id], [Full Name], [Geometry])
+VALUES (0, NULL, NULL),
+(1, 'Daenerys Targaryen', NULL),
+(2, 'John Snow', NULL),
+(3, 'Arya Stark', NULL),
+(4, 'Harry Strickland', NULL),
+(5, 'The Imp', NULL),
+(6, 'The Kingslayer', NULL),
+(7, 'Aemon Targaryen', geography::Parse('GEOMETRYCOLLECTION (LINESTRING (1.1 2.2 NULL, 2.2 2.2 NULL, 2.2 1.1 NULL, 7.1 7.2 NULL), LINESTRING (7.1 7.2 NULL, 20.2 20.2 NULL, 20.2 1.1 NULL, 70.1 70.2 NULL), MULTIPOINT ((1.1 2.2 NULL), (2.2 2.2 NULL), (2.2 1.1 NULL)), POLYGON ((1.1 2.2 NULL, 2.2 2.2 NULL, 2.2 1.1 NULL, 1.1 2.2 NULL)), POLYGON ((10.1 20.2 NULL, 20.2 20.2 NULL, 20.2 10.1 NULL, 10.1 20.2 NULL)), POINT (1.1 2.2 3.3), MULTILINESTRING ((1.1 2.2 NULL, 2.2 2.2 NULL, 2.2 1.1 NULL, 7.1 7.2 NULL), (7.1 7.2 NULL, 20.2 20.2 NULL, 20.2 1.1 NULL, 70.1 70.2 NULL)), MULTIPOLYGON (((10.1 20.2 NULL, 20.2 20.2 NULL, 20.2 10.1 NULL, 10.1 20.2 NULL)), ((1.1 2.2 NULL, 2.2 2.2 NULL, 2.2 1.1 NULL, 1.1 2.2 NULL))))'));
+IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'Id', N'Full Name', N'Geometry') AND [object_id] = OBJECT_ID(N'[dbo].[People]'))
+ SET IDENTITY_INSERT [dbo].[People] OFF;
+");
+ }
+
+ protected override string GetGeometryCollectionStoreType()
+ => "geography";
+
+ public override void InsertDataOperation_required_args()
+ {
+ base.InsertDataOperation_required_args();
+
+ AssertSql(
+ @"IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'First Name') AND [object_id] = OBJECT_ID(N'[People]'))
+ SET IDENTITY_INSERT [People] ON;
+INSERT INTO [People] ([First Name])
+VALUES (N'John');
+IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'First Name') AND [object_id] = OBJECT_ID(N'[People]'))
+ SET IDENTITY_INSERT [People] OFF;
+");
+ }
+
+ public override void InsertDataOperation_required_args_composite()
+ {
+ base.InsertDataOperation_required_args_composite();
+
+ AssertSql(
+ @"IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'First Name', N'Last Name') AND [object_id] = OBJECT_ID(N'[People]'))
+ SET IDENTITY_INSERT [People] ON;
+INSERT INTO [People] ([First Name], [Last Name])
+VALUES (N'John', N'Snow');
+IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'First Name', N'Last Name') AND [object_id] = OBJECT_ID(N'[People]'))
+ SET IDENTITY_INSERT [People] OFF;
+");
+ }
+
+ public override void InsertDataOperation_required_args_multiple_rows()
+ {
+ base.InsertDataOperation_required_args_multiple_rows();
+
+ AssertSql(
+ @"IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'First Name') AND [object_id] = OBJECT_ID(N'[People]'))
+ SET IDENTITY_INSERT [People] ON;
+INSERT INTO [People] ([First Name])
+VALUES (N'John'),
+(N'Daenerys');
+IF EXISTS (SELECT * FROM [sys].[identity_columns] WHERE [name] IN (N'First Name') AND [object_id] = OBJECT_ID(N'[People]'))
+ SET IDENTITY_INSERT [People] OFF;
+");
+ }
+
+ public override void InsertDataOperation_throws_for_unsupported_column_types()
+ {
+ base.InsertDataOperation_throws_for_unsupported_column_types();
+ }
+
+ public override void DeleteDataOperation_all_args()
+ {
+ base.DeleteDataOperation_all_args();
+
+ AssertSql(
+ @"DELETE FROM [People]
+WHERE [First Name] = N'Hodor';
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'Daenerys';
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'John';
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'Arya';
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'Harry';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void DeleteDataOperation_all_args_composite()
+ {
+ base.DeleteDataOperation_all_args_composite();
+
+ AssertSql(
+ @"DELETE FROM [People]
+WHERE [First Name] = N'Hodor' AND [Last Name] IS NULL;
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'Daenerys' AND [Last Name] = N'Targaryen';
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'John' AND [Last Name] = N'Snow';
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'Arya' AND [Last Name] = N'Stark';
+SELECT @@ROWCOUNT;
+
+DELETE FROM [People]
+WHERE [First Name] = N'Harry' AND [Last Name] = N'Strickland';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void DeleteDataOperation_required_args()
+ {
+ base.DeleteDataOperation_required_args();
+
+ AssertSql(
+ @"DELETE FROM [People]
+WHERE [Last Name] = N'Snow';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void DeleteDataOperation_required_args_composite()
+ {
+ base.DeleteDataOperation_required_args_composite();
+
+ AssertSql(
+ @"DELETE FROM [People]
+WHERE [First Name] = N'John' AND [Last Name] = N'Snow';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args()
+ {
+ base.UpdateDataOperation_all_args();
+
+ AssertSql(
+ @"UPDATE [People] SET [Birthplace] = N'Winterfell', [House Allegiance] = N'Stark', [Culture] = N'Northmen'
+WHERE [First Name] = N'Hodor';
+SELECT @@ROWCOUNT;
+
+UPDATE [People] SET [Birthplace] = N'Dragonstone', [House Allegiance] = N'Targaryen', [Culture] = N'Valyrian'
+WHERE [First Name] = N'Daenerys';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args_composite()
+ {
+ base.UpdateDataOperation_all_args_composite();
+
+ AssertSql(
+ @"UPDATE [People] SET [House Allegiance] = N'Stark'
+WHERE [First Name] = N'Hodor' AND [Last Name] IS NULL;
+SELECT @@ROWCOUNT;
+
+UPDATE [People] SET [House Allegiance] = N'Targaryen'
+WHERE [First Name] = N'Daenerys' AND [Last Name] = N'Targaryen';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args_composite_multi()
+ {
+ base.UpdateDataOperation_all_args_composite_multi();
+
+ AssertSql(
+ @"UPDATE [People] SET [Birthplace] = N'Winterfell', [House Allegiance] = N'Stark', [Culture] = N'Northmen'
+WHERE [First Name] = N'Hodor' AND [Last Name] IS NULL;
+SELECT @@ROWCOUNT;
+
+UPDATE [People] SET [Birthplace] = N'Dragonstone', [House Allegiance] = N'Targaryen', [Culture] = N'Valyrian'
+WHERE [First Name] = N'Daenerys' AND [Last Name] = N'Targaryen';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args_multi()
+ {
+ base.UpdateDataOperation_all_args_multi();
+
+ AssertSql(
+ @"UPDATE [People] SET [Birthplace] = N'Dragonstone', [House Allegiance] = N'Targaryen', [Culture] = N'Valyrian'
+WHERE [First Name] = N'Daenerys';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args()
+ {
+ base.UpdateDataOperation_required_args();
+
+ AssertSql(
+ @"UPDATE [People] SET [House Allegiance] = N'Targaryen'
+WHERE [First Name] = N'Daenerys';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_composite()
+ {
+ base.UpdateDataOperation_required_args_composite();
+
+ AssertSql(
+ @"UPDATE [People] SET [House Allegiance] = N'Targaryen'
+WHERE [First Name] = N'Daenerys' AND [Last Name] = N'Targaryen';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_composite_multi()
+ {
+ base.UpdateDataOperation_required_args_composite_multi();
+
+ AssertSql(
+ @"UPDATE [People] SET [Birthplace] = N'Dragonstone', [House Allegiance] = N'Targaryen', [Culture] = N'Valyrian'
+WHERE [First Name] = N'Daenerys' AND [Last Name] = N'Targaryen';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_multi()
+ {
+ base.UpdateDataOperation_required_args_multi();
+
+ AssertSql(
+ @"UPDATE [People] SET [Birthplace] = N'Dragonstone', [House Allegiance] = N'Targaryen', [Culture] = N'Valyrian'
+WHERE [First Name] = N'Daenerys';
+SELECT @@ROWCOUNT;
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_multiple_rows()
+ {
+ base.UpdateDataOperation_required_args_multiple_rows();
+
+ AssertSql(
+ @"UPDATE [People] SET [House Allegiance] = N'Stark'
+WHERE [First Name] = N'Hodor';
+SELECT @@ROWCOUNT;
+
+UPDATE [People] SET [House Allegiance] = N'Targaryen'
+WHERE [First Name] = N'Daenerys';
+SELECT @@ROWCOUNT;
+
");
}
@@ -577,7 +900,12 @@ public override void DefaultValue_with_line_breaks(bool isUnicode)
}
public SqlServerMigrationSqlGeneratorTest()
- : base(SqlServerTestHelpers.Instance)
+ : base(SqlServerTestHelpers.Instance,
+ new ServiceCollection().AddEntityFrameworkSqlServerNetTopologySuite(),
+ SqlServerTestHelpers.Instance.AddProviderOptions(
+ ((IRelationalDbContextOptionsBuilderInfrastructure)
+ new SqlServerDbContextOptionsBuilder(new DbContextOptionsBuilder()).UseNetTopologySuite())
+ .OptionsBuilder).Options)
{
}
}
diff --git a/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationSqlGeneratorTest.cs b/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationSqlGeneratorTest.cs
index f89e5c2cd78..e8f7b257daf 100644
--- a/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationSqlGeneratorTest.cs
+++ b/test/EFCore.Sqlite.Tests/Migrations/SqliteMigrationSqlGeneratorTest.cs
@@ -2,10 +2,12 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
+using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Migrations.Operations;
using Microsoft.EntityFrameworkCore.Sqlite.Internal;
using Microsoft.EntityFrameworkCore.Sqlite.Metadata.Internal;
using Microsoft.EntityFrameworkCore.TestUtilities;
+using Microsoft.Extensions.DependencyInjection;
using Xunit;
namespace Microsoft.EntityFrameworkCore.Migrations
@@ -25,7 +27,8 @@ public virtual void It_lifts_foreign_key_additions()
{
ClrType = typeof(int),
Name = "FlavorId",
- ColumnType = "INT"
+ ColumnType = "INT",
+ Table = "Pie"
}
}
},
@@ -57,6 +60,7 @@ public virtual void DefaultValue_formats_literal_correctly()
new AddColumnOperation
{
Name = "Event",
+ Table = "History",
ClrType = typeof(string),
ColumnType = "TEXT",
DefaultValue = new DateTime(2015, 4, 12, 17, 5, 0)
@@ -90,6 +94,7 @@ public void CreateTableOperation_with_annotations(bool autoincrement, string pkN
var addIdColumn = new AddColumnOperation
{
Name = "Id",
+ Table = "People",
ClrType = typeof(long),
ColumnType = "INTEGER",
IsNullable = false
@@ -109,6 +114,7 @@ public void CreateTableOperation_with_annotations(bool autoincrement, string pkN
new AddColumnOperation
{
Name = "EmployerId",
+ Table = "People",
ClrType = typeof(int),
ColumnType = "int",
IsNullable = true
@@ -116,6 +122,7 @@ public void CreateTableOperation_with_annotations(bool autoincrement, string pkN
new AddColumnOperation
{
Name = "SSN",
+ Table = "People",
ClrType = typeof(string),
ColumnType = "char(11)",
IsNullable = true
@@ -189,6 +196,60 @@ public void AddColumnOperation_with_computed_column_SQL()
Assert.Equal(SqliteStrings.ComputedColumnsNotSupported, ex.Message);
}
+ public override void AddColumnOperation_with_unicode_no_model()
+ {
+ base.AddColumnOperation_with_unicode_no_model();
+
+ AssertSql(
+ @"ALTER TABLE ""Person"" ADD ""Name"" TEXT NULL;
+");
+ }
+
+ public override void AddColumnOperation_with_fixed_length_no_model()
+ {
+ base.AddColumnOperation_with_fixed_length_no_model();
+
+ AssertSql(
+ @"ALTER TABLE ""Person"" ADD ""Name"" TEXT NULL;
+");
+ }
+
+ public override void AddColumnOperation_with_maxLength_no_model()
+ {
+ base.AddColumnOperation_with_maxLength_no_model();
+
+ AssertSql(
+ @"ALTER TABLE ""Person"" ADD ""Name"" TEXT NULL;
+");
+ }
+
+ public override void AddColumnOperation_with_precision_and_scale_overridden()
+ {
+ base.AddColumnOperation_with_precision_and_scale_overridden();
+
+ AssertSql(
+ @"ALTER TABLE ""Person"" ADD ""Pi"" TEXT NOT NULL;
+");
+ }
+
+ public override void AddColumnOperation_with_precision_and_scale_no_model()
+ {
+ base.AddColumnOperation_with_precision_and_scale_no_model();
+
+ AssertSql(
+ @"ALTER TABLE ""Person"" ADD ""Pi"" TEXT NOT NULL;
+");
+ }
+
+ public override void AddColumnOperation_with_unicode_overridden()
+ {
+ base.AddColumnOperation_with_unicode_overridden();
+
+ AssertSql(
+ @"ALTER TABLE ""Person"" ADD ""Name"" TEXT NULL;
+");
+ }
+
[ConditionalFact]
public void DropSchemaOperation_is_ignored()
{
@@ -247,6 +308,15 @@ public virtual void RenameIndexOperations_throws_when_no_model()
Assert.Equal(SqliteStrings.InvalidMigrationOperation("RenameIndexOperation"), ex.Message);
}
+ public override void RenameTableOperation()
+ {
+ base.RenameTableOperation();
+
+ AssertSql(
+ @"ALTER TABLE ""People"" RENAME TO ""Person"";
+");
+ }
+
public override void RenameTableOperation_legacy()
{
base.RenameTableOperation_legacy();
@@ -281,11 +351,293 @@ public virtual void CreateTableOperation_old_autoincrement_annotation()
@"CREATE TABLE ""People"" (
""Id"" INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT
);
+");
+ }
+
+ public override void SqlOperation()
+ {
+ base.SqlOperation();
+
+ AssertSql(
+ @"-- I <3 DDL
+");
+ }
+
+ public override void InsertDataOperation_all_args_spatial()
+ {
+ base.InsertDataOperation_all_args_spatial();
+
+ AssertSql(
+ @"INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (0, NULL, NULL);
+INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (1, 'Daenerys Targaryen', NULL);
+INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (2, 'John Snow', NULL);
+INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (3, 'Arya Stark', NULL);
+INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (4, 'Harry Strickland', NULL);
+INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (5, 'The Imp', NULL);
+INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (6, 'The Kingslayer', NULL);
+INSERT INTO ""People"" (""Id"", ""Full Name"", ""Geometry"")
+VALUES (7, 'Aemon Targaryen', GeomFromText('GEOMETRYCOLLECTION Z(LINESTRING Z(1.1 2.2 NaN, 2.2 2.2 NaN, 2.2 1.1 NaN, 7.1 7.2 NaN), LINESTRING Z(7.1 7.2 NaN, 20.2 20.2 NaN, 20.2 1.1 NaN, 70.1 70.2 NaN), MULTIPOINT Z((1.1 2.2 NaN), (2.2 2.2 NaN), (2.2 1.1 NaN)), POLYGON Z((1.1 2.2 NaN, 2.2 2.2 NaN, 2.2 1.1 NaN, 1.1 2.2 NaN)), POLYGON Z((10.1 20.2 NaN, 20.2 20.2 NaN, 20.2 10.1 NaN, 10.1 20.2 NaN)), POINT Z(1.1 2.2 3.3), MULTILINESTRING Z((1.1 2.2 NaN, 2.2 2.2 NaN, 2.2 1.1 NaN, 7.1 7.2 NaN), (7.1 7.2 NaN, 20.2 20.2 NaN, 20.2 1.1 NaN, 70.1 70.2 NaN)), MULTIPOLYGON Z(((10.1 20.2 NaN, 20.2 20.2 NaN, 20.2 10.1 NaN, 10.1 20.2 NaN)), ((1.1 2.2 NaN, 2.2 2.2 NaN, 2.2 1.1 NaN, 1.1 2.2 NaN))))', 4326));
+");
+ }
+
+ protected override string GetGeometryCollectionStoreType()
+ => "GEOMETRYCOLLECTION";
+
+ public override void InsertDataOperation_required_args()
+ {
+ base.InsertDataOperation_required_args();
+
+ AssertSql(
+ @"INSERT INTO ""People"" (""First Name"")
+VALUES ('John');
+");
+ }
+
+ public override void InsertDataOperation_required_args_composite()
+ {
+ base.InsertDataOperation_required_args_composite();
+
+ AssertSql(
+ @"INSERT INTO ""People"" (""First Name"", ""Last Name"")
+VALUES ('John', 'Snow');
+");
+ }
+
+ public override void InsertDataOperation_required_args_multiple_rows()
+ {
+ base.InsertDataOperation_required_args_multiple_rows();
+
+ AssertSql(
+ @"INSERT INTO ""People"" (""First Name"")
+VALUES ('John');
+INSERT INTO ""People"" (""First Name"")
+VALUES ('Daenerys');
+");
+ }
+
+ public override void InsertDataOperation_throws_for_unsupported_column_types()
+ {
+ // All column types are supported by Sqlite
+ }
+
+ public override void DeleteDataOperation_all_args()
+ {
+ base.DeleteDataOperation_all_args();
+
+ AssertSql(
+ @"DELETE FROM ""People""
+WHERE ""First Name"" = 'Hodor';
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'Daenerys';
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'John';
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'Arya';
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'Harry';
+SELECT changes();
+
+");
+ }
+
+ public override void DeleteDataOperation_all_args_composite()
+ {
+ base.DeleteDataOperation_all_args_composite();
+
+ AssertSql(
+ @"DELETE FROM ""People""
+WHERE ""First Name"" = 'Hodor' AND ""Last Name"" IS NULL;
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'Daenerys' AND ""Last Name"" = 'Targaryen';
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'John' AND ""Last Name"" = 'Snow';
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'Arya' AND ""Last Name"" = 'Stark';
+SELECT changes();
+
+DELETE FROM ""People""
+WHERE ""First Name"" = 'Harry' AND ""Last Name"" = 'Strickland';
+SELECT changes();
+
+");
+ }
+
+ public override void DeleteDataOperation_required_args()
+ {
+ base.DeleteDataOperation_required_args();
+
+ AssertSql(
+ @"DELETE FROM ""People""
+WHERE ""Last Name"" = 'Snow';
+SELECT changes();
+
+");
+ }
+
+ public override void DeleteDataOperation_required_args_composite()
+ {
+ base.DeleteDataOperation_required_args_composite();
+
+ AssertSql(
+ @"DELETE FROM ""People""
+WHERE ""First Name"" = 'John' AND ""Last Name"" = 'Snow';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args()
+ {
+ base.UpdateDataOperation_all_args();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""Birthplace"" = 'Winterfell', ""House Allegiance"" = 'Stark', ""Culture"" = 'Northmen'
+WHERE ""First Name"" = 'Hodor';
+SELECT changes();
+
+UPDATE ""People"" SET ""Birthplace"" = 'Dragonstone', ""House Allegiance"" = 'Targaryen', ""Culture"" = 'Valyrian'
+WHERE ""First Name"" = 'Daenerys';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args_composite()
+ {
+ base.UpdateDataOperation_all_args_composite();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""House Allegiance"" = 'Stark'
+WHERE ""First Name"" = 'Hodor' AND ""Last Name"" IS NULL;
+SELECT changes();
+
+UPDATE ""People"" SET ""House Allegiance"" = 'Targaryen'
+WHERE ""First Name"" = 'Daenerys' AND ""Last Name"" = 'Targaryen';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args_composite_multi()
+ {
+ base.UpdateDataOperation_all_args_composite_multi();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""Birthplace"" = 'Winterfell', ""House Allegiance"" = 'Stark', ""Culture"" = 'Northmen'
+WHERE ""First Name"" = 'Hodor' AND ""Last Name"" IS NULL;
+SELECT changes();
+
+UPDATE ""People"" SET ""Birthplace"" = 'Dragonstone', ""House Allegiance"" = 'Targaryen', ""Culture"" = 'Valyrian'
+WHERE ""First Name"" = 'Daenerys' AND ""Last Name"" = 'Targaryen';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_all_args_multi()
+ {
+ base.UpdateDataOperation_all_args_multi();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""Birthplace"" = 'Dragonstone', ""House Allegiance"" = 'Targaryen', ""Culture"" = 'Valyrian'
+WHERE ""First Name"" = 'Daenerys';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args()
+ {
+ base.UpdateDataOperation_required_args();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""House Allegiance"" = 'Targaryen'
+WHERE ""First Name"" = 'Daenerys';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_composite()
+ {
+ base.UpdateDataOperation_required_args_composite();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""House Allegiance"" = 'Targaryen'
+WHERE ""First Name"" = 'Daenerys' AND ""Last Name"" = 'Targaryen';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_composite_multi()
+ {
+ base.UpdateDataOperation_required_args_composite_multi();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""Birthplace"" = 'Dragonstone', ""House Allegiance"" = 'Targaryen', ""Culture"" = 'Valyrian'
+WHERE ""First Name"" = 'Daenerys' AND ""Last Name"" = 'Targaryen';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_multi()
+ {
+ base.UpdateDataOperation_required_args_multi();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""Birthplace"" = 'Dragonstone', ""House Allegiance"" = 'Targaryen', ""Culture"" = 'Valyrian'
+WHERE ""First Name"" = 'Daenerys';
+SELECT changes();
+
+");
+ }
+
+ public override void UpdateDataOperation_required_args_multiple_rows()
+ {
+ base.UpdateDataOperation_required_args_multiple_rows();
+
+ AssertSql(
+ @"UPDATE ""People"" SET ""House Allegiance"" = 'Stark'
+WHERE ""First Name"" = 'Hodor';
+SELECT changes();
+
+UPDATE ""People"" SET ""House Allegiance"" = 'Targaryen'
+WHERE ""First Name"" = 'Daenerys';
+SELECT changes();
+
");
}
public SqliteMigrationSqlGeneratorTest()
- : base(SqliteTestHelpers.Instance)
+ : base(SqliteTestHelpers.Instance,
+ new ServiceCollection().AddEntityFrameworkSqliteNetTopologySuite(),
+ SqliteTestHelpers.Instance.AddProviderOptions(
+ ((IRelationalDbContextOptionsBuilderInfrastructure)
+ new SqliteDbContextOptionsBuilder(new DbContextOptionsBuilder()).UseNetTopologySuite())
+ .OptionsBuilder).Options)
{
}
}
diff --git a/test/EFCore.Tests/ApiConsistencyTestBase.cs b/test/EFCore.Tests/ApiConsistencyTestBase.cs
index 19900486b14..f523942ba66 100644
--- a/test/EFCore.Tests/ApiConsistencyTestBase.cs
+++ b/test/EFCore.Tests/ApiConsistencyTestBase.cs
@@ -4,20 +4,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
-using System.Linq.Expressions;
using System.Reflection;
-using System.Text;
using System.Threading;
using System.Threading.Tasks;
using JetBrains.Annotations;
-using Microsoft.EntityFrameworkCore.ChangeTracking;
using Microsoft.EntityFrameworkCore.Diagnostics;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Metadata;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
-using Microsoft.EntityFrameworkCore.Storage;
-using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
-using Microsoft.EntityFrameworkCore.ValueGeneration;
using Microsoft.Extensions.DependencyInjection;
using Xunit;