Entity Framework model tanımlarında sınıf özellikleri için neden 'sanal' kullanılır?


223

Aşağıdaki blogda: http://weblogs.asp.net/scottgu/archive/2010/07/16/code-first-development-with-entity-framework-4.aspx

Blog aşağıdaki kod örneğini içerir:

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

virtualSınıftaki bir özelliği tanımlarken kullanmanın amacı nedir ? Ne gibi bir etkisi var?


9
C # 'sanal' anahtar kelimenin genel amacını veya özellikle Entity Framework ile nasıl ilgili olduğunu anlamak istiyor musunuz?
M.Babcock

2
@ M.Babcock: Amaçlarla ilgili amacın ne olduğunu soruyorum, çünkü bunu daha önce hiç görmedim.
Gary Jones

1
Sanal anahtar kelimenin yöntemlerde polimorfizmi nasıl etkilediğini biliyorsanız özellikler için de aynıdır.
M.Babcock

20
@ M.Babcock: bunu nasıl daha belirgin hale getirebilirdim? Soru "Neden sınıflardaki özellikler için 'sanal' kullanılır?"
Gary Jones

2
@Gary - getter / setter özellikleri aslında statik olarak yöntemlere derlenir. Yani 'kamusal sanal akşam yemeği' gibi geleneksel sınıf alanları değiller;
Shan Plourde

Yanıtlar:


248

Entity Framework'ün sanal özellik etrafında bir proxy oluşturmasına izin verir, böylece özellik tembel yüklemeyi ve daha verimli değişiklik izlemeyi destekleyebilir. Bkz . Sanal anahtar kelimenin önce Entity Framework 4.1 POCO Kodu'nda ne gibi etkileri olabilir? daha kapsamlı bir tartışma için.

Düzenlemek için "etrafında proxy oluşturmak": " etrafında bir proxy oluşturmak" tarafından özellikle Entity Framework ne yaptığı atıfta. Varlık Çerçevesi, tembel yükleme ve etkili değişiklik takibinin desteklenmesi için gezinme özelliklerinizin sanal olarak işaretlenmesini gerektirir. Bkz . POCO Proxy Oluşturma Gereksinimleri .
Entity Framework, bu işlevselliği desteklemek için miras kullanır, bu nedenle temel özellik POCO'larınızda belirli özelliklerin sanal olarak işaretlenmesini gerektirir. Kelimenin tam anlamıyla POCO türlerinizden türetilen yeni türler oluşturur. Bu nedenle POCO'nuz Entity Framework'ün dinamik olarak oluşturulmuş alt sınıfları için bir temel türü görevi görüyor. "Etrafında bir proxy oluştur" derdim.

Entity Framework'ün oluşturduğu dinamik olarak oluşturulan alt sınıflar, Entity Framework'ü statik derleme zamanında değil, çalışma zamanında kullanırken görünür hale gelir. Ve yalnızca Entity Framework'ün tembel yükleme veya değiştirme izleme özelliklerini etkinleştirirseniz. Varlık Çerçevesi'nin tembel yükleme veya değiştirme izleme özelliklerini (varsayılan olmayan) asla kullanmamayı seçerseniz, gezinme özelliklerinizden hiçbirini sanal olarak bildirmeniz gerekmez. Ardından, Entity Framework'ün "istekli yükleme" olarak adlandırdığı şeyi kullanarak veya ilgili türleri birden çok veritabanı sorgusundan manuel olarak almakla, bu gezinme özelliklerini kendiniz yüklemekle sorumlusunuz. Yine de birçok senaryoda navigasyon özellikleriniz için tembel yükleme ve izleme izleme özelliklerini kullanabilirsiniz ve kullanmalısınız.

Bağımsız bir sınıf oluşturacak ve özellikleri sanal olarak işaretleyecek ve Entity Framework kapsamı dışında kendi uygulamanızda bu sınıfların örneklerini oluşturup kullanacak olsaydınız, sanal mülkleriniz size kendi.

Özelliklerin neden sanal olarak işaretleneceğini açıklamak için düzenleyin

Gibi özellikler:

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

Alan değildir ve bu şekilde düşünülmemelidir. Bunlar getters ve setters olarak adlandırılır ve derleme sırasında yöntemlere dönüştürülür.

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

