Skip to content

UseNullableReferenceTypes=false generates nullable strings that v0.9 rejects at runtime #91

Description

@bazer

Summary

When model generation uses UseNullableReferenceTypes: false, DataLinq 0.9 can generate nullable reference columns that its own runtime later rejects as non-nullable.

A nullable database varchar is generated in the legacy nullable context as:

#nullable disable

[Nullable]
[Column("BankGiro")]
public abstract string BankGiro { get; }

The source generator then emits contradictory metadata:

new MetadataColumnDraft("BankGiro")
{
    Nullable = true,
}
// ...
{
    Attributes = [new NullableAttribute(), /* ... */],
    CsNullable = false,
}

When SQL returns NULL, v0.9 throws DataLinqNullabilityMismatchException because ValueProperty.CsNullable is false, even though the database column and generated model attribute both declare the column nullable and nullable reference annotations are explicitly disabled.

Verified with DataLinq.CLI 0.9.0-rc.4+9e6b80f6137e7a546a2616f62d5e6d5ec386040a.

Reproduction

Use a model-generation configuration containing:

{
  "UseNullableReferenceTypes": false
}

Generate a model from a table containing a nullable reference column, for example:

BankGiro varchar(50) NULL

Then query a row where that column is SQL NULL.

The generated model uses #nullable disable, [Nullable], and a plain string property. At runtime, materialization fails with a nullability mismatch stating that the database column is nullable but the generated model property is non-nullable.

Regenerating the models with the 0.9 CLI does not resolve the mismatch.

Expected behavior

Models generated with UseNullableReferenceTypes: false should remain usable with nullable reference columns. SQL NULL should materialize as null for a generated [Nullable] string property in a disabled nullable-annotation context.

The stricter v0.9 validation should still reject:

  • SQL NULL for non-nullable value types;
  • SQL NULL for reference properties explicitly known to be non-nullable;
  • schema/model nullability drift.

Likely cause

SyntaxParser currently derives CsNullable only from nullable type syntax:

property.SetCsNullableCore(propSyntax.Type is NullableTypeSyntax);

That is sufficient for string? with nullable reference types enabled, but not for plain string under #nullable disable. In that context, the absence of ? does not mean the CLR property cannot represent null.

The runtime nullability contract subsequently treats CsNullable = false as proof that the model rejects SQL NULL.

This is related to #11, but is the inverse case: #11 correctly aims to reject SQL NULL for genuinely non-nullable models, while this issue concerns a nullable model generated in the supported legacy nullable mode being classified as non-nullable.

Suggested regression coverage

Add an end-to-end generator/materialization test that:

  1. generates or parses a model under #nullable disable;
  2. uses [Nullable] public abstract string Value { get; };
  3. verifies Column.Nullable == true;
  4. verifies the effective model contract allows SQL NULL;
  5. materializes SQL NULL as null;
  6. preserves the existing mismatch behavior for genuinely non-nullable properties.

The fix likely needs to account for the nullable annotation context and/or the generated [Nullable] contract when constructing CsNullable, rather than relying exclusively on NullableTypeSyntax.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions