Настройка групповой конфигурации модели

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

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

Настройка групповой конфигурации в OnModelCreating

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

В следующем примере модель содержит пользовательский тип значения 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 метаданных

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

Предварительное соглашение о конфигурации

EF Core 6,0 позволяет задать конфигурацию сопоставления один раз для данного типа CLR. Эта конфигурация затем применяется ко всем свойствам этого типа в модели при их обнаружении. Это называется "конфигурацией модели с предварительным соглашением", так при этом настраиваются аспекты модели, которые затем используются соглашениями о построении модели. Такая конфигурация применяется путем переопределения <xref:Microsoft.EntityFrameworkCore.DbContext.ConfigureConventions> для типа, производного от <xref:Microsoft.EntityFrameworkCore.DbContext> .

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

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 не может найти правильный преобразователь значений. В этом случае можно вызвать, <xref:Microsoft.EntityFrameworkCore.ModelConfigurationBuilder.DefaultTypeMapping> чтобы добавить или переопределить сопоставление типа поставщика:

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

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

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