[c#] Entity Framework 모델 정의에서 클래스 속성에 ‘가상’을 사용하는 이유는 무엇입니까?

다음 블로그에서 : 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클래스에서 속성을 정의 할 때 사용하는 목적은 무엇입니까 ? 어떤 영향을 미칩니 까?



답변

Entity Framework는 가상 속성을 중심으로 프록시를 생성하여 속성이 지연 로딩 및보다 효율적인 변경 추적을 지원할 수 있도록합니다. 참조 가상 키워드는 엔티티 프레임 워크 4.1 POCO 코드 첫 번째로 할 수 있습니다 어떤 영향 (들)? 더 철저한 토론을 위해.

“프록시 생성”을 명확히하기 위해 편집 :
“프록시 생성”이란 Entity Framework의 기능을 구체적으로 언급하고 있습니다. Entity Framework에서는 지연 로딩 및 효율적인 변경 내용 추적이 지원되도록 탐색 속성을 가상으로 표시해야합니다. POCO 프록시 생성을위한 요구 사항을 참조하십시오 .
Entity Framework는 상속을 사용하여이 기능을 지원하므로 기본 클래스 POCO에서 특정 속성을 가상으로 표시해야합니다. 말 그대로 POCO 유형에서 파생되는 새로운 유형을 만듭니다. 따라서 POCO는 Entity Framework의 동적으로 생성 된 서브 클래스의 기본 유형으로 작동합니다. 이것이 바로 “프록시 생성”이라는 의미입니다.

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에서 사용하기 위해 가상으로 표시되어 동적으로 생성 된 클래스가 내부적으로 생성 된 함수 getset함수 를 재정의 할 수 있습니다 . 탐색 속성 getter / setter가 Entity Framework 사용법에서 작동하는 경우 속성으로 수정하고 다시 컴파일 한 다음 Entity Framework가 여전히 제대로 작동하는지 확인하십시오.

 public virtual ICollection<RSVP> RSVPs;


답변

virtualC # 의 키워드를 사용하면 메서드 나 속성을 자식 클래스로 재정의 할 수 있습니다. 자세한 내용 은 ‘virtual’키워드에 대한 MSDN 설명서 를 참조하십시오.

업데이트 : 현재 요청 된 질문에 대한 답변은 아니지만 원래 설명이 아닌 질문 에 대한 간단한 답변을 찾는 사람을 위해 여기에 남겨 두겠습니다 .


답변

OP의 좌절감을 이해합니다.이 가상 사용은 사실상 가상 수정자가 효과적인 템플릿 된 추상화가 아닙니다.

여전히이 문제로 어려움을 겪고 있다면 솔루션을 단순하게 유지하고 전문 용어를 최소한으로 유지하려고 노력하면서 내 견해를 제시 할 것입니다.

간단한 조각의 Entity Framework는 지연 로딩을 사용합니다. 이는 지연 실행을 위해 무언가를 준비하는 것과 같습니다. 그것은 ‘가상’수정 자에 적합하지만 이것에 더 많은 것이 있습니다.

Entity Framework에서 가상 탐색 등록 정보를 사용하면 SQL에서 널 입력 가능 외래 키와 동등한 것으로 표시 할 수 있습니다. 쿼리를 수행 할 때 모든 키 테이블을 열성적으로 조인하지 않아도되지만 정보가 필요할 때는 수요 중심이됩니다.

또한 많은 탐색 속성이 처음에는 관련이 없기 때문에 nullable을 언급했습니다. 즉, 고객 / 주문 시나리오에서는 고객을 생성하기 위해 주문이 처리 될 때까지 기다릴 필요가 없습니다. 이를 수행하기 위해 다단계 프로세스를 수행 한 경우 나중에 완료하거나 향후 주문에 배치하기 위해 고객 데이터 를 유지 해야 할 수도 있습니다 . 모든 탐색 속성이 구현 된 경우 저장시 모든 외래 키 및 관계형 필드를 설정해야합니다. 실제로 데이터를 메모리로 다시 설정하면 지속성 역할을 무효화합니다.

따라서 런타임에 실제 실행에서 암호처럼 보일 수 있지만 사용하는 가장 좋은 방법은 다음과 같습니다. 데이터를 출력하고 (View Model 또는 Serializable Model로 읽기) 참조 전에 값이 필요한 경우 가상을 사용하십시오. 스코프가 불완전하거나 검색해야 할 수있는 데이터를 수집하고 검색을 위해 모든 검색 매개 변수를 완료하지 않아도되는 경우, 코드는 nullable value 속성 int를 사용하는 것과 유사하게 참조를 잘 활용합니다. 긴?. 또한 데이터 수집에서 비즈니스 로직을 추출 할 때까지 오브젝트를 인스턴스화하고 널에서 시작하는 것과 유사한 많은 성능 이점이 있습니다. Entity Framework는 많은 리플렉션과 다이내믹스를 사용하므로 성능이 저하 될 수 있으며 성능을 관리하려면 수요에 맞게 확장 할 수있는 유연한 모델이 필요합니다.

저에게는 프록시, 델리게이트, 핸들러 등과 같은 오버로드 된 기술 전문 용어를 사용하는 것보다 항상 더 의미가 있습니다. 세 번째 또는 네 번째 프로그래밍 언어에 도달하면 이들을 어지럽 힐 수 있습니다.


답변

모델에서 탐색 속성을 가상으로 정의하는 것이 일반적입니다. 탐색 속성이 가상으로 정의되면 특정 Entity Framework 기능을 활용할 수 있습니다. 가장 일반적인 것은 게으른 로딩입니다.

지연로드는 모델에서 관련 데이터에 동적으로 액세스 할 수 있기 때문에 많은 ORM의 좋은 기능입니다. 실제로 액세스 할 때까지 관련 데이터를 불필요하게 페치하지 않으므로 데이터베이스에서 데이터의 선행 조회가 줄어 듭니다.

“부트 스트랩 및 Knockout.js가 포함 된 ASP.NET MVC 5″책에서


답변

EF와 관련하여 속성을 가상 으로 표시 하면 EF가 지연로드를 사용하여로드 할 수 있습니다. 지연로드가 작동하려면 EF는 참조 된 엔티티가 처음 액세스 될 때로드되는 구현으로 가상 특성을 대체하는 프록시 오브젝트를 작성해야합니다. 속성을 가상으로 표시하지 않으면 지연 로딩이 작동하지 않습니다.


답변

virtual 키워드는 메서드, 속성, 인덱서 또는 이벤트 선언을 수정하고 파생 클래스에서 재정의되도록 허용하는 데 사용됩니다. 예를 들어,이 메소드는이를 상속하는 모든 클래스에 의해 대체 될 수 있습니다.

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

정적, 추상, 개인 또는 대체 수정 자와 함께 가상 수정자를 사용할 수 없습니다. 다음 예제는 가상 속성을 보여줍니다.

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


답변

우리는 다형성 을 언급하지 않고 가상 멤버에 대해 이야기 할 수 없습니다 . 실제로 virtual로 표시된 기본 클래스의 함수, 속성, 인덱서 또는 이벤트 는 파생 클래스에서 재정의를 허용합니다.

기본적 으로 클래스 멤버는 가상 이 아니며 정적, 추상, 개인 또는 재정의 수정자인 경우 표시 할 수 없습니다.

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