Mengapa menggunakan 'virtual' untuk properti kelas dalam definisi model Entity Framework?

223

Dalam blog berikut: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

Blog berisi contoh kode berikut:

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; }
}

Apa tujuan menggunakan virtualketika mendefinisikan properti di kelas? Apa efeknya?

Gary Jones
sumber
9
Apakah Anda meminta untuk memahami tujuan umum kata kunci 'virtual' dalam C # atau bagaimana hal itu berkaitan secara spesifik dengan Entity Framework?
M.Babcock
2
@ M.Babcock: Saya bertanya apa tujuannya karena menyangkut properti, karena saya belum pernah melihat ini sebelumnya.
Gary Jones
1
Jika Anda terbiasa dengan bagaimana kata kunci virtual mempengaruhi polimorfisme dalam metode, maka itu sama untuk properti.
M.Babcock
20
@ M.Babcock: bagaimana saya membuatnya lebih jelas? Pertanyaannya berjudul "Mengapa menggunakan 'virtual' untuk properti di kelas?".
Gary Jones
2
@Gary - properti pengambil / penyetel sebenarnya dikompilasi secara statis menjadi metode. Jadi mereka bukan bidang kelas tradisional seperti 'Makan Malam virtual publik';
Shan Plourde

Jawaban:

248

Ini memungkinkan Kerangka Entitas untuk membuat proxy di sekitar properti virtual sehingga properti dapat mendukung pemuatan yang malas dan pelacakan perubahan yang lebih efisien. Lihat Apa pengaruh kata kunci virtual dalam Entity Framework 4.1 POCO Code First? untuk diskusi yang lebih menyeluruh.

Edit untuk mengklarifikasi "buat proxy di sekitar": Oleh "buat proxy di sekitar" Saya merujuk secara khusus pada apa yang dilakukan Entity Framework. Entity Framework mengharuskan properti navigasi Anda ditandai sebagai virtual sehingga didukung oleh pemuatan yang malas dan pelacakan perubahan yang efisien. Lihat Persyaratan untuk Membuat Proxy POCO .
Kerangka Entity menggunakan warisan untuk mendukung fungsi ini, itulah sebabnya ia membutuhkan properti tertentu untuk ditandai virtual di POCO kelas dasar Anda. Ini benar-benar menciptakan tipe baru yang berasal dari tipe POCO Anda. Jadi POCO Anda bertindak sebagai tipe dasar untuk subkelas Entity Framework yang dibuat secara dinamis. Itulah yang saya maksud dengan "membuat proxy di sekitar".

Subclass yang dibuat secara dinamis yang dibuat oleh Entity Framework menjadi jelas saat menggunakan Entity Framework saat runtime, bukan pada waktu kompilasi statis. Dan hanya jika Anda mengaktifkan malas memuat atau mengubah fitur pelacakan Entity Framework. Jika Anda memilih untuk tidak pernah menggunakan pemuatan malas atau mengubah fitur pelacakan dari Entity Framework (yang bukan default) maka Anda tidak perlu mendeklarasikan salah satu properti navigasi Anda sebagai virtual. Anda kemudian bertanggung jawab untuk memuat properti navigasi itu sendiri, baik menggunakan apa yang disebut Entity Framework sebagai "eager loading", atau secara manual mengambil tipe terkait di beberapa kueri basis data. Anda dapat dan harus menggunakan lazy loading dan mengubah fitur pelacakan untuk properti navigasi Anda dalam banyak skenario.

Jika Anda membuat kelas mandiri dan menandai properti sebagai virtual, dan cukup membuat dan menggunakan instance dari kelas-kelas itu di aplikasi Anda sendiri, sepenuhnya di luar lingkup Kerangka Kerja Entitas, maka properti virtual Anda tidak akan memberi Anda apa pun pada mereka sendiri.

Edit untuk menjelaskan mengapa properti akan ditandai sebagai virtual

Properti seperti:

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

Bukan bidang dan tidak boleh dianggap seperti itu. Ini disebut getter dan setter dan pada waktu kompilasi, mereka dikonversi menjadi metode.

//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;