Bu yüzden Entity Framework'te kullanılmak üzere sanal olarak işaretlenirler, dinamik olarak oluşturulan sınıfların dahili olarak oluşturulan işlevleri getve setişlevleri geçersiz kılmasına izin verir . Navigasyon mülk alıcısı / ayarlayıcılarınız Entity Framework kullanımınızda sizin için çalışıyorsa, bunları yalnızca mülklere revize etmeyi, yeniden derlemeyi deneyin ve Entity Framework'ün hala düzgün çalışıp çalışmadığını görün:

 public virtual ICollection<RSVP> RSVPs;

2
'Etrafında proxy oluştur' ile ne demek istiyorsun? Aslında burada neler oluyor?
Gary Jones

2
Merhaba Gary, "etrafında bir proxy oluştur" ile ne demek istediğimi netleştirmek için cevabımı gözden geçirdim Umarım bu biraz yardımcı olur.
Shan Plourde

2
"Özellikler ... mülk değildir" demek oldukça yararsızdır. Tüm özellikler alıcı ve / veya ayarlayıcı yöntemleri olarak uygulanır, bu nedenle "bu özellik gerçekten bir özellik değil bir alıcı ve ayarlayıcı yöntemidir" demek mantıklı değildir.
Ben Voigt

1
Geri bildiriminiz için teşekkürler Ben, "mülkler alan değil" diye netleştirmeliydim. Başka geri bildiriminiz veya sorunuz varsa bize bildirin.
Shan Plourde

İfadeyi değiştirdim ve "özellikler özellik değil" i biraz daha iyi açıklamak için başka bir kod örneği ekledim, istemiyorsanız lütfen geri alınız.
Scott Chamberlain

75

virtualC # anahtar kelimenin bir yöntem veya özellik çocuk sınıfları tarafından geçersiz kılınabilir sağlar. Daha fazla bilgi için lütfen 'sanal' anahtar kelimedeki MSDN belgelerine bakın

GÜNCELLEME: Bu, soruyu şu anda sorulduğu gibi cevaplamıyor, ancak sorulan orijinal , açıklayıcı olmayan soruya basit bir cevap arayan herkes için burada bırakacağım .


23
@Hooch bu doğru olarak işaretlenmedi çünkü "doğru" kabul edilen şey sadece soru başlığına bağlı değil. Kendim ve OP'nin dahil olduğu çoğu insanın ilk olarak virtualEntity Framework aracılığıyla mülklerle uğraştığını hayal ediyorum - OP'nin başlığında açık olmasa bile. Kabul edilen cevap, şeylerin Varlık Çerçevesi tarafına ve virtualözelliklerin bu bağlamda nasıl / neden kullanıldığına değinmesidir.
Don Cheadle

22

OP'nin hayal kırıklığını anlıyorum, bu sanal kullanım, defacto sanal değiştiricinin etkili olduğu tempolu soyutlama için değil.

Herhangi biri hala bununla uğraşıyorsa, çözümleri basit ve jargonu minimumda tutmaya çalışırken bakış açımı sunarım:

Entity Framework, basit bir parçada, gelecekteki yürütme için bir şey hazırlamanın karşılığı olan tembel yüklemeyi kullanır. Bu 'sanal' değiştiriciye uyuyor, ancak daha fazlası var.

Entity Framework'te, sanal bir gezinme özelliği kullanmak, onu SQL'deki boş bir Yabancı Anahtarın eşdeğeri olarak göstermenize olanak tanır. Bir sorgu gerçekleştirirken her anahtarlı tabloya hevesle katılmanız gerekmez, ancak bilgiye ihtiyacınız olduğunda - talep odaklı olur.

Ayrıca, çoğu navigasyon özelliğinin başlangıçta uygun olmadığı için nullable ifadesinden de bahsetmiştim. Bir müşteri / Siparişler senaryosunda, bir müşteri oluşturmak için bir siparişin işleme alınmasını beklemek zorunda değilsiniz. Bunu yapabilirsiniz, ancak bunu başarmak için çok aşamalı bir süreciniz varsa , müşteri verilerini daha sonra tamamlanması veya gelecekteki siparişlere dağıtım için devam ettirmeniz gerekebilir . Tüm nav mülkleri uygulanmışsa, kayıttaki her Yabancı Anahtarı ve ilişkisel alanı kurmanız gerekir. Bu gerçekten de verileri tekrar belleğe ayarlar, bu da kalıcılık rolünü yener.

