2010-01-07 60 views
1

我有一個內部應用程序的搜索頁面的LINQ方法。該方法看起來如下不同參數的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; 
    } 

現在,你可以毫無疑問的看到,我通過LastName創建此,因爲它只搜索時做了一個輕微的監督。當我對這件事情發生時,我正在擴大這個範圍,以致我可能無法正確處理這個問題。

所以,最後到我的問題;它是更理想(讀最好的做法,更有效,等等...)有這樣一個方法的機構(我正在考慮對非空PARAM交換機)來告訴搜索哪些參數與或應該我只是做出多個版本,一個la SearchForPersonByLastName & SearchForPersonBySSN

此外,是否還有一個更加靈活的解決方案,我會認爲是常見的問題?

回答

2

我是否正確理解只有一個參數將用於搜索?如果是這樣,那麼絕對應該是單獨的方法。任何時候,當您使用「and」或「or」來描述方法(或類等)時,您可能會有一種方法可以分解爲多種方法。因此,聽起來這種方法目前被描述爲「此方法通過FirstNameMiddleNameLastNameSSNBirthDate搜索Person。」所以,寫入方法

SearchByFirstName 
SearchByMiddleName 
SearchByLastName 
SearchBySSN 
SearchByBirthDate 

顯然這些方法之間會有一些共同的邏輯,你可以把它們分解成一個輔助方法。

請澄清,如果我誤解了,我會相應地編輯我的答案。

編輯:

好吧,所以你說你可能會搜索多個參數。我仍然非常喜歡每個參數都有獨立方法的想法(更好地分離關注點,更容易維護,更容易測試等)。下面是配合他們都聚集到一起的方式:

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. 

或者:

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()'? – 2010-01-07 16:57:39

+0

請參閱我的編輯。 – jason 2010-01-07 18:37:37

2

如果我理解你的問題吧,你想其他參數添加到您的查詢的where條款。我可能會建議:

var persons = (from person in context.tblPersons 
         where (!string.IsNullOrEmpty(LastName) && (person.LastName == LastName || person.LastName.StartsWith(LastName))) && 
         (!string.IsNullOrEmpty(SSN) && (person.SSN == SSN)) // && etc as needed 
         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 }); 

這將允許你傳遞的參數來篩選的任意組合這樣你就不會被鎖定在一個參數過濾。

1

使用多種方法的意圖會更加清晰。

如果我看看你的代碼,你只使用一個方法,我就能弄清楚發生了什麼事情,但我不得不看它一會兒,看看到底你在做什麼這樣做。也許有些意見會幫助澄清事情等等......

但是,多種方法將顯示我正確地嘗試做什麼。正如Jason所說的,確保將通用代碼分解爲輔助方法。我討厭在每種方法中看到相同的(或多或少)linq查詢。

1

您可以添加多個where子句使呼叫者可以指定希望搜索的名稱字段,或者爲null,以匹配任何東西:

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

因此,主叫方可能會specifiy:

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

要忽略搜索中的中間名。

請注意,您可以將這些子句合併爲一個使用& &,雖然這可能難以閱讀。

1

你擁有的單一方法很好。

我會一次構建LINQ one where子句。這樣,當你實際運行LINQ時,它只處理需要的where子句。這應該比提出的其他解決方案更有效率。 LINQ非常棒,因爲您可以根據需要逐個創建LINQ表達式,然後運行它。如果您可以在構建LINQ表達式時確定邏輯,則不需要將所有如果邏輯放入LINQ表達式中。

我也簡化到只有StartsWith。

另一件事,似乎filteredPersonsList過濾是多餘的,因爲你已經過濾,我相信你可以擺脫這些行。

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(姓,包含, 「鮑勃」); Person.AddFilter(LastName,StartsWith,「Z」);

另一種方法就是你的標準你的LINQ添加到SQL數據類型的IQueryable,使您可以簡單:

Person.Where(T => t.FirstName.Contains( 「鮑勃」)),其中( t => t.LastName.StartsWith(「Z」));