Skip to content

Skip ALTER COLUMN for computed columns when only CLR type changes#38252

Open
m-x-shokhzod wants to merge 1 commit into
dotnet:mainfrom
m-x-shokhzod:fix/migrations-computed-column-clr-type-change
Open

Skip ALTER COLUMN for computed columns when only CLR type changes#38252
m-x-shokhzod wants to merge 1 commit into
dotnet:mainfrom
m-x-shokhzod:fix/migrations-computed-column-clr-type-change

Conversation

@m-x-shokhzod
Copy link
Copy Markdown
Contributor

The migration model differ produced an AlterColumnOperation when the CLR type of a property mapped to a computed column changed (e.g. intlong for a column with .HasComputedColumnSql("DATALENGTH(...)")). The resulting ALTER TABLE ... ALTER COLUMN then failed at runtime with:

Cannot alter column 'FileSize' because it is 'COMPUTED'.

A computed column's store type and collation are derived from the expression; they aren't user-configurable. CLR-type-only changes are metadata on the EF side and require no database-side change.

Fix

In MigrationsModelDiffer.Diff(IColumn, IColumn, DiffContext), suppress columnTypeChanged and collationChanged when both source and target are computed with the same ComputedColumnSql and IsStored:

var isComputedWithUnchangedExpression =
    source.ComputedColumnSql != null
    && target.ComputedColumnSql != null
    && MultilineEquals(source.ComputedColumnSql, target.ComputedColumnSql)
    && source.IsStored == target.IsStored;

var columnTypeChanged = !isComputedWithUnchangedExpression && source.StoreType != target.StoreType;
var collationChanged = !isComputedWithUnchangedExpression && source.Collation != target.Collation;

Behavior

Scenario Before After
Computed column, CLR type changes (intlong), same expression AlterColumnOperationALTER COLUMN → SQL Server rejects No-op migration ✓
Computed column, expression changes (DATALENGTHLEN) AlterColumnOperation → SQL generator's drop+add path Unchanged — still triggers drop+add ✓
Non-computed column, type changes AlterColumnOperationALTER COLUMN Unchanged
Computed column, nullability/comment/order/annotation changes AlterColumnOperation produced Unchanged — still produced ✓

Tests

Full Relational.Tests suite passes (1427/1428, the 1 skip is pre-existing). SqlServer.Tests suite passes (1335/1335). End-to-end coverage will run on the SqlServer 2019/2022/2025 CI matrix.

Fixes #33425

@m-x-shokhzod m-x-shokhzod requested a review from a team as a code owner May 10, 2026 05:43
@roji
Copy link
Copy Markdown
Member

roji commented May 10, 2026

A computed column's store type and collation are derived from the expression; they aren't user-configurable. CLR-type-only changes are metadata on the EF side and require no database-side change.

Have you verified whether this is a SQL Server-specific thing, or universal to all databases? I'm pretty sure it's the former, in which case you're introducing a SQL Server-specific change into ModelDiffer, which is provider-independent.

The migration model differ produced an AlterColumnOperation when the CLR
type of a property mapped to a computed column changed (e.g. int → long
for a column with .HasComputedColumnSql("DATALENGTH(...)")). The
generated ALTER TABLE ... ALTER COLUMN then failed at runtime with
"Cannot alter column ... because it is 'COMPUTED'".

A computed column's store type and collation are derived from the
expression; they aren't user-configurable. CLR-type-only changes are
metadata on the EF side and require no database-side change. SQL Server
specifically rejects ALTER COLUMN on computed columns altogether.

The fix lives in SqlServerMigrationsSqlGenerator (provider-specific):
when both source and target are computed, set alterStatementNeeded to
false. This suppresses the ALTER COLUMN emission while letting other
separately-emitted facets (notably the comment block at line 488 via
sp_addextendedproperty) still apply. The drop+add path for expression
changes earlier in the method is unchanged.

MigrationsModelDiffer remains provider-independent: it still produces
AlterColumnOperation as before; other providers (MySQL with MODIFY
COLUMN, PostgreSQL, etc.) decide for themselves how to handle it.

Tests:
- Unit: AlterColumnOperation_computed_column_with_only_clr_type_change_is_noop
  asserts the generator produces empty SQL for the bug case.
- Unit: AlterColumnOperation_computed_column_with_changed_expression_drops_and_adds
  guards the existing drop+add path.
- Functional: Alter_computed_column_clr_type_only_change_is_noop runs the
  scenario end-to-end against real SQL Server in the CI matrix; verified
  locally against Azure SQL Edge on Apple Silicon.

Fixes dotnet#33425
@m-x-shokhzod m-x-shokhzod force-pushed the fix/migrations-computed-column-clr-type-change branch from ad662f8 to cdc292a Compare May 11, 2026 00:17
@m-x-shokhzod
Copy link
Copy Markdown
Contributor Author

m-x-shokhzod commented May 11, 2026

Good point — you're right. The constraint is SQL Server specific: PostgreSQL and SQLite have similar but narrower restrictions on generated columns, and MySQL handles type changes on generated columns fine via MODIFY COLUMN.

Reworked (force-pushed cdc292aff8):

  • MigrationsModelDiffer.cs reverted to upstream main — provider-independent again.
  • The fix now lives in SqlServerMigrationsSqlGenerator.Generate(AlterColumnOperation): when both source and target are computed, alterStatementNeeded is set to false. This suppresses only the ALTER COLUMN emission; the comment block via sp_addextendedproperty further down still runs (which the unit test approach in my first version missed — caught by the Alter_computed_column_add_comment regression).
  • Added an end-to-end test Alter_computed_column_clr_type_only_change_is_noop in MigrationsSqlServerTest that exercises the customer-reported scenario end-to-end. Verified locally against real SQL Server (Azure SQL Edge on Apple Silicon) — passes. Will also run on the SqlServer 2019/2022/2025 CI matrix.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Migration tries to alter column type of computed column if CLR type changes.

2 participants