Bu nedenle, çalışma zamanında gerçek yürütmede şifreli görünse de, kullanılacak en iyi kuralın şu olduğunu tespit ettim: veri çıkarıyorsanız (bir Görünüm Modeline veya Seri Modeline okuma) ve referanslardan önce değerlere ihtiyacınız varsa, sanal kullanın; Kapsamınız eksik veya arama yapılması gerekebilecek ve bir arama için tamamlanan her arama parametresini gerektirmeyen veriler topluyorsa, kod nullable değer özellikleri int? uzun?. Ayrıca, enjekte etme ihtiyacı olana kadar iş mantığınızı veri toplama işleminizden soyutlamak, bir nesneyi örneklemeye ve boş olarak başlatmaya benzer birçok performans avantajına sahiptir. Entity Framework, performansı düşürebilen çok fazla yansıma ve dinamik kullanır ve talebe göre ölçeklendirilebilen esnek bir modelin olması, performansı yönetmek için kritik öneme sahiptir.

Bana göre bu, proxy'ler, delegeler, işleyiciler ve benzeri gibi aşırı yüklenmiş teknoloji jargonunu kullanmaktan her zaman daha mantıklıydı. Üçüncü veya dördüncü programlama dilinize çarptığınızda, bunlar dağınık olabilir.


14

Bir modeldeki gezinme özelliklerini sanal olarak tanımlamak oldukça yaygındır. Bir navigation property sanal olarak tanımlandığında, bazı Entity Framework işlevlerinden yararlanabilir. En yaygın olanı tembel yükleme.

Tembel yükleme, birçok ORM'nin hoş bir özelliğidir, çünkü bir modelden ilgili verilere dinamik olarak erişmenizi sağlar. Gerçekte erişilene kadar ilgili verileri gereksiz yere getirmeyecek ve böylece veri tabanından verilerin ön sorgulamasını azaltacaktır.

"ASP.NET MVC 5, Bootstrap ve Knockout.js ile birlikte" kitabından


3

EF bağlamında, bir özelliği sanal olarak işaretlemek EF'in onu yüklemek için tembel yüklemeyi kullanmasına izin verir. Tembel yüklemenin çalışması için EF, başvurulan varlığı ilk erişildiğinde yükleyen bir uygulamayla sanal özelliklerinizi geçersiz kılan bir proxy nesnesi oluşturmalıdır. Özelliği sanal olarak işaretlemezseniz, tembel yükleme onunla çalışmaz.


0

Sanal anahtar sözcük, bir yöntemi, özelliği, dizin oluşturucuyu veya olay bildirimini değiştirmek ve türetilmiş bir sınıfta geçersiz kılınmasına izin vermek için kullanılır. Örneğin, bu yöntem onu ​​devralan herhangi bir sınıf tarafından geçersiz kılınabilir:

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

Sanal değiştiriciyi statik, soyut, özel veya geçersiz kılma değiştiricileriyle kullanamazsınız. Aşağıdaki örnek sanal bir özelliği göstermektedir:

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

Bu tamamen konu dışı kardeşim.
Eru

0

Polimorfizmden bahsetmeden sanal üyeler hakkında konuşamayız . Aslında, sanal olarak işaretlenmiş bir temel sınıftaki bir işlev, özellik, dizinleyici veya olay türetilmiş bir sınıftan geçersiz kılmaya izin verir.

Varsayılan olarak, bir sınıfın üyeleri sanal değildir ve statik, soyut, özel veya geçersiz kılma değiştiricileri gibi işaretlenemez.

Örnek System.Object içindeki ToString () yöntemini ele alalım . Bu yöntem System.Object öğesinin bir üyesi olduğu için tüm sınıflarda devralınır ve hepsine ToString () yöntemlerini sağlar.

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

Önceki kodun çıktısı:

VirtualMembersArticle.Company

Şirket sınıfımızda System.Object kaynağından devralınan ToString () yöntemlerinin standart davranışını değiştirmek istediğimizi düşünelim. Bu hedefe ulaşmak için, bu yöntemin başka bir uygulamasını bildirmek üzere override anahtar sözcüğünü kullanmak yeterlidir.

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

Şimdi, sanal bir yöntem çağrıldığında, çalışma zamanı türetilmiş sınıfında geçersiz kılınan bir üye olup olmadığını kontrol eder ve varsa çağırır. Uygulamamızın çıktısı şu şekilde olacaktır:

Name: Microsoft

Aslında, System.Object sınıfını kontrol ederseniz, yöntemin sanal olarak işaretlendiğini göreceksiniz.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.