@@ -27,6 +27,7 @@ public static void ValidateTables()
27
27
continue ;
28
28
}
29
29
30
+ // Validate that there's a primary key set.
30
31
if ( primaryKeyProps . Count == 0 )
31
32
{
32
33
errors . AppendLine ( $ "Error: Class '{ type . Name } ' is marked as a Magic Table but has no primary key property. A primary key is required.") ;
@@ -36,6 +37,37 @@ public static void ValidateTables()
36
37
errors . AppendLine ( $ "Error: Class '{ type . Name } ' has multiple properties marked with MagicPrimaryKeyAttribute. Only one primary key is allowed.") ;
37
38
}
38
39
40
+ foreach ( var prop in primaryKeyProps )
41
+ {
42
+ var primaryKeyAttribute = prop . GetCustomAttribute < MagicPrimaryKeyAttribute > ( ) ;
43
+
44
+ // Check if auto-increment is true, but type is not numeric
45
+ if ( primaryKeyAttribute != null && primaryKeyAttribute . AutoIncrement )
46
+ {
47
+ if ( ! IsNumericType ( prop . PropertyType )
48
+ //Future: Allow GUIDs as Primary Keys
49
+ // Update the comment to say numeric or guid when uncommented
50
+ //prop.PropertyType == typeof(Guid)
51
+ )
52
+ {
53
+ errors . AppendLine ( $ "Error: Primary key '{ prop . Name } ' in class '{ type . Name } ' is marked as auto-increment, but its type '{ prop . PropertyType . Name } ' is not numeric.") ;
54
+ }
55
+ }
56
+
57
+ // Ensure primary key is NOT nullable
58
+ if ( Nullable . GetUnderlyingType ( prop . PropertyType ) != null )
59
+ {
60
+ errors . AppendLine ( $ "Error: Primary key '{ prop . Name } ' in class '{ type . Name } ' cannot be nullable.") ;
61
+ }
62
+ }
63
+
64
+
65
+ /*
66
+ * Prevent any Magic attribute to be
67
+ * appended to more than one property.
68
+ * This isn't allowed, only 1 magic
69
+ * attribute per property!
70
+ */
39
71
foreach ( var prop in properties )
40
72
{
41
73
var magicAttributes = new List < Type >
@@ -58,6 +90,10 @@ public static void ValidateTables()
58
90
}
59
91
}
60
92
93
+ /*
94
+ * Run the GetCompoundKey and GetCompoundIndexes on each class
95
+ * to enforce constructor to fire all validations.
96
+ */
61
97
var instance = Activator . CreateInstance ( type ) ;
62
98
var getCompoundKeyMethod = type . GetMethod ( "GetCompoundKey" , BindingFlags . Public | BindingFlags . Instance ) ;
63
99
var getCompoundIndexesMethod = type . GetMethod ( "GetCompoundIndexes" , BindingFlags . Public | BindingFlags . Instance ) ;
@@ -72,20 +108,53 @@ public static void ValidateTables()
72
108
{
73
109
// Call both methods and force any errors to surface
74
110
var compoundKey = getCompoundKeyMethod . Invoke ( instance , null ) ;
75
- var compoundIndexes = getCompoundIndexesMethod . Invoke ( instance , null ) ;
111
+ var compoundIndexesObj = getCompoundIndexesMethod . Invoke ( instance , null ) ;
112
+
113
+ // Validate that compound indexes don't have duplicate indexes that overlap
114
+ var compoundIndexSets = new HashSet < string > ( ) ;
115
+
116
+ // Ensure the returned object is a List<IMagicCompoundIndex>
117
+ if ( compoundIndexesObj is List < IMagicCompoundIndex > compoundIndexes )
118
+ {
119
+ foreach ( var index in compoundIndexes )
120
+ {
121
+ string indexKey = string . Join ( "," , index . ColumnNamesInCompoundIndex . OrderBy ( x => x ) ) ;
122
+
123
+ if ( compoundIndexSets . Contains ( indexKey ) )
124
+ {
125
+ errors . AppendLine ( $ "Error: Duplicate compound index detected in class '{ type . Name } ' with the same properties ({ indexKey } ).") ;
126
+ }
127
+ else
128
+ {
129
+ compoundIndexSets . Add ( indexKey ) ;
130
+ }
131
+ }
132
+ }
76
133
}
77
134
catch ( TargetInvocationException tie ) when ( tie . InnerException != null )
78
135
{
79
136
// Extract and log the **actual** exception instead of the generic wrapper
80
137
errors . AppendLine ( $ "Error: Class '{ type . Name } ' encountered an issue when calling 'GetCompoundKey()' or 'GetCompoundIndexes()'. Exception: { tie . InnerException . Message } ") ;
81
138
}
82
- }
139
+ }
83
140
}
84
141
85
142
if ( errors . Length > 0 )
86
143
{
87
144
throw new Exception ( $ "Magic Table Validation Errors:\n { errors } ") ;
88
145
}
89
146
}
147
+
148
+ private static bool IsNumericType ( Type type )
149
+ {
150
+ Type underlyingType = Nullable . GetUnderlyingType ( type ) ?? type ;
151
+ return underlyingType == typeof ( byte ) || underlyingType == typeof ( sbyte ) ||
152
+ underlyingType == typeof ( short ) || underlyingType == typeof ( ushort ) ||
153
+ underlyingType == typeof ( int ) || underlyingType == typeof ( uint ) ||
154
+ underlyingType == typeof ( long ) || underlyingType == typeof ( ulong ) ||
155
+ underlyingType == typeof ( float ) || underlyingType == typeof ( double ) ||
156
+ underlyingType == typeof ( decimal ) ;
157
+ }
158
+
90
159
}
91
160
}
0 commit comments