diff --git a/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs b/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs
index a81a6df2ebd..b4d8f1d594a 100644
--- a/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs
+++ b/src/EFCore.SqlServer/Diagnostics/Internal/SqlServerLoggingDefinitions.cs
@@ -155,6 +155,14 @@ public class SqlServerLoggingDefinitions : RelationalLoggingDefinitions
///
public EventDefinitionBase? LogReflexiveConstraintIgnored;
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public EventDefinitionBase? LogDataverseForeignKeyInvalid;
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs b/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs
index 5940679dcf3..599c93a60a4 100644
--- a/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs
+++ b/src/EFCore.SqlServer/Diagnostics/SqlServerEventId.cs
@@ -69,6 +69,7 @@ private enum Id
ColumnWithoutTypeWarning,
ForeignKeyReferencesUnknownPrincipalTableWarning,
MissingViewDefinitionRightsWarning,
+ DataverseForeignKeyInvalidWarning,
}
private static readonly string ValidationPrefix = DbLoggerCategory.Model.Validation.Name + ".";
@@ -286,6 +287,14 @@ private static EventId MakeScaffoldingId(Id id)
///
public static readonly EventId ReflexiveConstraintIgnored = MakeScaffoldingId(Id.ReflexiveConstraintIgnored);
+ ///
+ /// An invalid foreign key constraint was skipped.
+ ///
+ ///
+ /// This event is in the category.
+ ///
+ public static readonly EventId DataverseForeignKeyInvalidWarning = MakeScaffoldingId(Id.DataverseForeignKeyInvalidWarning);
+
///
/// A duplicate foreign key constraint was skipped.
///
diff --git a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs
index bfa21d25d66..54e7f79c7aa 100644
--- a/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs
+++ b/src/EFCore.SqlServer/Extensions/Internal/SqlServerLoggerExtensions.cs
@@ -577,6 +577,27 @@ public static void ReflexiveConstraintIgnored(
// No DiagnosticsSource events because these are purely design-time messages
}
+ ///
+ /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
+ /// the same compatibility standards as public APIs. It may be changed or removed without notice in
+ /// any release. You should only use it directly in your code with extreme caution and knowing that
+ /// doing so can result in application failures when updating to a new Entity Framework Core release.
+ ///
+ public static void DataverseForeignKeyInvalidWarning(
+ this IDiagnosticsLogger diagnostics,
+ string foreignKeyName,
+ string tableName)
+ {
+ var definition = SqlServerResources.LogDataverseForeignKeyInvalidWarning(diagnostics);
+
+ if (diagnostics.ShouldLog(definition))
+ {
+ definition.Log(diagnostics, foreignKeyName, tableName);
+ }
+
+ // No DiagnosticsSource events because these are purely design-time messages
+ }
+
///
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
diff --git a/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs b/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs
index 00b27a4de1a..03681e04002 100644
--- a/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs
+++ b/src/EFCore.SqlServer/Properties/SqlServerStrings.Designer.cs
@@ -1018,6 +1018,31 @@ public static EventDefinition LogReflexiveConstraintIgnored(IDia
return (EventDefinition)definition;
}
+ ///
+ /// Skipping foreign key '{foreignKeyName}' on table '{tableName}' since it is not supported by the Dataverse TDS Endpoint
+ ///
+ public static EventDefinition LogDataverseForeignKeyInvalidWarning(IDiagnosticsLogger logger)
+ {
+ var definition = ((Diagnostics.Internal.SqlServerLoggingDefinitions)logger.Definitions).LogDataverseForeignKeyInvalid;
+ if (definition == null)
+ {
+ definition = NonCapturingLazyInitializer.EnsureInitialized(
+ ref ((Diagnostics.Internal.SqlServerLoggingDefinitions)logger.Definitions).LogDataverseForeignKeyInvalid,
+ logger,
+ static logger => new EventDefinition(
+ logger.Options,
+ SqlServerEventId.DataverseForeignKeyInvalidWarning,
+ LogLevel.Debug,
+ "SqlServerEventId.LogDataverseForeignKeyInvalidWarning",
+ level => LoggerMessage.Define(
+ level,
+ SqlServerEventId.DataverseForeignKeyInvalidWarning,
+ _resourceManager.GetString("LogDataverseForeignKeyInvalidWarning")!)));
+ }
+
+ return (EventDefinition)definition;
+ }
+
///
/// Savepoints are disabled because Multiple Active Result Sets (MARS) is enabled. If 'SaveChanges' fails, then the transaction cannot be automatically rolled back to a known clean state. Instead, the transaction should be rolled back by the application before retrying 'SaveChanges'. See https://go.microsoft.com/fwlink/?linkid=2149338 for more information and examples. To identify the code which triggers this warning, call 'ConfigureWarnings(w => w.Throw(SqlServerEventId.SavepointsDisabledBecauseOfMARS))'.
///
diff --git a/src/EFCore.SqlServer/Properties/SqlServerStrings.resx b/src/EFCore.SqlServer/Properties/SqlServerStrings.resx
index 2f8726af548..4e1bc2ebcb9 100644
--- a/src/EFCore.SqlServer/Properties/SqlServerStrings.resx
+++ b/src/EFCore.SqlServer/Properties/SqlServerStrings.resx
@@ -296,6 +296,10 @@
Skipping foreign key '{foreignKeyName}' on table '{tableName}' since all of its columns reference themselves.
Debug SqlServerEventId.ReflexiveConstraintIgnored string string
+
+ Skipping foreign key '{foreignKeyName}' on table '{tableName}' since it is not supported by the Dataverse TDS Endpoint
+ Debug SqlServerEventId.DataverseForeignKeyInvalidWarning string string
+
Savepoints are disabled because Multiple Active Result Sets (MARS) is enabled. If 'SaveChanges' fails, then the transaction cannot be automatically rolled back to a known clean state. Instead, the transaction should be rolled back by the application before retrying 'SaveChanges'. See https://go.microsoft.com/fwlink/?linkid=2149338 for more information and examples. To identify the code which triggers this warning, call 'ConfigureWarnings(w => w.Throw(SqlServerEventId.SavepointsDisabledBecauseOfMARS))'.
Warning SqlServerEventId.SavepointsDisabledBecauseOfMARS
diff --git a/src/EFCore.SqlServer/Scaffolding/Internal/SqlServerDatabaseModelFactory.cs b/src/EFCore.SqlServer/Scaffolding/Internal/SqlServerDatabaseModelFactory.cs
index 2cd011ba677..c60c517c7b2 100644
--- a/src/EFCore.SqlServer/Scaffolding/Internal/SqlServerDatabaseModelFactory.cs
+++ b/src/EFCore.SqlServer/Scaffolding/Internal/SqlServerDatabaseModelFactory.cs
@@ -682,10 +682,7 @@ FROM [sys].[views] AS [v]
// This is done separately due to MARS property may be turned off
GetColumns(connection, tables, tableFilterSql, viewFilter, typeAliases, databaseCollation);
- if (SupportsIndexes())
- {
- GetIndexes(connection, tables, tableFilterSql);
- }
+ GetIndexes(connection, tables, tableFilterSql);
GetForeignKeys(connection, tables, tableFilterSql);
@@ -1028,7 +1025,7 @@ private void GetIndexes(DbConnection connection, IReadOnlyList ta
[i].[has_filter],
[i].[filter_definition],
[i].[fill_factor],
- COL_NAME([ic].[object_id], [ic].[column_id]) AS [column_name],
+ [c].[name] AS [column_name],
[ic].[is_descending_key],
[ic].[is_included_column]
FROM [sys].[indexes] AS [i]
@@ -1089,7 +1086,7 @@ FROM [sys].[indexes] i
if (primaryKeyGroups.Length == 1)
{
- if (TryGetPrimaryKey(primaryKeyGroups[0], out var primaryKey))
+ if (TryGetPrimaryKey(primaryKeyGroups[0], out var primaryKey) && IsValidPrimaryKey(primaryKey))
{
_logger.PrimaryKeyFound(primaryKey.Name!, DisplayName(tableSchema, tableName));
table.PrimaryKey = primaryKey;
@@ -1137,6 +1134,16 @@ FROM [sys].[indexes] i
}
}
+ bool IsValidPrimaryKey(DatabasePrimaryKey primaryKey)
+ {
+ if (_engineEdition != EngineEdition.DynamicsTdsEndpoint)
+ {
+ return true;
+ }
+
+ return primaryKey.Columns.Count == 1 && primaryKey.Columns[0].StoreType == "uniqueidentifier";
+ }
+
bool TryGetPrimaryKey(
IGrouping<(string Name, string? TypeDesc, byte FillFactor), DbDataRecord> primaryKeyGroup,
[NotNullWhen(true)] out DatabasePrimaryKey? primaryKey)
@@ -1377,6 +1384,14 @@ FROM [sys].[foreign_keys] AS [f]
foreignKey.PrincipalColumns.Add(principalColumn);
}
+ if (!invalid && _engineEdition == EngineEdition.DynamicsTdsEndpoint && !IsValidDataverseForeignKey(foreignKey))
+ {
+ invalid = true;
+ _logger.DataverseForeignKeyInvalidWarning(
+ fkName!,
+ DisplayName(table.Schema, table.Name));
+ }
+
if (!invalid)
{
if (foreignKey.Columns.SequenceEqual(foreignKey.PrincipalColumns))
@@ -1406,6 +1421,36 @@ FROM [sys].[foreign_keys] AS [f]
}
}
}
+
+ bool IsValidDataverseForeignKey(DatabaseForeignKey foreignKey)
+ {
+ if (foreignKey.Columns.Count != 1)
+ {
+ return false;
+ }
+
+ if (foreignKey.Columns[0].StoreType != "uniqueidentifier")
+ {
+ return false;
+ }
+
+ if (foreignKey.PrincipalTable.PrimaryKey == null)
+ {
+ return false;
+ }
+
+ if (foreignKey.PrincipalTable.PrimaryKey.Columns.Count != 1)
+ {
+ return false;
+ }
+
+ if (foreignKey.PrincipalTable.PrimaryKey.Columns[0].Name != foreignKey.PrincipalColumns[0].Name)
+ {
+ return false;
+ }
+
+ return true;
+ }
}
private void GetTriggers(DbConnection connection, IReadOnlyList tables, string tableFilter)
@@ -1456,9 +1501,6 @@ private bool SupportsMemoryOptimizedTable()
private bool SupportsSequences()
=> _compatibilityLevel >= 110 && IsFullFeaturedEngineEdition();
- private bool SupportsIndexes()
- => _engineEdition != EngineEdition.DynamicsTdsEndpoint;
-
private bool SupportsViews()
=> _engineEdition != EngineEdition.DynamicsTdsEndpoint;