diff --git a/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs b/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs
index 6d625c7f9d..7262043d14 100644
--- a/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs
+++ b/src/Core/Services/MetadataProviders/Converters/DatabaseObjectConverter.cs
@@ -77,7 +77,8 @@ public override void Write(Utf8JsonWriter writer, DatabaseObject value, JsonSeri
// Only escape columns for properties whose type(derived type) is SourceDefinition.
if (IsSourceDefinitionOrDerivedClassProperty(prop) && propVal is SourceDefinition sourceDef)
{
- EscapeDollaredColumns(sourceDef);
+ // Check if we need to escape any column names
+ propVal = GetSourceDefinitionWithEscapedColumns(sourceDef);
}
JsonSerializer.Serialize(writer, propVal, options);
@@ -93,25 +94,126 @@ private static bool IsSourceDefinitionOrDerivedClassProperty(PropertyInfo prop)
}
///
- /// Escapes column keys that start with '$' to '_$' for serialization.
+ /// Returns the SourceDefinition as-is if no columns start with '$',
+ /// otherwise returns a copy with escaped column names.
///
- private static void EscapeDollaredColumns(SourceDefinition sourceDef)
+ private static SourceDefinition GetSourceDefinitionWithEscapedColumns(SourceDefinition sourceDef)
{
- if (sourceDef.Columns is null || sourceDef.Columns.Count == 0)
+ // If no columns or no columns starting with '$', return original
+ if (sourceDef.Columns is null || sourceDef.Columns.Count == 0 ||
+ !sourceDef.Columns.Keys.Any(k => k.StartsWith(DOLLAR_CHAR, StringComparison.Ordinal)))
{
- return;
+ return sourceDef;
}
- List keysToEscape = sourceDef.Columns.Keys
- .Where(k => k.StartsWith(DOLLAR_CHAR, StringComparison.Ordinal))
- .ToList();
+ // Create escaped columns dictionary
+ Dictionary escapedColumns = CreateEscapedColumnsDictionary(sourceDef.Columns);
+
+ // Create new instance based on the actual type
+ return sourceDef switch
+ {
+ StoredProcedureDefinition spDef => CreateStoredProcedureDefinition(spDef, escapedColumns),
+ _ => CreateSourceDefinition(sourceDef, escapedColumns)
+ };
+ }
- foreach (string key in keysToEscape)
+ ///
+ /// Creates a dictionary with escaped column keys for serialization.
+ ///
+ private static Dictionary CreateEscapedColumnsDictionary(IDictionary originalColumns)
+ {
+ Dictionary escapedColumns = new();
+ foreach (KeyValuePair kvp in originalColumns)
{
- ColumnDefinition col = sourceDef.Columns[key];
- sourceDef.Columns.Remove(key);
- string newKey = ESCAPED_DOLLARCHAR + key[1..];
- sourceDef.Columns[newKey] = col;
+ if (kvp.Key.StartsWith(DOLLAR_CHAR, StringComparison.Ordinal))
+ {
+ string escapedKey = ESCAPED_DOLLARCHAR + kvp.Key[1..];
+ escapedColumns[escapedKey] = kvp.Value;
+ }
+ else
+ {
+ escapedColumns[kvp.Key] = kvp.Value;
+ }
+ }
+
+ return escapedColumns;
+ }
+
+ ///
+ /// Creates a new StoredProcedureDefinition with escaped columns and copied properties.
+ ///
+ private static StoredProcedureDefinition CreateStoredProcedureDefinition(StoredProcedureDefinition original, Dictionary escapedColumns)
+ {
+ StoredProcedureDefinition newSpDef = new()
+ {
+ IsInsertDMLTriggerEnabled = original.IsInsertDMLTriggerEnabled,
+ IsUpdateDMLTriggerEnabled = original.IsUpdateDMLTriggerEnabled,
+ PrimaryKey = original.PrimaryKey
+ };
+
+ // Add escaped columns
+ AddColumnsToDictionary(newSpDef.Columns, escapedColumns);
+
+ // Copy parameters
+ AddParametersToDictionary(newSpDef.Parameters, original.Parameters);
+
+ // Copy relationship map if it exists
+ CopyRelationshipMap(newSpDef.SourceEntityRelationshipMap, original.SourceEntityRelationshipMap);
+
+ return newSpDef;
+ }
+
+ ///
+ /// Creates a new SourceDefinition with escaped columns and copied properties.
+ ///
+ private static SourceDefinition CreateSourceDefinition(SourceDefinition original, Dictionary escapedColumns)
+ {
+ SourceDefinition newSourceDef = new()
+ {
+ IsInsertDMLTriggerEnabled = original.IsInsertDMLTriggerEnabled,
+ IsUpdateDMLTriggerEnabled = original.IsUpdateDMLTriggerEnabled,
+ PrimaryKey = original.PrimaryKey
+ };
+
+ // Add escaped columns
+ AddColumnsToDictionary(newSourceDef.Columns, escapedColumns);
+
+ // Copy relationship map if it exists
+ CopyRelationshipMap(newSourceDef.SourceEntityRelationshipMap, original.SourceEntityRelationshipMap);
+
+ return newSourceDef;
+ }
+
+ ///
+ /// Helper method to add columns to a dictionary.
+ ///
+ private static void AddColumnsToDictionary(IDictionary target, Dictionary source)
+ {
+ foreach (KeyValuePair kvp in source)
+ {
+ target.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ ///
+ /// Helper method to add parameters to a dictionary.
+ ///
+ private static void AddParametersToDictionary(IDictionary target, IDictionary source)
+ {
+ foreach (KeyValuePair kvp in source)
+ {
+ target.Add(kvp.Key, kvp.Value);
+ }
+ }
+
+ ///
+ /// Helper method to copy relationship map between SourceDefinition instances.
+ ///
+ private static void CopyRelationshipMap(IDictionary target, IDictionary source)
+ {
+ foreach (KeyValuePair kvp in source)
+ {
+ target.Add(kvp.Key, kvp.Value);
}
}
diff --git a/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs b/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs
index 12ae3ee993..440a5e1c13 100644
--- a/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs
+++ b/src/Service.Tests/UnitTests/SerializationDeserializationTests.cs
@@ -308,7 +308,7 @@ public void TestDictionaryDatabaseObjectSerializationDeserialization_WithDollarC
Assert.AreEqual(deserializedDatabaseTable.SourceType, _databaseTable.SourceType);
Assert.AreEqual(deserializedDatabaseTable.FullName, _databaseTable.FullName);
- deserializedDatabaseTable.Equals(_databaseTable);
+ Assert.IsTrue(deserializedDatabaseTable.Equals(_databaseTable));
VerifySourceDefinitionSerializationDeserialization(deserializedDatabaseTable.SourceDefinition, _databaseTable.SourceDefinition, "$FirstName");
VerifySourceDefinitionSerializationDeserialization(deserializedDatabaseTable.TableDefinition, _databaseTable.TableDefinition, "$FirstName");
}
@@ -343,7 +343,7 @@ public void TestDatabaseViewSerializationDeserialization_WithDollarColumn()
DatabaseView deserializedDatabaseView = (DatabaseView)deserializedDict["person"];
Assert.AreEqual(deserializedDatabaseView.SourceType, _databaseView.SourceType);
- deserializedDatabaseView.Equals(_databaseView);
+ Assert.IsTrue(deserializedDatabaseView.Equals(_databaseView));
VerifySourceDefinitionSerializationDeserialization(deserializedDatabaseView.SourceDefinition, _databaseView.SourceDefinition, "$FirstName");
VerifySourceDefinitionSerializationDeserialization(deserializedDatabaseView.ViewDefinition, _databaseView.ViewDefinition, "$FirstName");
}
@@ -377,7 +377,7 @@ public void TestDatabaseStoredProcedureSerializationDeserialization_WithDollarCo
DatabaseStoredProcedure deserializedDatabaseSP = (DatabaseStoredProcedure)deserializedDict["person"];
Assert.AreEqual(deserializedDatabaseSP.SourceType, _databaseStoredProcedure.SourceType);
- deserializedDatabaseSP.Equals(_databaseStoredProcedure);
+ Assert.IsTrue(deserializedDatabaseSP.Equals(_databaseStoredProcedure));
VerifySourceDefinitionSerializationDeserialization(deserializedDatabaseSP.SourceDefinition, _databaseStoredProcedure.SourceDefinition, "$FirstName", true);
VerifySourceDefinitionSerializationDeserialization(deserializedDatabaseSP.StoredProcedureDefinition, _databaseStoredProcedure.StoredProcedureDefinition, "$FirstName", true);
}
@@ -529,7 +529,10 @@ private static void VerifyColumnDefinitionSerializationDeserialization(ColumnDef
Assert.AreEqual(fields, 8);
// test values
- expectedColumnDefinition.Equals(deserializedColumnDefinition);
+ Assert.AreEqual(expectedColumnDefinition.DbType, deserializedColumnDefinition.DbType);
+ Assert.AreEqual(expectedColumnDefinition.SqlDbType, deserializedColumnDefinition.SqlDbType);
+ Assert.AreEqual(expectedColumnDefinition.IsAutoGenerated, deserializedColumnDefinition.IsAutoGenerated);
+ Assert.AreEqual(expectedColumnDefinition.DefaultValue.ToString(), deserializedColumnDefinition.DefaultValue.ToString());
}
private static void VerifyParameterDefinitionSerializationDeserialization(ParameterDefinition expectedParameterDefinition, ParameterDefinition deserializedParameterDefinition)
@@ -538,7 +541,10 @@ private static void VerifyParameterDefinitionSerializationDeserialization(Parame
int fields = typeof(ParameterDefinition).GetFields(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance).Length;
Assert.AreEqual(fields, 9);
// test values
- expectedParameterDefinition.Equals(deserializedParameterDefinition);
+ Assert.AreEqual(expectedParameterDefinition.DbType, deserializedParameterDefinition.DbType);
+ Assert.AreEqual(expectedParameterDefinition.SqlDbType, deserializedParameterDefinition.SqlDbType);
+ Assert.AreEqual(expectedParameterDefinition.SystemType, deserializedParameterDefinition.SystemType);
+ Assert.AreEqual(expectedParameterDefinition.ConfigDefaultValue.ToString(), deserializedParameterDefinition.ConfigDefaultValue.ToString());
}
private static void VerifyRelationShipPair(RelationShipPair expectedRelationShipPair, RelationShipPair deserializedRelationShipPair)