@@ -51,6 +51,7 @@ public virtual void ProcessModelFinalizing(
51
51
var foreignKeys = new Dictionary < string , ( IConventionForeignKey , StoreObjectIdentifier ) > ( ) ;
52
52
var indexes = new Dictionary < string , ( IConventionIndex , StoreObjectIdentifier ) > ( ) ;
53
53
var checkConstraints = new Dictionary < ( string , string ? ) , ( IConventionCheckConstraint , StoreObjectIdentifier ) > ( ) ;
54
+ var defaultConstraints = new Dictionary < ( string , string ? ) , ( IConventionProperty , StoreObjectIdentifier ) > ( ) ;
54
55
var triggers = new Dictionary < string , ( IConventionTrigger , StoreObjectIdentifier ) > ( ) ;
55
56
foreach ( var ( ( tableName , schema ) , conventionEntityTypes ) in tables )
56
57
{
@@ -76,6 +77,11 @@ public virtual void ProcessModelFinalizing(
76
77
checkConstraints . Clear ( ) ;
77
78
}
78
79
80
+ if ( ! DefaultConstraintsUniqueAcrossTables )
81
+ {
82
+ defaultConstraints . Clear ( ) ;
83
+ }
84
+
79
85
if ( ! TriggersUniqueAcrossTables )
80
86
{
81
87
triggers . Clear ( ) ;
@@ -89,6 +95,7 @@ public virtual void ProcessModelFinalizing(
89
95
UniquifyForeignKeyNames ( entityType , foreignKeys , storeObject , maxLength ) ;
90
96
UniquifyIndexNames ( entityType , indexes , storeObject , maxLength ) ;
91
97
UniquifyCheckConstraintNames ( entityType , checkConstraints , storeObject , maxLength ) ;
98
+ UniquifyDefaultConstraintNames ( entityType , defaultConstraints , storeObject , maxLength ) ;
92
99
UniquifyTriggerNames ( entityType , triggers , storeObject , maxLength ) ;
93
100
}
94
101
}
@@ -124,14 +131,19 @@ protected virtual bool CheckConstraintsUniqueAcrossTables
124
131
protected virtual bool TriggersUniqueAcrossTables
125
132
=> true ;
126
133
134
+ /// <summary>
135
+ /// Gets a value indicating whether default constraint names should be unique across tables.
136
+ /// </summary>
137
+ protected virtual bool DefaultConstraintsUniqueAcrossTables
138
+ => false ;
139
+
127
140
private static void TryUniquifyTableNames (
128
141
IConventionModel model ,
129
142
Dictionary < ( string Name , string ? Schema ) , List < IConventionEntityType > > tables ,
130
143
int maxLength )
131
144
{
132
145
Dictionary < ( string Name , string ? Schema ) , Dictionary < ( string Name , string ? Schema ) , List < IConventionEntityType > > > ?
133
- clashingTables
134
- = null ;
146
+ clashingTables = null ;
135
147
foreach ( var entityType in model . GetEntityTypes ( ) )
136
148
{
137
149
var tableName = entityType . GetTableName ( ) ;
@@ -646,6 +658,107 @@ protected virtual bool AreCompatible(
646
658
return null ;
647
659
}
648
660
661
+ private void UniquifyDefaultConstraintNames (
662
+ IConventionEntityType entityType ,
663
+ Dictionary < ( string , string ? ) , ( IConventionProperty , StoreObjectIdentifier ) > defaultConstraints ,
664
+ in StoreObjectIdentifier storeObject ,
665
+ int maxLength )
666
+ {
667
+ foreach ( var property in entityType . GetProperties ( ) )
668
+ {
669
+ var constraintName = property . GetDefaultConstraintName ( storeObject ) ;
670
+ if ( constraintName == null )
671
+ {
672
+ continue ;
673
+ }
674
+
675
+ var columnName = property . GetColumnName ( storeObject ) ;
676
+ if ( columnName == null )
677
+ {
678
+ continue ;
679
+ }
680
+
681
+ if ( ! defaultConstraints . TryGetValue ( ( constraintName , storeObject . Schema ) , out var otherPropertyPair ) )
682
+ {
683
+ defaultConstraints [ ( constraintName , storeObject . Schema ) ] = ( property , storeObject ) ;
684
+ continue ;
685
+ }
686
+
687
+ var ( otherProperty , otherStoreObject ) = otherPropertyPair ;
688
+ if ( storeObject == otherStoreObject
689
+ && columnName == otherProperty . GetColumnName ( storeObject )
690
+ && AreCompatibleDefaultConstraints ( property , otherProperty , storeObject ) )
691
+ {
692
+ continue ;
693
+ }
694
+
695
+ var newConstraintName = TryUniquifyDefaultConstraint ( property , constraintName , storeObject . Schema , defaultConstraints , storeObject , maxLength ) ;
696
+ if ( newConstraintName != null )
697
+ {
698
+ defaultConstraints [ ( newConstraintName , storeObject . Schema ) ] = ( property , storeObject ) ;
699
+ continue ;
700
+ }
701
+
702
+ var newOtherConstraintName = TryUniquifyDefaultConstraint ( otherProperty , constraintName , storeObject . Schema , defaultConstraints , otherStoreObject , maxLength ) ;
703
+ if ( newOtherConstraintName != null )
704
+ {
705
+ defaultConstraints [ ( constraintName , storeObject . Schema ) ] = ( property , storeObject ) ;
706
+ defaultConstraints [ ( newOtherConstraintName , otherStoreObject . Schema ) ] = otherPropertyPair ;
707
+ }
708
+ }
709
+ }
710
+
711
+ /// <summary>
712
+ /// Gets a value indicating whether two default constraints with the same name are compatible.
713
+ /// </summary>
714
+ /// <param name="property">A property with a default constraint.</param>
715
+ /// <param name="duplicateProperty">Another property with a default constraint.</param>
716
+ /// <param name="storeObject">The identifier of the store object.</param>
717
+ /// <returns><see langword="true" /> if compatible</returns>
718
+ protected virtual bool AreCompatibleDefaultConstraints (
719
+ IReadOnlyProperty property ,
720
+ IReadOnlyProperty duplicateProperty ,
721
+ in StoreObjectIdentifier storeObject )
722
+ => property . GetDefaultValue ( storeObject ) == duplicateProperty . GetDefaultValue ( storeObject )
723
+ && property . GetDefaultValueSql ( storeObject ) == duplicateProperty . GetDefaultValueSql ( storeObject ) ;
724
+
725
+ private static string ? TryUniquifyDefaultConstraint (
726
+ IConventionProperty property ,
727
+ string constraintName ,
728
+ string ? schema ,
729
+ Dictionary < ( string , string ? ) , ( IConventionProperty , StoreObjectIdentifier ) > defaultConstraints ,
730
+ in StoreObjectIdentifier storeObject ,
731
+ int maxLength )
732
+ {
733
+ var mappedTables = property . GetMappedStoreObjects ( StoreObjectType . Table ) ;
734
+ if ( mappedTables . Count ( ) > 1 )
735
+ {
736
+ // For TPC and some entity splitting scenarios we end up with multiple tables having to define the constraint.
737
+ // Since constraint name has to be unique, we can't keep the same name for all
738
+ // Disabling this scenario until we have better way to configure the constraint name
739
+ // see issue #27970
740
+ if ( property . GetDefaultConstraintNameConfigurationSource ( ) == null )
741
+ {
742
+ throw new InvalidOperationException (
743
+ RelationalStrings . ImplicitDefaultNamesNotSupportedForTpcWhenNamesClash ( constraintName ) ) ;
744
+ }
745
+ else
746
+ {
747
+ throw new InvalidOperationException (
748
+ RelationalStrings . ExplicitDefaultConstraintNamesNotSupportedForTpc ( constraintName ) ) ;
749
+ }
750
+ }
751
+
752
+ if ( property . Builder . CanSetAnnotation ( RelationalAnnotationNames . DefaultConstraintName , null ) )
753
+ {
754
+ constraintName = Uniquifier . Uniquify ( constraintName , defaultConstraints , n => ( n , schema ) , maxLength ) ;
755
+ property . Builder . HasAnnotation ( RelationalAnnotationNames . DefaultConstraintName , constraintName ) ;
756
+ return constraintName ;
757
+ }
758
+
759
+ return null ;
760
+ }
761
+
649
762
private void UniquifyTriggerNames (
650
763
IConventionEntityType entityType ,
651
764
Dictionary < string , ( IConventionTrigger , StoreObjectIdentifier ) > triggers ,
0 commit comments