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.Babcock

2
@ M. 밥콕 : 나는 전에 이것을 본 적이 없기 때문에 그 목적이 속성과 관련이 있는지 묻고 있습니다.
Gary Jones

1
virtual 키워드가 메소드의 다형성에 어떻게 영향을 미치는지 잘 알고 있다면 속성과 동일합니다.
M.Babcock

20
@ M. 밥콕 : 어떻게 더 분명하게 만들 수 있습니까? 문제는 "클래스의 속성에 '가상'을 사용하는 이유는 무엇입니까?"입니다.
게리 존스

2
@Gary-getter / setter 속성은 실제로 정적으로 메서드로 컴파일됩니다. 따라서 그들은 '공공 가상 만찬'과 같은 전통적인 수업 분야가 아닙니다.
Shan Plourde

답변:


248

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;

2
'프록시 생성'이란 무엇을 의미합니까? 실제로 여기서 무슨 일이 일어나고 있습니까?
게리 존스

2
Gary 님, 저는 "대리인을 만들어서"의 의미를 명확히하기 위해 답변을 수정했습니다. 희망이 조금 도움이됩니다.
Shan Plourde

2
"속성 ... 속성이 아님"이라고 말하는 것은 매우 도움이되지 않습니다. 모든 속성은 getter 및 / 또는 setter 메서드로 구현되므로 "이 속성은 실제로 속성이 아닌 getter 및 setter 메서드"라고 말하는 것은 의미가 없습니다.
벤 Voigt

1
귀하의 의견에 감사드립니다. Ben, "속성은 필드가 아닙니다"라는 것을 분명히했습니다. 다른 의견이나 질문이 있으면 알려주세요.
Shan Plourde

"속성이 속성이 아닙니다"라는 표현을 좀 더 잘 설명 할 수 있도록 표현을 변경하고 다른 코드 예제를 추가했습니다. 원하지 않는 경우 롤백하십시오.
Scott Chamberlain

75

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

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


23
@Hooch 이것은 "정확한"것으로 간주되는 것이 단순히 질문 제목에 의존하지 않기 때문에 올바른 것으로 표시되지 않습니다. 본인과 OP가 포함 된 대부분의 사람들이 먼저 virtualEntity Framework를 통해 속성을 처리한다고 생각 합니다. OP의 제목에는 명시 적이 지 않습니다. 대답은 Entity Framework 측면과 virtual그 컨텍스트에서 속성이 사용되는 방법 / 이유 를 다루기 때문입니다.
Don Cheadle

22

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

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

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

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

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

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

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


14

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

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

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


3

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


0

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

이것은 완전히 주제가 아닙니다.
Eru

0

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

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

System.ObjectToString () 메서드를 살펴 보겠습니다 . 이 메서드는 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.