Entity Frameworkモデル定義のクラスプロパティに「仮想」を使用する理由


223

次のブログ: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クラスでプロパティを定義するときに使用する目的は何ですか?どんな効果がありますか?


9
C#の「仮想」キーワードの一般的な目的、またはそれが具体的にEntity Frameworkにどのように関係するかを理解するように求めていますか?
M.バブコック

2
@M。バブコック:私はこれまでこれまで見たことがなかったので、それがプロパティに関係するので、目的は何かを尋ねています。
Gary Jones

1
virtualキーワードがメソッドのポリモーフィズムにどのように影響するかを理解している場合は、プロパティも同じです。
M.バブコック

20
@M。バブコック:どうすればもっとはっきりさせることができますか?質問のタイトルは「クラスのプロパティに「仮想」を使用する理由」です。
Gary Jones

2
@Gary-getter / setterプロパティは実際には静的にメソッドにコンパイルされます。したがって、これらは「パブリックバーチャルディナー」などの従来のクラスフィールドではありません。
Shan Plourde

回答:


248

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

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

Entity Frameworkが作成する動的に作成されたサブクラスは、静的コンパイル時ではなく実行時にEntity Frameworkを使用すると明らかになります。また、Entity Frameworkの遅延読み込みまたは変更追跡機能を有効にした場合のみ。Entity Frameworkの遅延読み込みまたは変更追跡機能を使用しないことを選択した場合(これはデフォルトではありません)、ナビゲーションプロパティを仮想として宣言する必要はありません。次に、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で使用するために仮想としてマークされている理由です。これにより、動的に作成されたクラスが内部で生成さgetれたset関数と関数をオーバーライドできます。Entity Frameworkの使用法でナビゲーションプロパティのゲッター/セッターが機能している場合は、プロパティのみに変更して再コンパイルし、Entity Frameworkが引き続き適切に機能するかどうかを確認してください。

 public virtual ICollection<RSVP> RSVPs;

2
「プロキシを作成する」とはどういう意味ですか?ここで実際に何が起こっているのですか?
Gary Jones

2
こんにちはゲイリー、私は「プロキシを作成する」とはどういう意味かを明確にするために私の回答を修正しました。少しお役に立てば幸いです。
Shan Plourde

2
「プロパティ...はプロパティではない」と言ってもまったく役に立ちません。すべてのプロパティはゲッターおよび/またはセッターメソッドとして実装されるため、「このプロパティは実際にはプロパティではなくゲッターおよびセッターメソッドです」と言っても意味がありません。
Ben Voigt 2013

1
ベンのフィードバックに感謝します。「プロパティはフィールドではない」ことを明確にすべきでした。他にご意見やご質問がございましたらお知らせください。
Shan Plourde

表現を変更し、「プロパティはプロパティではない」を説明するのに役立つ別のコード例を追加しました。必要ない場合はロールバックしてください。
Scott Chamberlain

75

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

更新:これは現在尋ねられている質問の答えにはなりませんが、尋ねられた元の説明的でない質問に対する簡単な答えを探している人のためにここに残しておきます。


23
@Hoochこれは正しいとマークされていません。「正しい」と見なされるのは質問のタイトルだけに依存するものではないためです。私やOPを含め、ほとんどの人が最初にvirtualEntity Frameworkを介してプロパティを処理することを想像します。たとえそれがOPのタイトルで明示されていなくてもです。受け入れられた答えは、物事のエンティティフレームワーク側、およびvirtualそのコンテキストでプロパティがどのように/どのように使用されるかについて触れているためです。
Don Cheadle 2015年

22

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

まだこれで苦労している場合は、ソリューションをシンプルにして専門用語を最小限に抑えるように努めるので、私は私の視点を提供します。

単純な部分のEntity Frameworkは遅延読み込みを利用します。これは、将来の実行のために何かを準備することと同じです。これは 'virtual'修飾子に適合しますが、これ以外にもあります。

Entity Frameworkでは、仮想ナビゲーションプロパティを使用すると、SQLのnull可能な外部キーに相当するものとしてそれを示すことができます。クエリを実行するときにすべてのキー付きテーブルを熱心に結合する必要はありませんが、情報が必要な場合は、需要主導になります。

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

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

私にとっては、プロキシ、デリゲート、ハンドラーなどのオーバーロードされた技術用語を使用するよりも、常にそれが理にかなっています。3番目または4番目のプログラミング言語にぶつかると、これらが乱雑になる可能性があります。


14

モデルでナビゲーションプロパティを仮想的に定義することは非常に一般的です。ナビゲーションプロパティを仮想として定義すると、特定のEntity Framework機能を利用できます。最も一般的なのは遅延読み込みです。

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

本「BootstrapとKnockout.jsを備えたASP.NET MVC 5」から


3

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


0

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

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

virtual修飾子を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";
            }
        }
    }
}

これは完全に話題の仲間ではありません。
Eru

0

ポリモーフィズムを参照せずに仮想メンバーについて語ることはできません。実際、仮想としてマークされた基本クラスの関数、プロパティ、インデクサー、またはイベントは、派生クラスからのオーバーライド許可します。

デフォルトでは、クラスのメンバーは非仮想ですあり、静的修飾子、抽象修飾子、プライベート修飾子、またはオーバーライド修飾子の場合、そのようにマークすることはできません。

System.ObjectのToString()メソッドを 考えてみましょう。このメソッドはSystem.Objectのメンバーであるため、すべてのクラスで継承され、すべてのクラスにToString()メソッドを提供します。

namespace VirtualMembersArticle
{
    public class Company
    {
        public string Name { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            Company company = new Company() { Name = "Microsoft" };
            Console.WriteLine($"{company.ToString()}");
            Console.ReadLine();
        }   
    }
}

前のコードの出力は次のとおりです。

VirtualMembersArticle.Company

CompanyクラスのSystem.Objectから継承されたToString()メソッドの標準動作を変更したいとします。この目標を達成するには、overrideキーワードを使用して、そのメソッドの別の実装を宣言するだけで十分です。

public class Company
{
    ...
    public override string ToString()
    {
        return $"Name: {this.Name}";
    }         
}

これで、仮想メソッドが呼び出されると、ランタイムは派生クラスのオーバーライドメンバーをチェックし、存在する場合はそれを呼び出します。アプリケーションの出力は次のようになります。

Name: Microsoft

実際、System.Objectクラスを確認すると、メソッドが仮想としてマークされていることがわかります。

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.