2010-01-07 4 views
1

사내 응용 프로그램의 검색 페이지에 대해 LINQ 메서드가 있습니다. LastName하여에만 검색으로 이것을 만들 때 틀림없이 볼 수있는 방법은 지금다양한 매개 변수가있는 LINQ 메서드

public static DataTable SearchForPerson(String FirstName, String MiddleName, String LastName, String SSN, DateTime? BirthDate) 
    { 
     var persons = (from person in context.tblPersons 
         where person.LastName == LastName || person.LastName.StartsWith(LastName) 
         join addresse in context.tblAddresses on person.PersonID equals addresse.PersonID 
         orderby person.LastName 
         select new { person.PersonID, person.LastName, person.FirstName, person.SSN, addresse.AddressLine1 }); 

     var filteredPersonsList = persons.Where(p => p.LastName == LastName).ToList(); 
     if (filteredPersonsList.Count == 0) 
      filteredPersonsList = persons.Where(p => p.LastName.StartsWith(LastName)).ToList(); 

     var dataTable = filteredPersonsList.CopyLinqToDataTable(); 



     return dataTable; 
    } 

아래와 같이 보인다, 나는 약간의 감독을했다. 나는 이것이 적절하게 돌아 가지 않을지도 모른다는 것을 알게되었을 때 이것을 확장하는 과정에있었습니다.

결국, 내 질문에; 더 바람직합니까 (읽기 - 우수 사례,보다 효율적으로 ...) 메커니즘을 사용하여 이와 같은 단일 메서드를 사용하는 것입니다 (비어 있지 않은 매개 변수에 대한 스위치를 생각하고 있습니다.) 검색 할 매개 변수를 지정하십시오. 여러 버전을 만들거나해야합니까? SearchForPersonByLastName & SearchForPersonBySSN?

또한 이보다 더 매력적인 해결책이 있습니까? 저는 공통점이 있다고 생각합니까?

답변

2

매개 변수 중 하나만 검색에 사용된다는 것을 올바르게 이해하고 있습니까? 그렇다면 절대적으로 이들은 분리 된 방법이어야합니다. "and"또는 "or"이라는 단어를 사용하여 메서드 (또는 클래스 등)를 설명 할 때는 언제나 여러 메서드로 분리 할 수있는 메서드가있을 수 있습니다. 따라서이 방법은 현재 "이 방법은 FirstName 또는 MiddleName 또는 LastName 또는 SSN 또는 BirthDate에 의해 Person을 검색합니다."라고 설명되어 있습니다. 그래서, 쓰기 방법

분명히 당신이 도우미 방법으로 요소 수있는 몇 가지 일반적인 논리가있을 것입니다.

내가 오해 한 경우 명확히 말하면 그에 맞게 답변을 수정하겠습니다.

편집 :

좋아, 당신은 당신이 여러 매개 변수에 의해 검색 될 수 있다고 말한다. 나는 여전히 각 매개 변수에 대해 분리 된 방법의 아이디어를 선호한다. (문제의 분리, 유지 보수의 용이성, 테스트의 용이성 등).

DataTable Search(
    string firstName, 
    string middleName, 
    string lastName, 
    string ssn, 
    DateTime? birthdate 
) { 
    IQueryable<Person> query = context.tblPersons; 
    if(SearchParameterIsValid(firstName)) { 
     query = SearchByFirstName(query, firstName); 
    } 
    if(SearchParameterIsValid(middleName)) { 
     query = SearchByMiddleName(query, middleName); 
    } 
    if(SearchParameterIsValid(lastName)) { 
     query = SearchByLastName(query, lastName); 
    } 
    if(SearchParameterIsValid(ssn)) { 
     query = SearchBySSN(query, ssn); 
    } 
    if(birthDate != null) { 
     query = SearchByBirthDate(query, birthDate); 
    } 

    // fill up and return DataTable from query 
} 

bool SearchParameterIsValid(string s) { 
    return !String.IsNullOrEmpty(s); 
} 

IQueryable<Person> SearchByFirstName(
    IQueryable<Person> source 
    string firstName 
) { 
    return from p in source 
      where p.FirstName == firstName || p.FirstName.StartsWith(firstName) 
      select p; 
} 

// etc. 

또는 : 당신이 당신의 쿼리의 where 절에 다른 매개 변수를 추가하려는

내가 질문 권리를 이해한다면
DataTable Search(
    string firstName, 
    string middleName, 
    string lastName, 
    string ssn, 
    DateTime? birthdate 
) { 
    Predicate<Person> predicate = p => true; 
    if(SearchParameterIsValid(firstName)) { 
     predicate = PredicateAnd(predicate, FirstNamePredicate(firstName)); 
    } 
    if(SearchParameterIsValid(middleName)) { 
     predicate = PredicateAnd(predicate, MiddleNamePredicate(middleName)); 
    } 
    // etc. 
} 

Predicate<T> PredicateAnd<T>(Predicate<T> first, Predicate<T> second) { 
    return t => first(t) && second(t); 
} 

Predicate<Person> FirstNamePredicate(string firstName) { 
    return p => p.FirstName == firstName || p.FirstName.StartsWith(firstName); 
} 

