EF Core 2.1.0 set default string length and column type

c# entity-framework entity-framework-core

Question

Since Entity Framework uses nvarchar(max) as default for strings I would like to set something else as default.

https://dba.stackexchange.com/questions/48408/ef-code-first-uses-nvarcharmax-for-all-strings-will-this-hurt-query-performan

In Entity Framework 6.1.3 I could modify OnModelCreating(DbModelBuilder modelBuilder) like this:

modelBuilder.Properties<DateTime>().Configure(c => c.HasColumnType("datetime2"));
modelBuilder.Properties<DateTime>().Configure(c => c.HasPrecision(0));

modelBuilder.Properties<string>()
    .Configure(s => s.HasMaxLength(256).HasColumnType("nvarchar"));

If I then modified a property with data annotations EF used these values instead, like this:

[MaxLength(128)]
public string Name { get; set; }

[Column(TypeName = "nvarchar(MAX)")]
[MaxLength]
public string Comment { get; set; }

However using Microsoft.EntityFrameworkCore.SqlServer 2.1.0 I cant do it like this and I can't use Conventions either.

I could solve datetime like this but if I try to do the same for strings the migration says type: "nvarchar(256)", maxLength: 128 if I use data annotations for example. How can I solve this?

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(DateTime)))
{
    property.Relational().ColumnType = "datetime2(0)";
}
1
5
6/13/2018 3:14:41 PM

Accepted Answer

There are several attributes indirectly affecting the column type of a string property - MaxLength (e.g. varchar(256) vs varchar(MAX), IsUnicode (e.g. nvarchar vs varchar) and IsFixedLength (e.g. char vs varchar).

The current model API is inconsistent. The first is accessible through GetMaxLength and SetMaxLength, the second - via IsUnicode and IsUnicode, and there is no public model API for the third one (only fluent API).

So to set the MaxLength you could use:

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(string)))
{
    if (property.GetMaxLength() == null)
        property.SetMaxLength(256);
}

which is not fully correct, because null in this case has dual meaning - not specified and MAX.

The correct way requires using EF Core internal API, which provides much more configuration methods, in particular allowing you to pass ConfigurationSource enum value along with the attribute value. ConfigurationSource enum values define the priority of the configuration - with Convention being the lowest, then DataAnnotation and finally Explicit being the highest. The whole idea is that the lower priority configuration do not overwrite the configuration already set by a higher priority. All public fluent API use Explcit, while in our case the Convention perfectly fits (since we are simulating conventional default).

So if you accept the warning "This API supports the Entity Framework Core infrastructure and is not intended to be used directly from your code. This API may change or be removed in future releases.", add

using Microsoft.EntityFrameworkCore.Metadata.Internal;

and use

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(string)))
{
    property.AsProperty().Builder
        .HasMaxLength(256, ConfigurationSource.Convention);
}
10
6/14/2018 8:03:33 AM


Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow