2010-01-07 41 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”));