// etc. 

DataTable SearchByPredicate(
    IQueryable<Person> source, 
    Predicate<Person> predicate 
) { 
    var query = source.Where(predicate) 
         .Join(
          context.tblAddresses, 
           p => p.PersonID, 
           a => a.PersonID, 
           (p, a) => new { 
            p.PersonID, 
            p.LastName, 
            p.FirstName, 
            p.SSN, 
            a.AddressLine1 
           } 
         ); 

    return query.CopyLinqToDataTable(); 
} 
+0

아니요 대부분 정확합니다. 내 유일한 관심사는 여러 매개 변수로 검색하고 더 많은 메소드를 사용하려는 경우의 경우입니다. 'SearchByFirstAndLastName()'?? –

+0

제 편집을 참조하십시오. – jason

2

여기에 함께 그들 모두를 묶어 하나의 방법이다. 추천 : 아마도

이렇게하면 매개 변수의 조합을 전달하여 한 매개 변수에 걸리지 않도록 할 수 있습니다.

1

여러 가지 방법으로 의도가 훨씬 명확 해집니다.

코드를보고 단 하나의 방법을 사용하면 어떤 일이 발생했는지 파악할 수 있지만, 잠시 동안 당신이 무엇인지 알아야 할 것입니다. 하기. 아마도 일부 의견은 사물 등을 명확하게하는 데 도움이 될 것입니다 ...

그러나 여러 방법을 통해 내가하는 일을 정확히 알 수 있습니다.

제이슨이 말했듯이 공통 코드를 도우미 메서드로 분해해야합니다. 나는 각 메서드에서 동일한 (다소간) linq 쿼리를 보는 것을 싫어한다.

var filteredPersonsList = persons 
    .Where(p => FirstName != null && p.FirstName == FirstName) 
    .Where(p => MiddleName != null && p.MiddleName == MiddleName) 
    .Where(p => LastName != null && p.LastName == LastName).ToList(); 

그래서 발신자가에서 지정할 수 :

1

조항은 발신자가에 검색하고자하는 이름 필드를 지정할 수 있도록 경우 여러 추가 할 수 있습니다, 또는 null 아무것도 일치하도록하려면

var matches = SearchForPerson("firstName", null, "lastName", "SSN", dob); 

검색에서 중간 이름을 무시하십시오.

& &을 사용하면이 절을 하나로 결합 할 수 있지만 읽기가 어려울 수 있습니다.

1

단일 방법으로 문제가 없습니다.

한 번에 한 절씩 LINQ를 빌드합니다. 이 방법은 실제로 LINQ를 실행할 때 필요한 where 절만 처리합니다. 이것은 제안 된 다른 솔루션보다 더 효율적이어야합니다. LINQ는 필요에 따라 로직을 사용하여 조각으로 LINQ 표현식을 생성 한 다음 실행할 수 있으므로 훌륭합니다. LINQ 표현식을 작성하는 동안 논리를 판별 할 수있는 경우 모든 if 논리를 LINQ 표현식에 넣을 필요는 없습니다.

또한 StartsWith 만 단순화했습니다.

다른 하나는 필터링 된 필자 목록 필터가 이미 필터링되어있어 중복되는 것으로 보이며 사용자가 해당 줄을 제거 할 수 있다고 생각합니다.

var persons = from person in context.tblPersons 
    select person; 
if (!string.IsNullOrEmpty(FirstName)) 
    persons = from person in persons 
     where person.FirstName.StartsWith(FirstName) 
     select person; 
if (!string.IsNullOrEmpty(MiddleName)) 
    persons = from person in persons 
     where person.MiddleName.StartsWith(MiddleName) 
     select person; 
if (!string.IsNullOrEmpty(LastName)) 
    persons = from person in persons 
     where person.LastName.StartsWith(LastName) 
     select person; 
if (!string.IsNullOrEmpty(SSN)) 
    persons = from person in persons 
     where person.SSN = SSN 
     select person; 
if (BirthDate.HasValue) 
    persons = from person in persons 
     where person.BirthDate == BirthDate.Value 
     select person; 
return (from person in persons 
    join address in context.tblAddresses 
    on person.PersonID equals address.PersonID 
    orderby person.LastName   
    select new { person.PersonID, person.LastName, 
     person.FirstName, person.SSN, address.AddressLine1 }) 
    .ToList() 
    .CopyLinqToDataTable(); 
1

이 사람을 반영하기 위해 객체를 생성 할 수 있습니다, 다음에 필터 방법을 추가

Person.AddFilter (fieldToLimit, 연산자, 값)

당신이 어떤을 추가 할 수 있습니다이 방법 객체에 대한 필터 기준 수입니다.

예 :

Person.AddFilter (firstName을 포함합니다 "밥"); Person.AddFilter (성, StartsWith, "Z");

또 다른 방법은 단순히 SQL 된 IQueryable 데이터 형식에 Linq에에 기준을 추가하는 것입니다 단순히 당신이 할 수 있도록 :.

Person.Where (t => t.FirstName.Contains ("밥")) 경우 (t => t.LastName.StartsWith ("Z"));

관련 문제