Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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 @@ -237,10 +237,10 @@ static SqlServerTypeMappingSource()
if (clrType == typeof(JsonTypePlaceholder))
{
// We get here when a structural type (complex type or owned entity) is mapped to JSON.
return storeTypeName switch
return mappingInfo.StoreTypeNameBase switch
{
"json" => SqlServerStructuralJsonTypeMapping.JsonTypeDefault,
"nvarchar(max)" => SqlServerStructuralJsonTypeMapping.NvarcharMaxDefault,
"nvarchar" => SqlServerStructuralJsonTypeMapping.NvarcharMaxDefault,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's not nvarchar(max) you'd need to create a new mapping with the specified length as it's done for strings below

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thanks.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AndriySvyryd on a deeper look...

First, the current behavior seems to be the same thing we had in 9.0, i.e. return an nvarchar(max) mapping that then gets cloned with the correct length facet (so the returned store type is indeed nvarchar(2000), not max).

Second, IIUC, if the user explicitly specifies a store type, for strings as well we don't go through the creation of a new string type mapping via the code you're referring to; we find nvarchar(max) or nvarchar (the base store type) in _storeTypeMappings, and then just return that (that gets cloned with the right size outside of SqlServerTypeMappingSource).

The "complicated" fragment which constructs new SqlStringStringTypeMappings kicks in only when there's no store type configured, and allows users to optionally specify the size via data annotations/fluent API. But I don't think we allow MaxLength to be configured on complex properties (they're not regular properties). In other words, if users want a non-max nvarchar, they have to currently specify the store type fully and directly, rather than rely on size data annotation/fluent API.

So I think the current thing is in line with what we're already doing for strings, but this is a tricky area, so definitely take a look and let me know what you think.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The "complicated" fragment which constructs new SqlStringStringTypeMappings kicks in only when there's no store type configured, and allows users to optionally specify the size via data annotations/fluent API. But I don't think we allow MaxLength to be configured on complex properties (they're not regular properties).

Sure, but we'll have to do it when #28591 is implemented. My preference is to have future-proof logic, but if you are going to port this this 10, then the simpler fix is better

Copy link
Member Author

@roji roji Jan 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's go with the simple fix for now - I'm thinking we should maybe patch this, as it's affecting multiple users, and there isn't a really good workaround (aside from manually editing all migrations to remove the size). What's your opinion?

BTW #28591 seems to be far less relevant, now that SQL Server has a dedicated json type (and so do most/all other databases which support JSON). We should generally start viewing nvarchar JSON mapping as legacy, in the same way that SQL Server does. And although in theory json columns can have facets, in practice they don't... So I wouldn't worry too much about this.


null when _isJsonTypeSupported => SqlServerStructuralJsonTypeMapping.JsonTypeDefault,
null => SqlServerStructuralJsonTypeMapping.NvarcharMaxDefault,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1602,6 +1602,20 @@ public void Json_is_mapped_to_nvarchar_with_compatibility_level_170_when_explici
Assert.Equal("nvarchar(max)", typeMappingSource.FindMapping(typeof(int[]), "nvarchar(max)").StoreType);
}

[ConditionalFact]
public void Json_can_be_mapped_to_nvarchar_non_max()
{
var typeMappingSource = CreateTypeMappingSource(o => o.UseSqlServer());

Assert.Equal(
"nvarchar(2000)",
typeMappingSource.FindMapping(typeof(JsonTypePlaceholder), storeTypeName: "nvarchar(2000)").StoreType);

Assert.Equal(
"nvarchar(2000)",
typeMappingSource.FindMapping(typeof(int[]), storeTypeName: "nvarchar(2000)").StoreType);
}

private SqlServerTypeMappingSource CreateTypeMappingSource(Action<DbContextOptionsBuilder> optionsAction = null)
{
var optionsBuilder = new DbContextOptionsBuilder();
Expand Down
Loading