web-dev-qa-db-ja.com

Entity Frameworkのモデル定義のクラスプロパティに 'virtual'を使用するのはなぜですか?

次のブログでは: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

このブログには、次のコードサンプルが含まれています。

public class Dinner
{
   public int DinnerID { get; set; }
   public string Title { get; set; }
   public DateTime EventDate { get; set; }
   public string Address { get; set; }
   public string HostedBy { get; set; }
   public virtual ICollection<RSVP> RSVPs { get; set; }
}

public class RSVP
{
   public int RsvpID { get; set; }
   public int DinnerID { get; set; }
   public string AttendeeEmail { get; set; }
   public virtual Dinner Dinner { get; set; }
}

クラスでプロパティを定義するときにvirtualを使用する目的は何ですか?どんな効果がありますか?

204
Gary Jones

これにより、Entity Frameworkは仮想プロパティの周囲にプロキシを作成して、プロパティが遅延読み込みとより効率的な変更追跡をサポートできるようにすることができます。より徹底的な議論については、 Entity Framework 4.1 POCOコードを最初にした場合にvirtualキーワードが持つことができる影響は? を参照してください。

「周りにプロキシを作成する」を明確にするために編集します。「周りにプロキシを作成する」では、Entity Frameworkが行うことを具体的に説明します。 Entity Frameworkでは、遅延読み込みと効率的な変更追跡がサポートされるように、ナビゲーションプロパティを仮想としてマークする必要があります。 POCOプロキシを作成するための要件 を参照してください。
Entity Frameworkは継承を使用してこの機能をサポートしているため、基本クラスのPOCOで特定のプロパティを仮想的にマークする必要があります。それは文字通りあなたのPOCOタイプから派生する新しいタイプを作成します。だからあなたのPOCOはEntity Frameworkの動的に作成されたサブクラスの基本型として機能しています。それが「プロキシを作成する」という意味です。

Entity Frameworkが作成する動的に作成されたサブクラスは、静的コンパイル時ではなく実行時にEntity Frameworkを使用するときに明らかになります。また、Entity Frameworkの遅延ロードを有効にした場合、または追跡機能を変更した場合に限ります。 Entity Frameworkの遅延読み込みや変更追跡機能を使用しないことを選択した場合(これはデフォルトではありません)、ナビゲーションプロパティを仮想として宣言する必要はありません。その後、Entity Frameworkが "eager loading"と呼ぶものを使用するか、複数のデータベースクエリにわたって関連するタイプを手動で取得するかのいずれかを使用して、これらのナビゲーションプロパティを自分でロードする必要があります。ただし、多くのシナリオでは、ナビゲーションプロパティの遅延読み込み機能と変更追跡機能を使用することができます。

スタンドアロンクラスを作成し、プロパティを仮想としてマークし、それらのクラスのインスタンスを単にEntity Frameworkの範囲外で独自のアプリケーションで構築して使用するのであれば、仮想プロパティでは何も得られません。自分の。

プロパティが仮想としてマークされる理由を説明するために編集します

以下のようなプロパティ

 public ICollection<RSVP> RSVPs { get; set; }

分野ではなく、そのように考えるべきではありません。これらはゲッターとセッターと呼ばれ、コンパイル時にメソッドに変換されます。

//Internally the code looks more like this:
public ICollection<RSVP> get_RSVPs()
{
    return _RSVPs;
}

public void set_RSVPs(RSVP value)
{
    _RSVPs = value;
}

private RSVP _RSVPs;

それが、それらがEntity Frameworkでの使用のために仮想としてマークされている理由です。それは動的に作成されたクラスが内部的に生成されたgetset関数をオーバーライドすることを可能にします。ナビゲーションプロパティのゲッター/セッターがEntity Frameworkで使用できるように動作している場合は、それらをプロパティのみに修正し、再コンパイルして、Entity Frameworkがまだ​​適切に機能できるかどうかを確認します。

 public virtual ICollection<RSVP> RSVPs;
229
Shan Plourde

C#のvirtualキーワードを使用すると、メソッドまたはプロパティを子クラスでオーバーライドできます。詳細については、 'virtual'キーワードに関するMSDNのドキュメント を参照してください。

更新:これは現在の質問の答えにはなりませんが、 オリジナルの の説明的でない質問に対する簡単な答えを探している人のためにここに残します。

71
M.Babcock

私はOPのフラストレーションを理解しています。このvirtualの使用法は事実上の仮想修飾子が効果的であるというテンプレート化された抽象化のためのものではありません。

