Резервные поля

Резервные поля позволяют EF читать и /или записывать данные в поле, а не свойство. Это может быть полезно, если инкапсуляция в классе используется для ограничения использования и/или улучшения семантики доступа к данным по коду приложения, но значение должно быть считываться из базы данных и /или записываться в базу данных без использования этих ограничений или улучшений.

Базовая конфигурация

По соглашению следующие поля будут обнаружены как резервные поля для заданного свойства (указанный в порядке приоритета).

  • <camel-cased property name>
  • _<camel-cased property name>
  • _<property name>
  • m_<camel-cased property name>
  • m_<property name>

В следующем примере Url свойство настроено _url в качестве резервного поля:

public class Blog
{
    private string _url;

    public int BlogId { get; set; }

    public string Url
    {
        get { return _url; }
        set { _url = value; }
    }
}

Обратите внимание, что резервные поля обнаруживаются только для свойств, включенных в модель. Дополнительные сведения о том, какие свойства включены в модель, см. в разделе "Включение и исключение свойств".

Вы также можете настроить резервные поля с помощью заметок данных или API Fluent, например, если имя поля не соответствует приведенным выше соглашениям:

public class Blog
{
    private string _validatedUrl;

    public int BlogId { get; set; }

    [BackingField(nameof(_validatedUrl))]
    public string Url
    {
        get { return _validatedUrl; }
    }

    public void SetUrl(string url)
    {
        // put your validation code here

        _validatedUrl = url;
    }
}

Доступ к полям и свойствам

По умолчанию EF всегда будет читать и записывать в резервное поле , предполагая, что он настроен правильно, и никогда не будет использовать свойство. Однако EF также поддерживает другие шаблоны доступа. Например, следующий пример указывает EF записывать в резервное поле только во время материализации и использовать свойство во всех других случаях:

protected override void OnModelCreating(ModelBuilder modelBuilder)
{
    modelBuilder.Entity<Blog>()
        .Property(b => b.Url)
        .HasField("_validatedUrl")
        .UsePropertyAccessMode(PropertyAccessMode.PreferFieldDuringConstruction);
}

Полный набор поддерживаемых параметров см. в перечислении PropertyAccessMode.

Свойства только для полей

Вы также можете создать концептуальное свойство в модели, которое не имеет соответствующего свойства CLR в классе сущностей, но вместо этого использует поле для хранения данных в сущности. Это отличается от свойств тени, где данные хранятся в отслеживании изменений, а не в типе среды CLR сущности. Свойства, доступные только для полей, обычно используются, если класс сущности использует методы вместо свойств для получения и задания значений, или в случаях, когда поля не должны предоставляться вообще в модели домена (например, первичные ключи).

Вы можете настроить свойство только для полей, указав имя в Property(...) API:

internal class MyContext : DbContext
{
    public DbSet<Blog> Blogs { get; set; }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Blog>()
            .Property("_validatedUrl");
    }
}

public class Blog
{
    private string _validatedUrl;

    public int BlogId { get; set; }

    public string GetUrl()
    {
        return _validatedUrl;
    }

    public void SetUrl(string url)
    {
        using (var client = new HttpClient())
        {
            var response = client.GetAsync(url).Result;
            response.EnsureSuccessStatusCode();
        }

        _validatedUrl = url;
    }
}

EF попытается найти свойство CLR с заданным именем или поле, если свойство не найдено. Если ни свойство, ни поле не найдены, вместо этого будет настроено теневое свойство.

Возможно, потребуется ссылаться на свойство только для полей из запросов LINQ, но такие поля обычно являются частными. Метод можно использовать в запросе EF.Property(...) LINQ для ссылки на поле:

var blogs = db.blogs.OrderBy(b => EF.Property<string>(b, "_validatedUrl"));