Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1356,7 +1356,13 @@ protected virtual void ColumnDefinition(
return;
}

var columnType = operation.ColumnType ?? GetColumnType(schema, table, name, operation, model)!;
var columnType = operation.ColumnType ?? GetColumnType(schema, table, name, operation, model);
if (columnType == null)
{
throw new InvalidOperationException(
RelationalStrings.UnsupportedType(operation.ClrType?.Name ?? "unknown"));
}
Comment thread
AndriySvyryd marked this conversation as resolved.
Outdated

builder
.Append(Dependencies.SqlGenerationHelper.DelimitIdentifier(name))
.Append(" ")
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.TestUtilities;
using System.ComponentModel.DataAnnotations.Schema;

namespace Microsoft.EntityFrameworkCore.SqlServer.Tests.Migrations;

/// <summary>
/// Integration test that simulates the user's original issue scenario more closely
/// </summary>
public class InvalidColumnTypeIntegrationTest
Comment thread
AndriySvyryd marked this conversation as resolved.
Outdated
{
[ConditionalFact]
public void Creating_migration_with_invalid_column_type_should_not_throw_null_reference()
{
var options = new DbContextOptionsBuilder<InvalidColumnTypeContext>()
.UseSqlServer("Server=(localdb)\\mssqllocaldb;Database=TestInvalidColumnType;Trusted_Connection=true;MultipleActiveResultSets=true")
.Options;

using var context = new InvalidColumnTypeContext(options);

// This simulates the scenario where a user has invalid Column attributes
// Before the fix, this would throw a NullReferenceException
// After the fix, this should throw a more helpful InvalidOperationException
var ex = Assert.ThrowsAny<Exception>(() =>
{
context.Database.EnsureDeleted();
context.Database.EnsureCreated();
});

// We should NOT get a NullReferenceException
Assert.IsNotType<NullReferenceException>(ex);

// We should get some form of meaningful error instead
Assert.True(ex is InvalidOperationException || ex is ArgumentException || ex is NotSupportedException);
}

public class InvalidColumnTypeContext : DbContext
{
public InvalidColumnTypeContext(DbContextOptions options) : base(options)
{
}

public DbSet<Person> People { get; set; }
public DbSet<Contract> Contracts { get; set; }
}

public class Person
{
public int Id { get; set; }

[Column(TypeName = "decimal(18,2)")] // Invalid for string - this is what the user had
public string FirstName { get; set; } = string.Empty;

[Column(TypeName = "decimal(18,2)")] // Invalid for string - this is what the user had
public string LastName { get; set; } = string.Empty;

public int Age { get; set; }
public List<Contract> Contracts { get; set; } = new();
}

public class Contract
{
public int Id { get; set; }
public string ContractLabel { get; set; } = string.Empty;
public DateTime DateSign { get; set; } = DateTime.Now;
public int? PersonId { get; set; }
public Person Person { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using Microsoft.EntityFrameworkCore.Migrations.Operations;

namespace Microsoft.EntityFrameworkCore.SqlServer.Tests.Migrations;

public class SqlServerMigrationsSqlGeneratorNullReferenceTest : MigrationsSqlGeneratorTestBase
{
public SqlServerMigrationsSqlGeneratorNullReferenceTest()
: base(SqlServerTestHelpers.Instance)
{
}

protected override string GetGeometryCollectionStoreType()
=> "geography";

[ConditionalFact]
public void CreateTableOperation_with_invalid_string_column_store_type_should_not_throw_null_reference()
Comment thread
AndriySvyryd marked this conversation as resolved.
Outdated
{
// This should NOT throw a null reference exception when an invalid store type is specified for a string column
Generate(
new CreateTableOperation
{
Name = "People",
Columns =
{
new AddColumnOperation
{
Name = "FirstName",
Table = "People",
ClrType = typeof(string),
ColumnType = "decimal(18,2)", // Invalid store type for string
IsNullable = false
}
}
});

// Let's see what SQL is generated
Assert.NotEmpty(Sql);
// If we reach here, no null reference exception was thrown, which is correct
}

[ConditionalFact]
public void Reproduce_null_reference_with_invalid_column_type_during_migration_add()
{
// This test directly tests the fix by showing that
// when GetColumnType would return null, we now get a proper exception
// instead of a null reference exception

var operation = new AddColumnOperation
{
Name = "TestColumn",
Table = "TestTable",
ClrType = typeof(System.IO.FileStream), // This should definitely not have a mapping
ColumnType = null, // Force it to call GetColumnType
IsNullable = false
};

// Let's see what SQL gets generated instead of what we expect
// This test validates that we don't get a NullReferenceException anymore
try
{
Generate(operation);
// If we get here without exception, the type mapping source found something unexpected
Assert.NotNull(Sql);
}
catch (NullReferenceException)
{
// This should NOT happen anymore with our fix
Assert.Fail("NullReferenceException should not be thrown - this indicates the fix didn't work");
}
catch (InvalidOperationException ex)
{
// This is the expected behavior with our fix
Assert.Contains("type mapping", ex.Message);
}
// Any other exception is also acceptable as long as it's not NullReferenceException
}
}
Loading