Itu sebabnya mereka ditandai sebagai virtual untuk digunakan dalam Entity Framework, memungkinkan kelas yang dibuat secara dinamis untuk mengesampingkan fungsi getdan setfungsi yang dihasilkan secara internal . Jika pengambil properti penentu navigasi Anda bekerja untuk Anda dalam penggunaan Kerangka Entity Anda, cobalah merevisinya untuk hanya properti, kompilasi ulang, dan lihat apakah Kerangka Entity masih dapat berfungsi dengan baik:

 public virtual ICollection<RSVP> RSVPs;
Shan Plourde
sumber
2
Apa yang Anda maksud dengan 'membuat proxy sekitar'? Apa yang sebenarnya terjadi di sini?
Gary Jones
2
Halo Gary, saya merevisi jawaban saya untuk menjelaskan apa yang saya maksud dengan "membuat proxy di sekitar". Semoga itu bisa membantu sedikit.
Shan Plourde
2
Mengatakan "properti ... bukan properti" cukup tidak membantu. Semua properti diimplementasikan sebagai metode pengambil dan / atau penyetel, sehingga tidak masuk akal untuk mengatakan "properti ini benar-benar metode pengambil dan penyetel bukan properti".
Ben Voigt
1
Terima kasih atas tanggapan Anda, Ben, saya seharusnya menjelaskan bahwa "properti bukan bidang". Beri tahu saya jika Anda memiliki umpan balik atau pertanyaan lain.
Shan Plourde
Saya mengubah kata-kata dan menambahkan contoh kode lain untuk membantu menjelaskan "properti bukan properti" sedikit lebih baik, silakan putar kembali jika Anda tidak menginginkannya.
Scott Chamberlain
75

Kata virtualkunci dalam C # memungkinkan metode atau properti ditimpa oleh kelas anak. Untuk informasi lebih lanjut silakan lihat dokumentasi MSDN pada kata kunci 'virtual'

UPDATE: Ini tidak menjawab pertanyaan seperti saat ini ditanyakan, tetapi saya akan meninggalkannya di sini untuk siapa pun yang mencari jawaban sederhana untuk pertanyaan asli , non-deskriptif yang diajukan.

M.Babcock
sumber
23
@Hooch ini tidak ditandai sebagai benar karena apa yang dianggap "benar" tidak hanya bergantung pada judul pertanyaan. Saya membayangkan sebagian besar orang, termasuk saya dan OP, menangani virtualproperti pertama kali melalui Entity Framework - meskipun itu tidak eksplisit dalam judul OP. Jawaban yang diterima begitu karena menyentuh pada sisi Kerangka Entitas hal, dan bagaimana / mengapa virtualproperti digunakan dalam konteks itu.
Don Cheadle
22

Saya mengerti frustrasi OPs, penggunaan virtual ini bukan untuk abstraksi templated bahwa defacto virtual modifier efektif untuk.

Jika ada yang masih berjuang dengan ini, saya akan menawarkan sudut pandang saya, karena saya mencoba untuk menjaga solusi sederhana dan jargon seminimal mungkin:

Entity Framework dalam sebuah karya sederhana memang memanfaatkan lazy loading, yang setara dengan menyiapkan sesuatu untuk eksekusi di masa mendatang. Itu cocok dengan pengubah 'virtual', tetapi ada lebih dari ini.

Di Entity Framework, menggunakan properti navigasi virtual memungkinkan Anda untuk menunjukkannya sebagai setara dengan kunci asing nullable di SQL. Anda tidak HARUS bergabung dengan setiap tabel yang dikunci saat melakukan kueri, tetapi ketika Anda membutuhkan informasi - itu menjadi didorong oleh permintaan.

Saya juga menyebutkan nullable karena banyak properti navigasi tidak relevan pada awalnya. yaitu dalam skenario pelanggan / pesanan, Anda tidak harus menunggu sampai saat pesanan diproses untuk membuat pelanggan. Anda bisa, tetapi jika Anda memiliki proses multi-tahap untuk mencapai ini, Anda mungkin merasa perlu untuk bertahan data pelanggan untuk penyelesaian kemudian atau untuk penyebaran untuk pesanan masa depan. Jika semua properti nav diimplementasikan, Anda harus menetapkan setiap kunci asing dan bidang relasional di save. Itu benar-benar hanya mengatur data kembali ke memori, yang mengalahkan peran kegigihan.

