Tại sao sử dụng 'ảo' cho các thuộc tính lớp trong định nghĩa mô hình Entity Framework?


223

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

Blog chứa mẫu mã sau:

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

Mục đích của việc sử dụng virtualkhi định nghĩa một thuộc tính trong một lớp là gì? Nó có tác dụng gì?


9
Bạn đang yêu cầu hiểu mục đích chung của từ khóa 'ảo' trong C # hoặc cách nó liên quan cụ thể đến Entity Framework?
M.Babcock

2
@ M.Babcock: Tôi đang hỏi mục đích là gì khi nó liên quan đến tài sản, bởi vì tôi chưa bao giờ thấy điều này trước đây.
Gary Jones

1
Nếu bạn đã quen thuộc với cách từ khóa ảo ảnh hưởng đến tính đa hình trong các phương thức thì nó cũng tương tự đối với các thuộc tính.
M.Babcock

20
@ M.Babcock: làm thế nào tôi có thể làm cho nó rõ ràng hơn? Câu hỏi có tiêu đề "Tại sao sử dụng 'ảo' cho các thuộc tính trong các lớp?".
Gary Jones

2
@Gary - Các thuộc tính getter / setter thực sự được biên dịch tĩnh thành các phương thức. Vì vậy, chúng không phải là các lớp học truyền thống như 'Bữa tối ảo công cộng';
Shan Plourde

Câu trả lời:


248

Nó cho phép Entity Framework tạo proxy xung quanh thuộc tính ảo để thuộc tính có thể hỗ trợ tải lười biếng và theo dõi thay đổi hiệu quả hơn. Xem Từ khóa ảo có thể có tác dụng gì trong Entity Framework 4.1 Mã POCO trước? cho một cuộc thảo luận kỹ lưỡng hơn.

Chỉnh sửa để làm rõ "tạo proxy xung quanh": Bằng cách "tạo proxy xung quanh" Tôi đang đề cập cụ thể đến những gì Entity Framework làm. Entity Framework yêu cầu các thuộc tính điều hướng của bạn được đánh dấu là ảo để tải nhanh và theo dõi thay đổi hiệu quả được hỗ trợ. Xem Yêu cầu để tạo Proxy POCO .
Entity Framework sử dụng tính kế thừa để hỗ trợ chức năng này, đó là lý do tại sao nó yêu cầu một số thuộc tính nhất định được đánh dấu ảo trong các POCO lớp cơ sở của bạn. Nó thực sự tạo ra các loại mới xuất phát từ các loại POCO của bạn. Vì vậy, POCO của bạn đang hoạt động như một loại cơ sở cho các lớp con được tạo động của Entity Framework. Đó là những gì tôi có nghĩa là "tạo một proxy xung quanh".

Các lớp con được tạo động mà Khung thực thể tạo ra trở nên rõ ràng khi sử dụng Khung thực thể trong thời gian chạy, không phải ở thời gian biên dịch tĩnh. Và chỉ khi bạn kích hoạt tính năng tải hoặc thay đổi theo dõi lười biếng của Entity Framework. Nếu bạn chọn không bao giờ sử dụng các tính năng theo dõi lười biếng hoặc thay đổi theo dõi của Entity Framework (không phải là mặc định) thì bạn không cần phải khai báo bất kỳ thuộc tính điều hướng nào là ảo. Sau đó, bạn chịu trách nhiệm tự tải các thuộc tính điều hướng đó, bằng cách sử dụng cái mà Entity Framework gọi là "tải háo hức" hoặc truy xuất thủ công các loại liên quan qua nhiều truy vấn cơ sở dữ liệu. Bạn có thể và nên sử dụng lười biếng tải và thay đổi các tính năng theo dõi cho các thuộc tính điều hướng của mình trong nhiều tình huống.

Nếu bạn đã tạo một lớp độc lập và đánh dấu các thuộc tính là ảo, và chỉ cần xây dựng và sử dụng các thể hiện của các lớp đó trong ứng dụng của riêng bạn, hoàn toàn nằm ngoài phạm vi của Entity Framework, thì các thuộc tính ảo của bạn sẽ không thu được gì cho bạn sở hữu.

Chỉnh sửa để mô tả lý do tại sao các thuộc tính sẽ được đánh dấu là ảo

Các thuộc tính như:

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

Không phải là lĩnh vực và không nên nghĩ như vậy. Chúng được gọi là getters và setters và tại thời điểm biên dịch, chúng được chuyển đổi thành các phương thức.

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

Đó là lý do tại sao chúng được đánh dấu là ảo để sử dụng trong Entity Framework, nó cho phép các lớp được tạo động để ghi đè các hàm getsethàm được tạo bên trong . Nếu getter / setters thuộc tính điều hướng của bạn đang hoạt động cho bạn trong việc sử dụng Entity Framework, hãy thử sửa đổi chúng thành các thuộc tính, biên dịch lại và xem liệu Entity Framework có thể vẫn hoạt động bình thường không:

 public virtual ICollection<RSVP> RSVPs;

