2012-09-13 4 views
1

좋아요. 그래서 처음으로 커다란 (ish) EF 4.1 POCO + MVC 어플리케이션을 만들었습니다. 레거시 시스템을 대체하므로 기존 데이터베이스를 사용하고 있습니다.EF, POCO, DB First ... "set"속성에서 비즈니스 로직을 수행하는 방법?

DbContext T4 세대를 사용하여 POCO 클래스를 생성했습니다. 정말 멋진 폼이 있고 MVC 클래스의 많은 섹시한 제네릭이 보일러 플레이트 코드를 줄이기 위해 정말 좋은 유효성 검사를하고 있습니다. 모두 좋습니다.

갑자기 나는 (내게있어) 일부 비즈니스 로직이 내 POCO 객체의 일부 속성의 "집합"에 있다는 것을 깨달았다.

예. 다음 클래스가 T4에 의해 생성되었다고 가정합니다.

public partial class SalesOrderLine 
{ 
    public int ID { get; set; } 
    public int SalesOrderID { get; set; } 
    public int ProductID { get; set; } 
    public decimal UnitPrice { get; set; } 
    public int Quantity { get; set; } 
    public decimal ExtendedPrice { get; set; } 

    public virtual Product Product { get; set; } 
    public virtual SalesOrder SalesOrder { get; set; } 
} 

잠시 계산 된 필드 "의 ExtendedPrice는"심지어 데이터베이스에을 저장, 그리고 그냥 타고 나와 함께

을 따라 와서는 안된다는 명백한 인수를 무시합니다. 이 객체가 실제로 판매 주문 라인을 표현하도록되어있는 경우 ..then, 내가 다음과 같은 단위 테스트가 작동하도록 내 객체를 생성 할 수 있어야한다고, 논리적으로, 나에게 보인다

SalesOrderLine sol = new SalesOrderLine(); 
sol.UnitPrice = 100; 
sol.Quantity = 5; 
Assert.IsEqual(sol.ExtendedPrice, 500); 

. .. 분명히 나는 ​​할 수 없다. 내가 T4에 의해 기본 POCO가 생성되기를 원하는 한. 내가 몇 가지 옵션이 나에게 보인다

  1. 생성 된 코드 파일의 속성을 "컴파일되지 않습니다"다른 파일로 생성 된 코드를 복사 및 붙여 넣기 및 수정 설정 설정의 비즈니스 로직을 수행하는 "설정" 단가 또는 수량이 설정된 경우 확장 된 가격. 여기서 단점은 데이터베이스에서 개체가로드 될 때마다 논리가 실행된다는 것입니다 (EF는 개인 필드가 아닌 공용 속성을 설정하므로). 또한이 개체는 데이터베이스 변경이 발생할 때 프로젝트의 나머지 수명 동안 수동으로 유지 관리해야합니다.

  2. DbContext의 SaveChanges()에 의해 호출 된 내 개체에 대한 유효성 검사 루틴에서 호출되는 UpdateTotals 함수를 만듭니다. 분명히 위의 단위 테스트는이 경우 작동하지 않습니다. 그러나 시스템과 내 통합 테스트는 작동 할 것이고 오브젝트가 변경 될 때만 코드를 호출합니다.

  3. 잘못된 질문을하고 "SetPrice"및 "SetQuantity"라는 개체에 메서드를 추가하고 UnitPrice 및 Quantity의 접근자를 "내부"로 한정해야한다고 결정하십시오. . 여기서의 단점은 MVC가 폼에서 모델을 업데이트하고 업데이트 할 것이므로 이러한 속성을 설정할 수 없다는 것입니다.

  4. 내가 이미 가지고있는 것보다 훨씬 더 추상화 수준을 만드는 두 개 또는 세 개의 프레임 워크를 다운로드하는 몇 가지 솔루션 ... 저장소 패턴 또는 "NHibernate 사용"또는 이와 비슷한 것 ...이 방법을 제안 할 수 있습니다. 그러나 나는 그것을 "학문적으로 올바른"방식으로하기 위해 일을 설정하는 것이 얼마나 많은 일에 지치고 있습니다. 이 프로젝트에서 필자는 장기 유지 보수성 대 개발 속도 스펙트럼의 절반을 충족시키고 여분의 도구 및 DLL을 사용하여 프로젝트를 복잡하게 만들지 않을 것입니다.그러나 나는이 열린 마음을 유지하려고합니다 :)

--- 편집 : 또 다른 아이디어 ---

[5] 또 다른 생각, 필드는 항상 단순히이 계산되기 때문에 데이터베이스 또는 다른 방법으로 설정하지 않아도됩니다. 따라서이 같은 작동 될 수 있습니다