Jadi sementara itu mungkin tampak samar dalam eksekusi aktual pada saat run time, saya telah menemukan aturan praktis terbaik untuk digunakan adalah: jika Anda mengeluarkan data (membaca Model Tampilan atau Model Serializable) dan membutuhkan nilai sebelum referensi, jangan gunakan virtual; Jika ruang lingkup Anda mengumpulkan data yang mungkin tidak lengkap atau kebutuhan untuk mencari dan tidak mengharuskan setiap parameter pencarian selesai untuk pencarian, kode akan menggunakan referensi dengan baik, mirip dengan menggunakan properti nilai nullable int? panjang?. Juga, mengabstraksi logika bisnis Anda dari pengumpulan data Anda hingga kebutuhan untuk menyuntikkannya memiliki banyak manfaat kinerja, mirip dengan membuat instance objek dan memulainya dengan nol. Entity Framework menggunakan banyak refleksi dan dinamika, yang dapat menurunkan kinerja, dan kebutuhan untuk memiliki model yang fleksibel yang dapat disesuaikan dengan kebutuhan sangat penting untuk mengelola kinerja.

Bagi saya, itu selalu lebih masuk akal daripada menggunakan jargon teknologi kelebihan seperti proxy, delegasi, penangan dan semacamnya. Setelah Anda menekan lang pemrograman ketiga atau keempat Anda, itu bisa berantakan dengan ini.

Nathan Teague
sumber
14

Cukup umum untuk mendefinisikan properti navigasi dalam model menjadi virtual. Ketika properti navigasi didefinisikan sebagai virtual, ia dapat memanfaatkan fungsionalitas Entity Framework tertentu. Yang paling umum adalah pemuatan malas.

Pemuatan malas adalah fitur bagus dari banyak ORM karena memungkinkan Anda untuk secara dinamis mengakses data terkait dari suatu model. Itu tidak akan perlu mengambil data terkait sampai benar-benar diakses, sehingga mengurangi permintaan data di muka dari database.

Dari buku "ASP.NET MVC 5 dengan Bootstrap dan Knockout.js"

Hassan Rahman
sumber
3

Dalam konteks EF, menandai properti sebagai virtual memungkinkan EF untuk menggunakan lazy loading untuk memuatnya. Agar pemuatan malas berfungsi, EF harus membuat objek proxy yang mengesampingkan properti virtual Anda dengan implementasi yang memuat entitas yang direferensikan saat pertama kali diakses. Jika Anda tidak menandai properti sebagai virtual maka pemuatan malas tidak akan berhasil dengannya.

Shakeer Hussain
sumber
0

Kata kunci virtual digunakan untuk memodifikasi metode, properti, pengindeks, atau pernyataan acara dan memungkinkannya untuk ditimpa dalam kelas turunan. Sebagai contoh, metode ini dapat diganti oleh kelas apa pun yang mewarisinya:

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

Anda tidak dapat menggunakan pengubah virtual dengan pengubah statis, abstrak, pribadi, atau mengesampingkan. Contoh berikut menunjukkan properti virtual:

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";
            }
        }
    }
}
FatalMan
sumber
Ini benar-benar di luar topik bro.
Eru
0

Kami tidak dapat berbicara tentang anggota virtual tanpa merujuk pada polimorfisme . Bahkan, fungsi, properti, pengindeks atau peristiwa dalam kelas dasar yang ditandai sebagai virtual akan memungkinkan penggantian dari kelas turunan.

Secara default, anggota kelas adalah non-virtual dan tidak dapat ditandai sebagai pengubah statis, abstrak, pribadi, atau override.

Contoh Mari kita pertimbangkan metode ToString () di System.Object . Karena metode ini adalah anggota dari System.Object itu diwarisi di semua kelas dan akan memberikan metode ToString () kepada mereka semua.

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();
        }   
    }
}

Output dari kode sebelumnya adalah:

VirtualMembersArticle.Company

Mari kita pertimbangkan bahwa kita ingin mengubah perilaku standar metode ToString () yang diwarisi dari System.Object di kelas Perusahaan kita. Untuk mencapai tujuan ini, cukup menggunakan kata kunci override untuk mendeklarasikan implementasi lain dari metode itu.

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

Sekarang, ketika metode virtual dipanggil, run-time akan memeriksa anggota utama dalam kelas turunannya dan akan memanggilnya jika ada. Output dari aplikasi kita adalah:

Name: Microsoft

Bahkan, jika Anda memeriksa kelas System.Object Anda akan menemukan bahwa metode ini ditandai sebagai virtual.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
Ivan Porta
sumber