2
Ý bạn là gì khi 'tạo một proxy xung quanh'? Điều gì đang thực sự xảy ra ở đây?
Gary Jones

2
Xin chào Gary, tôi đã sửa đổi câu trả lời của mình để làm rõ ý của tôi bằng cách "tạo proxy xung quanh". Hi vọng nó giúp được chút ít.
Shan Plourde

2
Nói "tài sản ... không phải là tài sản" là khá hữu ích. Tất cả các thuộc tính được triển khai như các phương thức getter và / hoặc setter, vì vậy sẽ không có nghĩa gì khi nói "thuộc tính này thực sự là một phương thức getter và setter không phải là một thuộc tính".
Ben Voigt

1
Cảm ơn phản hồi của bạn Ben, tôi nên đã làm rõ rằng "tài sản không phải là trường". Hãy cho tôi biết nếu bạn có bất kỳ phản hồi hoặc câu hỏi nào khác.
Shan Plourde

Tôi đã thay đổi từ ngữ và thêm một ví dụ mã khác để giúp giải thích "thuộc tính không phải là thuộc tính" tốt hơn một chút, vui lòng quay lại nếu bạn không muốn.
Scott Chamberlain

75

Các virtualtừ khóa trong C # cho phép một phương pháp hay tài sản được ghi đè bởi lớp trẻ. Để biết thêm thông tin, vui lòng tham khảo tài liệu MSDN về từ khóa 'ảo'

CẬP NHẬT: Điều này không trả lời câu hỏi như hiện đang được hỏi, nhưng tôi sẽ để nó ở đây cho bất cứ ai đang tìm kiếm một câu trả lời đơn giản cho câu hỏi ban đầu , không mô tả.


23
@Hooch điều này không được đánh dấu là chính xác bởi vì những gì được coi là "chính xác" không chỉ phụ thuộc vào tiêu đề câu hỏi. Tôi tưởng tượng hầu hết mọi người, bao gồm cả tôi và OP, trước tiên xử lý các virtualthuộc tính thông qua Entity Framework - ngay cả khi nó không rõ ràng trong tiêu đề của OP. Câu trả lời được chấp nhận là như vậy bởi vì nó chạm vào phía của Entity Framework của mọi thứ và làm thế nào / tại sao virtualcác thuộc tính được sử dụng trong bối cảnh đó.
Don Cheadle

22

Tôi hiểu sự thất vọng của OP, việc sử dụng ảo này không dành cho sự trừu tượng hóa theo khuôn mẫu mà công cụ sửa đổi ảo defacto có hiệu quả.

Nếu bất kỳ ai vẫn đang vật lộn với điều này, tôi sẽ đưa ra quan điểm của mình, vì tôi cố gắng giữ cho các giải pháp đơn giản và biệt ngữ ở mức tối thiểu:

Entity Framework trong một phần đơn giản sử dụng tải lười biếng, tương đương với việc chuẩn bị một cái gì đó để thực hiện trong tương lai. Điều đó phù hợp với công cụ sửa đổi 'ảo', nhưng có nhiều thứ hơn thế.

Trong Entity Framework, sử dụng thuộc tính điều hướng ảo cho phép bạn biểu thị nó là tương đương với Khóa ngoài không thể rỗng trong SQL. Bạn KHÔNG Háo hức tham gia mọi bảng có khóa khi thực hiện truy vấn, nhưng khi bạn cần thông tin - nó sẽ trở thành nhu cầu.

Tôi cũng đã đề cập đến nullable vì ban đầu nhiều thuộc tính điều hướng không liên quan. tức là trong kịch bản khách hàng / đơn hàng, bạn không phải đợi đến thời điểm đơn hàng được xử lý để tạo khách hàng. Bạn có thể, nhưng nếu bạn có một quy trình gồm nhiều giai đoạn để đạt được điều này, bạn có thể thấy cần phải duy trì dữ liệu khách hàng để hoàn thành sau này hoặc triển khai các đơn đặt hàng trong tương lai. Nếu tất cả các thuộc tính điều hướng đã được triển khai, bạn phải thiết lập mọi Khóa ngoại và trường quan hệ khi lưu. Điều đó thực sự chỉ đặt dữ liệu trở lại vào bộ nhớ, đánh bại vai trò của sự bền bỉ.