public decimal ExtendedAmount 
{ 
    get { return UnitPrice * Quantity; } 
    internal set { } 
} 

... 내 생각은 객체가 저장하거나 확인되었을 때, 다음의 EF 인스턴스화이 (가) "설정"전화를 시도 할 것이다, 그러나 세트가 아무것도하지 않는 것입니다 변화를 위해서 그것은 'get'이라고 부를 것이고 계산 된 값을 반환 할 것이고 그 값은 DB에 저장 될 것입니다. 여기서 유일한 단점은 데이터베이스가 ExtendedAmount 필드에 저장된 잘못된 값을 가졌을 때 개체 모델을 사용하여 데이터베이스의 유효성을 검사하려고 할 때입니다. 그것은 약간의 hokie입니다,하지만 저는 흥미로운 속임수라고 생각했습니다 ... 사실 "set"은 예외 일 가능성이 있습니다 (값! = 단가 * 수량)

--- END EDIT ---

나는 그것이 일반적이라고 확신하기 때문에 이러한 종류의 사례에서 다른 사람들이 한 일을 듣고 싶어합니다. 많은 자습서가 "데이터베이스에서 POCO 클래스 생성"까지 당신을 데리고 가서 프로젝트 개발의 나머지 부분을 맡길 것입니다.

건배, 크리스

답변

1

몇 가지 아이디어 :

  1. Code First를 사용할 수 있습니까? 그렇게하면 비즈니스 로직 (예 : 계산 된 속성)을 엔티티 클래스에 넣을 수 있습니다.

    public partial class SalesOrderLine 
    { 
        public int ID { get; set; } 
        public int SalesOrderID { get; set; } 
        public int ProductID { get; set; } 
    
        private decimal _unitPrice; 
        public decimal UnitPrice 
        { 
         get { return _unitPrice; } 
         set 
         { 
           if (value == _unitPrice) return; 
           _unitPrice = value; 
           CalculateExtendedPrice(); 
         } 
        } 
    
        private decimal _quantity; 
        public decimal Quantity 
        { 
         get { return _quantity; } 
         set 
         { 
           if (value == _quantity) return; 
           _quantity= value; 
           CalculateExtendedPrice(); 
         } 
        } 
    
        public decimal ExtendedPrice { get; set; } 
    
        public virtual Product Product { get; set; } 
        public virtual SalesOrder SalesOrder { get; set; } 
    
        private void CalculateExtendedPrice() 
        { 
         ExtendedPrice = UnitPrice * Quantity; 
        } 
    } 
    
  2. 코드 첫 번째는 옵션이 아닌 경우, 어떤 당신의 기업을 만들기에 대한 partial class (아직 연결하지 않은 경우) 별도의 코드 파일에서 비즈니스 로직을 넣어 (하지만 같은과 클래스 이름). 이렇게하면 생성 할 때 주 코드 파일을 덮어 쓰게되지만 보조 코드 파일은 그대로 유지됩니다. 이것은 생성 된 코드를 다루는 일반적인 방법입니다.

+0

1. 나는 레거시 데이터베이스로 시작 했으므로 코드를 만들었지 만 지금은 여기서부터 코드 유지 (또는 이중 유지 관리)를 할 수 있다고 생각합니다. 나는 여전히 프레임 워크가 모든 가격 필드를로드하고 데이터베이스에서 인스턴스화 된 모든 엔티티에 대한 계산 코드를 호출한다는 사실에 조금 귀찮다. –

+0

2. 부분 클래스는 속성 set/get 접근자를 재정의 할 수 없습니다. (아마 나는 여기에서 뭔가를 놓치고있다). 부분 클래스에는 이미 많은 새 함수와 읽기 전용 계산 된 속성이 있지만 AFAIK에서는 두 개의 부분 클래스 파일에서 같은 이름으로 정의 된 동일한 속성을 가질 수 없습니다. –

+0

2, 아아, 아니요, 하나의 부분 클래스의 속성을 다른 부분 클래스에서 재정의 할 수 없습니다. 그러나, 생성 된 코드가'INotifyPropertyChanged'를 구현하면, 커스텀 partial 클래스에서'PropertyChanged' 이벤트를 처리하고 거기에서 계산을 시작할 수 있습니다. 엔티티가로드 될 때마다 발생하는 계산에 대해 신경 쓰지 않는 한 성능 저하를 야기하는 경우에만 관심을 가져야합니다. 계산이 간단하면 문제가되지 않습니다. – devuxer

관련 문제