Hi there,
I'm generating my EF Core model from a Postgres database and I noticed that varchar fields receive the StringLength attribute but char(...) fields don't. Is there a way to configure this so that they receive the StringLength attribute as well? I'm using the attributes for validation in my REST API so having the attribute generated would be helpful to prevent invalid values throwing an exception on insert.
The relevant columns in the database:
| Column | Type | Modifiers |
|--------------|------------------------|-----------|
| translation | character varying(100) | not null | |
| dflt_station | character(8) | |
And the relevant parts of the model:
```[Required][Column("translation")]
[Required]
[StringLength(100)]
public string Translation { get; set; }
[Column("dflt_station", TypeName = "character(8)")]
public string DfltStation { get; set; }
As you can see the char column receives the TypeName attribute instead of StringLength.
The model is being generated with the command:
Scaffold-DbContext "..." Npgsql.EntityFrameworkCore.PostgreSQL -o Models -Context "..." -ContextDir Database -f -d
```
This is occurring with Dotnet Core 2.2, Npgsql.EntityFrameworkCore.PostgreSQL 2.2, and EntityFramework 6.2.0
I'm not sure if this is on your side or on EF Core so sorry for the trouble.
Thanks!
Could you clarify the versions you're using? Are you using borth EF and EF Core in the same app? Or are you seeing this happen in separate apps that use either EF or EF Core?
I'm using EF Core only, sorry for the confusion.
@roji See https://github.com/aspnet/EntityFrameworkCore/issues/9579 In 2.2, I believe this should be working, so if it's not there may be some mismatch between EF and the provider, or it may not be working completely for other providers.
/cc @smitpatel
I would mark this as duplicate of https://github.com/aspnet/EntityFrameworkCore/issues/9580
In the meantime, if Postgre is able to construct the type without requiring ColumnType attribute, (i.e. inferable based on facets), then it should work in 2.2 also.
@smitpatel @ajcvickers I checked and this seems a bit more complicated...
The provider currently has two separate mappings for char (fixed-length) and varchar (not fixed-length). Currently there's a bug where char's type mapping does not specify fixedLength=true in its RelationalTypeMappingParameters. If I understand everything correctly, this is why the store type is set by the scaffolder ([Column("dflt_station", TypeName = "character(8)")].
Once I fixed this in a local branch and made char's mapping returned fixedLength=true, the situation changed: I now get two identical string columns:
c#
[StringLength(10)]
public string chars { get; set; }
[StringLength(10)]
public string varchars { get; set; }
Note that the distinction between char and varchar has been lost - no mentioned of the fixed length was scaffolded either via attributes or the fluent API, although the type mapping returned by the mapper contains fixedlength=true. Is this an issue on the EF Core side or am I missing something?
@roji - Since fixed length does not have corresponding data annotation, it is always scaffolded via Fluent API. For above code, scaffolder should output IsFixedLength for chars property in OnModelCreating. If you are not seeing it then it may be bug (though looking at the code, it should be present)
@smitpatel I dove into it a little and I think there's an issue on the EF Core side...
In ScaffoldingTypeMapper, there's a section that tries to find out whether fixedLength=true is the default, inferred setting - if it is there's no need to specify it explicitly on the scaffolded model:
```c#
var fixedLengthMapping = _typeMappingSource.FindMapping(
typeof(string),
null,
keyOrIndex,
unicode: mapping.IsUnicode,
size: mapping.Size,
fixedLength: true); // This is odd
scaffoldFixedLength = fixedLengthMapping.IsFixedLength != stringMapping.IsFixedLength ? (bool?)stringMapping.IsFixedLength : true;
```
The part I don't understand is where fixedLength is true in this call - if you pass in a RelationalTypeMappingInfo with fixedLength true, then obviously you'll get back a mapping with the same, and assume that it's the default and does not need to be scaffolded.
Compare this with the check for maxLength just below, where the length isn't passed into the check. In that case, if the length on the returned mapping is equal to the one specified, that indeed means it can be omitted from the scaffolded model.
Note that the same issue seems to exist with the unicode check above. Assuming I haven't totally misunderstood this code, I can submit a fix.
/cc @bricelam @ajcvickers
@roji - That indeed a bug.
Basically, we are supposed to pass in the default value for the facet and see if the typemapping sets the facet as what we got from server. Hence we don't need to scaffold. For Unicode default is true, for size default is null, for fixed length default is ... false.
Created https://github.com/aspnet/EntityFrameworkCore/issues/14160
Assigned Shay as a label.
Closing per aspnet/EntityFrameworkCore#14160.
Most helpful comment
@roji - That indeed a bug.
Basically, we are supposed to pass in the default value for the facet and see if the typemapping sets the facet as what we got from server. Hence we don't need to scaffold. For Unicode default is true, for size default is null, for fixed length default is ... false.