Массовая конфигурация модели

Если аспект необходимо настроить одинаково для нескольких типов сущностей, следующие методы позволяют уменьшить дублирование кода и консолидировать логику.

См. полный пример проекта , содержащий фрагменты кода, представленные ниже.

Массовая конфигурация в OnModelCreating

Каждый объект построителя, возвращаемый из ModelBuilder объекта Model или Metadata свойства, предоставляющий низкоуровневый доступ к объектам, составляющим модель. В частности, существуют методы, позволяющие выполнять итерацию определенных объектов в модели и применять к ним общую конфигурацию.

В следующем примере модель содержит пользовательский тип Currencyзначения:

public readonly struct Currency
{
    public Currency(decimal amount)
        => Amount = amount;

    public decimal Amount { get; }

    public override string ToString()
        => $"${Amount}";
}

Свойства этого типа по умолчанию не обнаруживаются, так как текущий поставщик EF не знает, как сопоставить его с типом базы данных. Этот фрагмент кода OnModelCreating добавляет все свойства типа Currency и настраивает преобразователь значений в поддерживаемый тип: decimal

foreach (var entityType in modelBuilder.Model.GetEntityTypes())
{
    foreach (var propertyInfo in entityType.ClrType.GetProperties())
    {
        if (propertyInfo.PropertyType == typeof(Currency))
        {
            entityType.AddProperty(propertyInfo)
                .SetValueConverter(typeof(CurrencyConverter));
        }
    }
}
public class CurrencyConverter : ValueConverter<Currency, decimal>
{
    public CurrencyConverter()
        : base(
            v => v.Amount,
            v => new Currency(v))
    {
    }
}

Недостатки API метаданных

  • В отличие от API Fluent, каждое изменение модели необходимо выполнить явным образом. Например, если некоторые свойства Currency были настроены в качестве навигации по соглашению, необходимо сначала удалить навигацию, ссылающуюся на свойство CLR, прежде чем добавить для него свойство типа сущности. No 9117 улучшит это.
  • Соглашения выполняются после каждого изменения. Если вы удалите навигацию, обнаруженную соглашением, то соглашение будет выполняться снова и может добавить его обратно. Чтобы предотвратить это, необходимо либо отложить соглашения до тех пор, пока свойство не будет добавлено путем вызова DelayConventions() и последующего удаления возвращаемого объекта или пометки свойства CLR как игнорируемого с помощью AddIgnored.
  • Типы сущностей могут быть добавлены после этого итерации, и конфигурация не будет применена к ним. Обычно это можно предотвратить, разместив этот код в конце OnModelCreating, но если у вас есть два взаимозависимых набора конфигураций, возможно, не будет порядка, который позволит им применяться согласованно.

Настройка предварительного соглашения

EF Core 6.0 позволяет указать конфигурацию сопоставления один раз для заданного типа СРЕДЫ CLR; затем эта конфигурация применяется ко всем свойствам этого типа в модели по мере их обнаружения. Это называется "конфигурацией модели с предварительным соглашением", так при этом настраиваются аспекты модели, которые затем используются соглашениями о построении модели. Такая конфигурация применяется путем переопределения ConfigureConventions типа, производного от DbContext.

В этом примере показано, как настроить все свойства типа Currency для преобразования значений:

protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder)
{
    configurationBuilder
        .Properties<Currency>()
        .HaveConversion<CurrencyConverter>();
}

В этом примере показано, как настроить некоторые аспекты для всех свойств типа string:

configurationBuilder
    .Properties<string>()
    .AreUnicode(false)
    .HaveMaxLength(1024);

Примечание

Тип, указанный в вызове ConfigureConventions , может быть базовым типом, интерфейсом или определением универсального типа. Все соответствующие конфигурации будут применяться в порядке от наименьшего значения:

  1. Интерфейс
  2. Базовый тип
  3. Определение универсального типа
  4. Тип значения, не допускающий значения Null
  5. Точный тип

Игнорирование типов

Конфигурация предварительного соглашения также позволяет игнорировать тип и предотвращать его обнаружение в соответствии с соглашениями как тип сущности, так и как свойство типа сущности:

configurationBuilder
    .IgnoreAny(typeof(IList<>));

Сопоставление типов по умолчанию

Как правило, EF может преобразовывать запросы с константами типа, который не поддерживается поставщиком, если вы указали преобразователь значений для свойства этого типа. Однако в запросах, которые не содержат каких-либо свойств этого типа, ef не может найти правильный преобразователь значений. В этом случае можно вызвать DefaultTypeMapping добавление или переопределение сопоставления типов поставщика:

configurationBuilder
    .DefaultTypeMapping<Currency>()
    .HasConversion<CurrencyConverter>();

Ограничения конфигурации предварительного соглашения

  • Многие аспекты не могут быть настроены с помощью этого подхода. #6787 расширит этот параметр до дополнительных типов.
  • В настоящее время конфигурация определяется только типом СРЕДЫ CLR. #20418 разрешает пользовательские предикаты.
  • Эта конфигурация выполняется до создания модели. Если при его применении возникают конфликты, трассировка стека исключений не будет содержать ConfigureConventions метод, поэтому может быть труднее найти причину.