バッキング フィールド

バッキング フィールドは、プロパティにではなくフィールドに対して 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; }
    }
}

バッキング フィールドが検出されるのは、モデルに含まれるプロパティだけです。 どのプロパティがモデルに含まれるかの詳細については、含まれるプロパティと除外されるプロパティに関するページを参照してください。

フィールド名が前述の規約に沿っていない場合などは、データの注釈または Fluent API を使用してバッキング フィールドを構成することもできます。

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 では、他のアクセス パターンもサポートされています。 たとえば、次のサンプルでは、バッキング フィールドへの書き込みを具体化時のみに限定し、その他のケースではプロパティを使用するよう 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 クエリから参照することが必要になる場合もありますが、そのようなフィールドはプライベートであることが一般的です。 LINQ クエリでは、EF.Property(...) メソッドを使用することで、そのようなフィールドを参照できます。

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