EF Core 2.1.0 устанавливает длину строки и тип столбца по умолчанию

Поскольку Entity Framework использует nvarchar(max) по умолчанию для строк, я хотел бы установить по умолчанию что-то еще.

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

В Entity Framework 6.1.3 я мог изменить OnModelCreating(DbModelBuilder modelBuilder) следующим образом:

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"));

Если я затем изменил свойство с помощью аннотаций данных, EF вместо этого использовал бы эти значения, например:

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

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

Однако, используя Microsoft.EntityFrameworkCore.SqlServer 2.1.0, я не могу этого сделать, и я тоже не могу использовать Conventions.

Я мог бы решить datetime таким образом, но если я попытаюсь сделать то же самое для строк, миграция скажет type: "nvarchar(256)", maxLength: 128, если я, например, использую аннотации данных. Как я могу это решить?

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(DateTime)))
{
    property.Relational().ColumnType = "datetime2(0)";
}

Зачем использовать разные размеры в nvarchar и maxlength?

CodeNotFound 13.06.2018 17:28

@CodeNotFound. Это проблема, миграция генерирует такой код в ядре EF: Comment = table.Column<string>(type: "nvarchar(256)", maxLength: 128, nullable: false),. В EF 6.1.3 этого не произошло.

Ogglas 13.06.2018 17:33

Кстати, я не знаю, почему вы просто не используете nvarchar (128) вместо nvarchar (MAX)?

CodeNotFound 13.06.2018 17:54
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
11
3
8 539
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Есть несколько атрибутов, косвенно влияющих на тип столбца свойства string - MaxLength (например, varchar(256) против varchar(MAX), IsUnicode (например, nvarchar против varchar) и IsFixedLength (например, char против varchar).

Текущая модель API несовместима. Первый доступен через GetMaxLength и SetMaxLength, второй - через IsUnicode и IsUnicode, а для третьего нет публичного API модели (только свободный API).

Итак, чтобы установить MaxLength, вы можете использовать:

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

что не совсем правильно, потому что null в данном случае имеет двойное значение - не указано и MAX.

Правильный способ требует использования внутреннего API EF Core, который предоставляет гораздо больше методов конфигурации, в частности, позволяет передавать значение перечисления ConfigurationSource вместе со значением атрибута. Значения перечисления ConfigurationSource определяют приоритет конфигурации: Convention - самый низкий, затем DataAnnotation и, наконец, Explicit - самый высокий. Вся идея в том, что конфигурация с более низким приоритетом не перезаписывает конфигурацию, уже установленную более высоким приоритетом. Все общедоступные свободно распространяемые API используют Explcit, в то время как в нашем случае Convention идеально подходит (поскольку мы имитируем обычное значение по умолчанию).

Поэтому, если вы принимаете предупреждение «Этот API поддерживает инфраструктуру Entity Framework Core и не предназначен для использования непосредственно из вашего кода. Этот API может быть изменен или удален в будущих выпусках»., добавьте

using Microsoft.EntityFrameworkCore.Metadata.Internal;

и использовать

foreach (var property in modelBuilder.Model.GetEntityTypes()
    .SelectMany(t => t.GetProperties())
    .Where(p => p.ClrType == typeof(string)))
{
    property.AsProperty().Builder
        .HasMaxLength(256, ConfigurationSource.Convention);
}

Могу ли я узнать, было ли обновление этого ответа, это действительно работает, но, поскольку это внутренний метод, он может не поддерживаться в будущих обновлениях, поэтому есть ли лучший подход, который следует использовать вместо этого?

Steven Price 02.05.2021 12:19

@StevenPrice. Когда он изменяется, или, лучше, когда EF Core публикует соглашения, ответ будет обновлен. В настоящее время даже последняя версия EFC 5.x не предоставляет общедоступный API для таких задач. Существует общедоступный интерфейс IConventionPropertyBuilder, но нет общедоступного способа его получить, поскольку конструктор класса PropertyBuilder (который его реализует) также помечен как «внутренний API».

Ivan Stoev 02.05.2021 12:36

Другие вопросы по теме