Skip to content

Commit c5d8f3c

Browse files
authored
[release/8.0] Add complex type mappings to the default relational mappings (#32901)
* Add complex type mappings to the default relational mappings (#32867) * Add complex type mappings to the default relational mappings Replaces #32816 Fixes #32699 As discussed, leaving FromSql to use the default mappings, but add complex types to the default mappings. * Add note to SelectExpression * Fix merge and add quirks
1 parent cb3b1b6 commit c5d8f3c

File tree

4 files changed

+398
-42
lines changed

4 files changed

+398
-42
lines changed

src/EFCore.Relational/Metadata/Internal/RelationalModel.cs

Lines changed: 108 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Text;
45
using System.Text.Json;
56

67
namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
@@ -13,6 +14,9 @@ namespace Microsoft.EntityFrameworkCore.Metadata.Internal;
1314
/// </summary>
1415
public class RelationalModel : Annotatable, IRelationalModel
1516
{
17+
internal static readonly bool UseOldBehavior32699 =
18+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue32699", out var enabled32699) && enabled32699;
19+
1620
private bool _isReadOnly;
1721

1822
/// <summary>
@@ -340,31 +344,38 @@ private static void AddDefaultMappings(
340344
}
341345
else
342346
{
343-
foreach (var property in entityType.GetProperties())
347+
if (UseOldBehavior32699)
344348
{
345-
var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringType == mappedType
346-
? property.GetColumnName()
347-
: null;
348-
if (columnName == null)
349+
foreach (var property in entityType.GetProperties())
349350
{
350-
continue;
351-
}
351+
var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringType == mappedType
352+
? property.GetColumnName()
353+
: null;
354+
if (columnName == null)
355+
{
356+
continue;
357+
}
352358

353-
var column = (ColumnBase<ColumnMappingBase>?)defaultTable.FindColumn(columnName);
354-
if (column == null)
355-
{
356-
column = new ColumnBase<ColumnMappingBase>(columnName, property.GetColumnType(), defaultTable)
359+
var column = (ColumnBase<ColumnMappingBase>?)defaultTable.FindColumn(columnName);
360+
if (column == null)
357361
{
358-
IsNullable = property.IsColumnNullable()
359-
};
360-
defaultTable.Columns.Add(columnName, column);
361-
}
362-
else if (!property.IsColumnNullable())
363-
{
364-
column.IsNullable = false;
365-
}
362+
column = new ColumnBase<ColumnMappingBase>(columnName, property.GetColumnType(), defaultTable)
363+
{
364+
IsNullable = property.IsColumnNullable()
365+
};
366+
defaultTable.Columns.Add(columnName, column);
367+
}
368+
else if (!property.IsColumnNullable())
369+
{
370+
column.IsNullable = false;
371+
}
366372

367-
CreateColumnMapping(column, property, tableMapping);
373+
CreateColumnMapping(column, property, tableMapping);
374+
}
375+
}
376+
else
377+
{
378+
CreateDefaultColumnMapping(entityType, mappedType, defaultTable, tableMapping, isTph, isTpc);
368379
}
369380
}
370381

@@ -386,6 +397,83 @@ private static void AddDefaultMappings(
386397
tableMappings.Reverse();
387398
}
388399

400+
private static void CreateDefaultColumnMapping(
401+
ITypeBase typeBase,
402+
ITypeBase mappedType,
403+
TableBase defaultTable,
404+
TableMappingBase<ColumnMappingBase> tableMapping,
405+
bool isTph,
406+
bool isTpc)
407+
{
408+
foreach (var property in typeBase.GetProperties())
409+
{
410+
var columnName = property.IsPrimaryKey() || isTpc || isTph || property.DeclaringType == mappedType
411+
? GetColumnName(property)
412+
: null;
413+
414+
if (columnName == null)
415+
{
416+
continue;
417+
}
418+
419+
var column = (ColumnBase<ColumnMappingBase>?)defaultTable.FindColumn(columnName);
420+
if (column == null)
421+
{
422+
column = new ColumnBase<ColumnMappingBase>(columnName, property.GetColumnType(), defaultTable)
423+
{
424+
IsNullable = property.IsColumnNullable()
425+
};
426+
defaultTable.Columns.Add(columnName, column);
427+
}
428+
else if (!property.IsColumnNullable())
429+
{
430+
column.IsNullable = false;
431+
}
432+
433+
CreateColumnMapping(column, property, tableMapping);
434+
}
435+
436+
foreach (var complexProperty in typeBase.GetDeclaredComplexProperties())
437+
{
438+
var complexType = complexProperty.ComplexType;
439+
tableMapping = new TableMappingBase<ColumnMappingBase>(complexType, defaultTable, includesDerivedTypes: false);
440+
441+
CreateDefaultColumnMapping(complexType, complexType, defaultTable, tableMapping, isTph, isTpc);
442+
443+
var tableMappings = (List<TableMappingBase<ColumnMappingBase>>?)complexType
444+
.FindRuntimeAnnotationValue(RelationalAnnotationNames.DefaultMappings);
445+
if (tableMappings == null)
446+
{
447+
tableMappings = new List<TableMappingBase<ColumnMappingBase>>();
448+
complexType.AddRuntimeAnnotation(RelationalAnnotationNames.DefaultMappings, tableMappings);
449+
}
450+
tableMappings.Add(tableMapping);
451+
452+
defaultTable.ComplexTypeMappings.Add(tableMapping);
453+
}
454+
455+
static string GetColumnName(IProperty property)
456+
{
457+
var complexType = property.DeclaringType as IComplexType;
458+
if (complexType != null)
459+
{
460+
var builder = new StringBuilder();
461+
builder.Append(property.Name);
462+
while (complexType != null)
463+
{
464+
builder.Insert(0, "_");
465+
builder.Insert(0, complexType.ComplexProperty.Name);
466+
467+
complexType = complexType.ComplexProperty.DeclaringType as IComplexType;
468+
}
469+
470+
return builder.ToString();
471+
}
472+
473+
return property.GetColumnName();
474+
}
475+
}
476+
389477
private static void AddTables(
390478
RelationalModel databaseModel,
391479
IEntityType entityType,

src/EFCore.Relational/Query/SqlExpressions/SelectExpression.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3201,9 +3201,18 @@ public static StructuralTypeShaperExpression GenerateComplexPropertyShaperExpres
32013201
var propertyExpressionMap = new Dictionary<IProperty, ColumnExpression>();
32023202

32033203
// We do not support complex type splitting, so we will only ever have a single table/view mapping to it.
3204+
// See Issue #32853 and Issue #31248
32043205
var complexTypeTable = complexProperty.ComplexType.GetViewOrTableMappings().Single().Table;
3205-
var tableReferenceExpression = containerProjection.TableMap[complexTypeTable];
3206-
3206+
TableReferenceExpression? tableReferenceExpression;
3207+
if (RelationalModel.UseOldBehavior32699)
3208+
{
3209+
tableReferenceExpression = containerProjection.TableMap[complexTypeTable];
3210+
}
3211+
else if (!containerProjection.TableMap.TryGetValue(complexTypeTable, out tableReferenceExpression))
3212+
{
3213+
complexTypeTable = complexProperty.ComplexType.GetDefaultMappings().Single().Table;
3214+
tableReferenceExpression = containerProjection.TableMap[complexTypeTable];
3215+
}
32073216
var isComplexTypeNullable = containerProjection.IsNullable || complexProperty.IsNullable;
32083217

32093218
// If the complex property is declared on a type that's derived relative to the type being projected, the projected column is

0 commit comments

Comments
 (0)