@@ -168,8 +168,14 @@ CancellationToken ct
168168 cmd . CommandText = $ "SELECT * FROM { qualifiedTable } WHERE 1 = 0";
169169 cmd . CommandType = CommandType . Text ;
170170
171+ // KeyInfo is required for Npgsql to populate AllowDBNull from
172+ // pg_attribute.attnotnull — without it, GetSchemaTable returns DBNull
173+ // for the AllowDBNull column on every row, which would make every
174+ // NOT NULL entity property look like a nullability mismatch (verified
175+ // empirically against Npgsql 10.0.1). The flag is a hint, not a
176+ // contract; SQLite and SqlClient tolerate it without behavior change.
171177 await using var reader = await cmd . ExecuteReaderAsync (
172- CommandBehavior . SchemaOnly | CommandBehavior . SingleResult ,
178+ CommandBehavior . SchemaOnly | CommandBehavior . SingleResult | CommandBehavior . KeyInfo ,
173179 ct
174180 ) ;
175181
@@ -225,9 +231,17 @@ private static Dictionary<string, ActualColumn> BuildColumnDictionary(DbDataRead
225231
226232 private static bool ParseAllowDbNull ( object ? value )
227233 {
234+ // Provider differences: Microsoft.Data.Sqlite types this column as bool;
235+ // Npgsql and SqlClient return "YES"/"NO"; some return "1"/"0".
228236 if ( value is null || value == DBNull . Value )
229237 {
230- return true ; // Unknown → lenient (don't false-flag).
238+ // When AllowDBNull is unreadable, treat the column as NOT NULL so the
239+ // nullability check becomes a safe no-op for it. The opposite default
240+ // (assume nullable) reliably false-flags every non-nullable entity
241+ // property — a regression we hit on Npgsql before adding KeyInfo above.
242+ // Defaulting to false defers detection of real drift to runtime for
243+ // this column rather than blocking pipelines that would succeed.
244+ return false ;
231245 }
232246 if ( value is bool b )
233247 {
0 commit comments