LINQ方法具有不同的参数

时间:2010-01-07 16:41:04

标签: c# .net linq linq-to-sql

我有一个内部应用程序的搜索页面的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上的SWITCH)是否更可取(读取最佳实践,更高效等等...... ) / em>)告诉我要搜索哪个参数,或者我应该简单地创建多个版本,la SearchForPersonByLastName& SearchForPersonBySSN

此外,是否有一个更为精细的解决方案,我认为常见,问题?

6 个答案:

答案 0 :(得分:2)

我是否正确理解只会使用其中一个参数进行搜索?如果是这样,那么绝对应该是单独的方法。每当您使用单词“and”或“or”描述方法(或类等)时,您可能有一个可以分解为多个方法的方法。所以听起来这种方法目前被描述为“此方法通过PersonFirstNameMiddleNameLastName或{{1}搜索SSN s }“。所以,写方法

BirthDate

显然,这些方法之间会有一些共同的逻辑,你可以将它们分解为辅助方法。

请澄清我是否误解了,我会相应地编辑我的答案。

编辑:

好的,所以你说你可能会通过多个参数进行搜索。我仍然强烈希望为每个参数提供单独的方法(更好地分离关注点,更易于维护,更容易测试等)。这是将它们联系在一起的一种方法:

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.

答案 1 :(得分: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 });

这将允许您传递任何参数组合以进行过滤,因此您不会被锁定以过滤一个参数。

答案 2 :(得分:1)

使用多种方法可以更加清晰。

如果我查看你的代码而你只使用一种方法,我就能弄清楚发生了什么,但是我必须看一下它看看你在做什么。也许一些评论有助于澄清事情等等......

但是,多种方法将完全显示您正在尝试做的事情。

正如杰森所说,一定要将公共代码分解为辅助方法。我不想在每种方法中看到相同的(或多或少)linq查询。

答案 3 :(得分: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();

所以调用者可以指定:

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

忽略搜索中的中间名。

请注意,您可以将这些条款合并为一个使用&amp;&amp;虽然这可能难以阅读。

答案 4 :(得分:1)

你拥有的单一方法很好。

我会一次构建LINQ one where子句。这样,当您实际运行LINQ时,它只处理所需的where子句。这应该比提出的其他解决方案更有效。 LINQ很棒,因为您可以根据需要使用if逻辑逐个创建LINQ表达式然后运行它。如果可以在构建LINQ表达式时确定逻辑,则不需要将所有if逻辑放在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();

答案 5 :(得分:1)

可能想要创建一个反映某个人的对象,然后为其添加一个过滤方法:

Person.AddFilter(fieldToLimit,operator,value)

这样,您可以向对象添加任意数量的过滤条件。

示例:

Person.AddFilter(FirstName,Contains,“Bob”); Person.AddFilter(LastName,StartsWith,“Z”);

另一种方法是简单地将您的条件添加到Linq to SQL IQueryable数据类型中,以便您可以简单地:

Person.Where(t =&gt; t.FirstName.Contains(“Bob”))。其中(t =&gt; t.LastName.StartsWith(“Z”));