Vì vậy, mặc dù có vẻ khó hiểu khi thực thi thực tế vào thời gian chạy, tôi đã tìm thấy quy tắc tốt nhất để sử dụng là: nếu bạn xuất dữ liệu (đọc vào Mô hình xem hoặc Mô hình nối tiếp) và cần các giá trị trước khi tham chiếu, đừng sử dụng ảo; Nếu phạm vi của bạn đang thu thập dữ liệu có thể không đầy đủ hoặc cần tìm kiếm và không yêu cầu mọi tham số tìm kiếm đã hoàn thành cho tìm kiếm, mã sẽ sử dụng tốt tham chiếu, tương tự như sử dụng thuộc tính giá trị null? Dài?. Ngoài ra, trừu tượng hóa logic kinh doanh của bạn từ bộ sưu tập dữ liệu của bạn cho đến khi cần tiêm nó có nhiều lợi ích về hiệu suất, tương tự như khởi tạo một đối tượng và bắt đầu nó ở mức null. Entity Framework sử dụng rất nhiều phản xạ và động lực, có thể làm giảm hiệu suất và nhu cầu phải có một mô hình linh hoạt có thể mở rộng theo yêu cầu là rất quan trọng để quản lý hiệu suất.

Đối với tôi, điều đó luôn có ý nghĩa hơn là sử dụng các thuật ngữ công nghệ quá tải như proxy, đại biểu, xử lý và như vậy. Một khi bạn nhấn lang lập trình thứ ba hoặc thứ tư, nó có thể trở nên lộn xộn với những thứ này.


14

Nó khá phổ biến để định nghĩa các thuộc tính điều hướng trong một mô hình là ảo. Khi một thuộc tính điều hướng được định nghĩa là ảo, nó có thể tận dụng chức năng Entity Framework nhất định. Một phổ biến nhất là lười tải.

Tải nhanh là một tính năng hay của nhiều ORM vì nó cho phép bạn tự động truy cập dữ liệu liên quan từ một mô hình. Nó sẽ không lấy dữ liệu liên quan một cách không cần thiết cho đến khi nó thực sự được truy cập, do đó làm giảm việc truy vấn dữ liệu từ cơ sở dữ liệu.

Từ cuốn sách "ASP.NET MVC 5 với Bootstrap và Knockout.js"


3

Trong ngữ cảnh của EF, việc đánh dấu một thuộc tính là ảo cho phép EF sử dụng tải lười biếng để tải nó. Để lười tải hoạt động, EF phải tạo một đối tượng proxy ghi đè các thuộc tính ảo của bạn bằng cách triển khai tải thực thể được tham chiếu khi truy cập lần đầu tiên. Nếu bạn không đánh dấu tài sản là ảo thì lười tải sẽ không hoạt động với nó.


0

Từ khóa ảo được sử dụng để sửa đổi một phương thức, thuộc tính, bộ chỉ mục hoặc khai báo sự kiện và cho phép nó bị ghi đè trong một lớp dẫn xuất. Ví dụ, phương thức này có thể bị ghi đè bởi bất kỳ lớp nào kế thừa nó:

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

Bạn không thể sử dụng công cụ sửa đổi ảo với các công cụ sửa đổi tĩnh, trừu tượng, riêng tư hoặc ghi đè. Ví dụ sau đây cho thấy một thuộc tính ảo:

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

Điều này là hoàn toàn ra khỏi chủ đề bro.
Eru

0

Chúng ta không thể nói về các thành viên ảo mà không đề cập đến đa hình . Trong thực tế, một hàm, thuộc tính, bộ chỉ mục hoặc sự kiện trong lớp cơ sở được đánh dấu là ảo sẽ cho phép ghi đè từ lớp dẫn xuất.

Theo mặc định, các thành viên của một lớp là không ảo và không thể được đánh dấu là nếu các bộ sửa đổi tĩnh, trừu tượng, riêng tư hoặc ghi đè.

Ví dụ Hãy xem xét phương thức ToString () trong System.Object . Bởi vì phương thức này là một thành viên của System.Object, nó được kế thừa trong tất cả các lớp và sẽ cung cấp các phương thức ToString () cho tất cả chúng.

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

Đầu ra của mã trước đó là:

VirtualMembersArticle.Company

Hãy xem xét rằng chúng tôi muốn thay đổi hành vi tiêu chuẩn của các phương thức ToString () được kế thừa từ System.Object trong lớp Công ty của chúng tôi. Để đạt được mục tiêu này, đủ để sử dụng từ khóa ghi đè để khai báo một triển khai khác của phương thức đó.

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

Bây giờ, khi một phương thức ảo được gọi, thời gian chạy sẽ kiểm tra một thành viên ghi đè trong lớp dẫn xuất của nó và sẽ gọi nó nếu có. Đầu ra của ứng dụng của chúng tôi sau đó sẽ là:

Name: Microsoft

Trong thực tế, nếu bạn kiểm tra lớp System.Object, bạn sẽ thấy rằng phương thức được đánh dấu là ảo.

namespace System
{
    [NullableContextAttribute(2)]
    public class Object
    {
        ....
        public virtual string? ToString();
        ....
    }
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.