2009-09-16 5 views
2

저는 치과 의사가 특정 임상 활동에 대한 정보를 수집 할 수있는 응용 프로그램을 개발 중입니다. 응용 프로그램이 고도로 사용자 지정할 수있는 것은 아니지만 (사용자 지정 워크 플로 또는 양식 없음) 기본 초안 사용자 지정 기능을 제공합니다. 고객은 사전 정의 된 양식 필드를 자체 사용자 정의 양식으로 보강하도록 선택할 수 있습니다. 관리자가 작성할 수있는 필드 유형은 약 6 가지입니다 (예 : 텍스트, 날짜, 숫자, 드롭 다운 등). 우리는 지속성 측면에서 EAV (Entity-Attribute-Value)를 사용하여이 기능을 모델링했습니다.OO 및 단위 테스트 친화적 인 쿼리 시스템 설계

응용 프로그램의 다른 주요 기능 중 하나는 이러한 사용자 지정 필드에 대해 사용자 지정 쿼리를 만드는 기능입니다. 이것은 임의의 수의 규칙 (날짜 < = (지금 - 5 일), 텍스트 444 ', 드롭 다운 =='ICU ')을 생성 할 수있는 UI를 통해 수행됩니다. 모든 규칙은 쿼리를 생성하기 위해 함께 AND 연산됩니다.

현재 구현 ("상속 됨")은 객체 지향적이거나 단위 테스트 가능하지 않습니다. 근본적으로 모든 무수한 규칙 유형을 복잡한 동적 SQL 문 (즉, 내부 조인, 외부 조인 및 하위 선택)으로 직접 컴파일하는 단일 "God"클래스가 있습니다. 이 방법은 여러 가지 이유로 문제가된다 : 분리 에서

  • 단위 테스트 개별 규칙은 마지막 포인트는 가장 확실히 열기 청산 원칙을 위반하는 것입니다 향후 추가 규칙 유형을 추가하는 것을 의미
  • 거의 불가능하다.
  • 비즈니스 논리 및 지속성 문제가 혼재하고 있습니다.
  • 느린 실행 단위 테스트 진짜 데이터베이스가 필요하기 때문에 (SQLLite는 T-SQL을 구문 분석 할 수 파서을 조롱하는 것은 ... 으음 어려울 것이다)

은 내가 마련하기 위해 노력하고있어 유연하고, 유지 보수가 가능하며, 테스트가 가능한 대체 디자인은 물론 쿼리 성능을 상당히 좋게 유지합니다. 이 마지막 점은 OOAD 기반 구현이 데이터베이스 서버에서 .NET (.NET) 응용 프로그램 서버로 데이터 필터링 로직의 일부를 옮길 것이라고 생각하기 때문에 중요합니다.

나는 명령 및 체인의 책임 패턴의 조합을 고려 중이 야 :

쿼리 클래스는 추상적 인 규칙 클래스의 모음 (DateRule, TextRule 등)가 포함되어 있습니다. 필터링되지 않은 데이터 집합을 포함하는 DataSet 클래스에 대한 참조를 보유합니다. DataSet은 지속성에 좌우되지 않는 방식으로 모델링됩니다. 즉, 데이터베이스 유형에 대한 참조 또는 후크가 없습니다.

규칙에는 DataSet을 가져 와서 적절하게 필터링 한 다음 호출자에게 반환하는 단일 Filter() 메소드가 있습니다. Query 클래스는 단순히 각 규칙을 반복하는 것보다는 각 규칙이 적합하다고 판단되는대로 DataSet을 필터링 할 수 있도록합니다. 일단 모든 규칙이 실행되거나 DataSet이 필터링되지 않으면 실행이 중지됩니다.

이 접근 방식에 대해 저를 걱정하는 한 가지 점은 .NET에서 잠재적으로 커다란 필터링되지 않은 데이터 집합을 구문 분석 할 때의 성능 관련 사항입니다. 확실히 유지 보수 성과 성능 사이의 균형을 잘 맞추는 이런 종류의 문제를 해결하기위한 몇 가지 방법이 있습니다.

마지막주의 사항 : 경영진은 NHibernate의 사용을 허용하지 않을 것입니다. Linq에서 SQL 로의 변환이 가능할 수도 있지만,이 기술이 현재 당면한 과제에 얼마나 적절하게 적용될 수 있을지 확신 할 수 없습니다.

많은 분들께 감사드립니다.

업데이트 : 아직 해결책을 찾고 있습니다.

답변

1

저는 LINQ to SQL이 VS2008 샘플의 Dynamic LINQ와 결합 된 이상적인 솔루션이라고 생각합니다. 특히 IEnumerable/IQueryable의 확장 메서드를 사용하여 LINQ를 사용하면 얻는 입력에 따라 표준 및 사용자 지정 논리를 사용하여 쿼리를 작성할 수 있습니다. 이 기술을 사용하여 많은 MVC 작업에 대한 필터를 효과적으로 구현합니다. 실제로 표현식 트리를 작성한 후이 쿼리를 사용하여 쿼리를 구체화해야하는 시점에 SQL을 생성합니다. 따라서 SQL Server에서 많은 부분을 수행하기 때문에 시나리오에 이상적이라고 생각합니다. LINQ가 최적이 아닌 쿼리를 생성하는 경우에는 항상 최적화 된 쿼리를 활용하는 방법으로 LINQ 데이터 컨텍스트에 추가 된 테이블 반환 함수 또는 저장 프로 시저를 사용할 수 있습니다.

업데이트 됨 : PredicateBuilder을 C# 3.0 Nutshell에서 사용해 볼 수도 있습니다.

예 : 제목에 검색어 집합 중 하나가 포함되어 있고 게시자가 O'Reilly 인 모든 책을 찾으십시오.

var predicate = PredicateBuilder.True<Book>(); 
predicate = predicate.And(b => b.Publisher == "O'Reilly"); 
var titlePredicate = PredicateBuilder.False<Book>(); 
foreach (var term in searchTerms) 
{ 
    titlePredicate = titlePredicate.Or(b => b.Title.Contains(term)); 
} 
predicate = predicate.And(titlePredicate); 

var books = dc.Book.Where(predicate); 
+0

희 희 - "자바 스크립트 해봤습니까?" –

+0

팀, 답장을 보내 주셔서 감사합니다. 나는 당신이 참조 할 수있는 Dynamic LINQ 샘플을 제외하고 내가 공부할 수있는 샘플 코드를 가지고 있지 않다고 생각하지 않습니까? –

0

나는 그것이 다 봤어요 방법은 개체를 만드는 것입니다 그 모델은 사용자가에서 자신의 쿼리를 작성하고, 이들을 사용하여 객체의 트리를 구축하려는 각 조건.

개체 트리에서 쿼리를 만족시키는 SQL 문을 재귀 적으로 빌드 할 수 있어야합니다.

기본 객체는 AND, OR 객체뿐 아니라 EQUALS, LESSTHAN 등과 같은 모델 비교 대상 객체입니다. 이러한 객체에 대한 인터페이스를 사용하여 다른 방법으로 쉽게.

예제이다 :

public interface IQueryItem 
{ 
    public String GenerateSQL(); 
} 


public class AndQueryItem : IQueryItem 
{ 
    private IQueryItem _FirstItem; 
    private IQueryItem _SecondItem; 

    // Properties and the like 

    public String GenerateSQL() 
    { 
     StringBuilder builder = new StringBuilder(); 
     builder.Append(_FirstItem.GenerateSQL()); 
     builder.Append(" AND "); 
     builder.Append(_SecondItem.GenerateSQL()); 

     return builder.ToString(); 
    } 
} 

아주 쉽게 단위 테스트 규칙을 허용해야한다 이런 식으로 구현.

부정적인면에서이 솔루션은 여전히 ​​데이터베이스에 많은 작업을 수행하며 실제로 원하지 않는 것처럼 들립니다.

+0

이것은 확실히 실행 가능한 솔루션이지만 역동적 인 SQL (단위 테스트 용이성, 격리 테스트 규칙 등)을 스풀링하는 것과 관련된 함정을 피할 수있는 솔루션을 설계하기를 희망했습니다. –