まだこれに苦労している人がいるなら、私は私が私の視点を提供するでしょう、私は解決を単純にして専門用語を最小限に保とうとするので:

Entity Frameworkは単純な部分では遅延ロードを利用します。これは将来の実行のために何かを用意することと同じです。これは 'virtual'修飾子には当てはまりますが、これ以外にもあります。

Entity Frameworkでは、仮想ナビゲーションプロパティを使用すると、それをSQLのNULL可能な外部キーと同等のものとして表すことができます。クエリを実行するときにすべてのキー付きテーブルを積極的に結合する必要はありませんが、情報が必要なときには、要求駆動型になります。

最初は多くのナビゲーションプロパティが関連性がないため、null許容とも述べました。つまり、顧客/注文のシナリオでは、顧客を作成するために注文が処理される瞬間まで待つ必要はありません。できますが、これを達成するための多段階プロセスがある場合は、後で完了させるため、または将来の注文に展開するために、顧客データを永続化する必要があるかもしれません。すべてのナビゲーションプロパティが実装されている場合は、保存時にすべての外部キーとリレーショナルフィールドを設定する必要があります。それは本当にデータをメモリに戻すだけで、永続性の役割を無効にします。

実行時の実際の実行ではわかりにくいかもしれませんが、使用する最良の経験則は次のとおりです。データを出力していて(ビューモデルまたはシリアライズ可能モデルへの読み込み)、参照前に値が必要な場合仮想を使用します。スコープが不完全または検索の必要がある可能性があり、検索のためにすべての検索パラメーターを完成させる必要がない可能性があるデータを収集している場合、コードはnull許容値プロパティーの使用と同様に参照を活用します。長いです?。また、データコレクションからビジネスロジックを注入する必要があるまで抽象化すると、オブジェクトをインスタンス化してnullで開始するのと同様に、多くのパフォーマンス上の利点があります。 Entity Frameworkは、パフォーマンスを低下させる可能性がある多くのリフレクションとダイナミクスを使用します。また、要求に合わせて拡張できる柔軟なモデルを使用する必要性は、パフォーマンスを管理するために重要です。

私にとっては、プロキシ、デリゲート、ハンドラなどのようなオーバーロードされた技術用語を使用するよりも、常に意味がありました。あなたがあなたの3番目または4番目のプログラミング言語にぶつかると、それはこれらをめちゃくちゃにすることができます。

20
Nathan Teague

モデル内でナビゲーションプロパティを仮想に定義するのは一般的です。ナビゲーションプロパティが仮想として定義されている場合は、特定のEntity Framework機能を利用できます。最も一般的なものは遅延ロードです。

遅延ロードは、モデルから関連データに動的にアクセスできるため、多くのORMの優れた機能です。実際にアクセスされるまで関連データを不必要にフェッチすることはないため、データベースからのデータの事前照会は削減されます。

本から "ブートストラップとKnockout.jsとASP.NET MVC 5"

13
Hassan Rahman

EFのコンテキストでは、プロパティをvirtualとしてマークすると、EFは遅延ロードを使用してロードできます。遅延ロードを機能させるには、EFは仮想プロパティを最初にアクセスしたときに参照エンティティをロードする実装でオーバーライドするプロキシオブジェクトを作成する必要があります。プロパティを仮想としてマークしないと、遅延ロードは機能しません。

2
Shakeer Hussain

Virtualキーワードは、メソッド、プロパティ、インデクサー、またはイベント宣言を変更し、それを派生クラスでオーバーライドできるようにするために使用されます。たとえば、このメソッドはそれを継承するクラスでオーバーライドすることができます。

public virtual double Area() 
{
    return x * y;
}

仮想修飾子をstatic、abstract、private、またはoverride修飾子と一緒に使用することはできません。次の例は仮想プロパティを示しています。

class MyBaseClass
{
    // virtual auto-implemented property. Overrides can only
    // provide specialized behavior if they implement get and set accessors.
    public virtual string Name { get; set; }

    // ordinary virtual property with backing field
    private int num;
    public virtual int Number
    {
        get { return num; }
        set { num = value; }
    }
}


class MyDerivedClass : MyBaseClass
{
    private string name;

    // Override auto-implemented property with ordinary property
    // to provide specialized accessor behavior.
    public override string Name
    {
        get
        {
            return name;
        }
        set
        {
            if (value != String.Empty)
            {
                name = value;
            }
            else
            {
                name = "Unknown";
            }
        }
    }
}
1
FatalMan