3

问题陈述

假设我有一个搜索人名的查询:

var result = (from person in container.people select person)
             .Where(p => p.Name.Contains(some_criterion)

这将被转换为包含以下 like 子句的 SQL 查询:

WHERE NAME LIKE '%some_criterion%'

这有一些性能影响,因为数据库无法有效地使用 name 列上的索引(如果我没记错的话,索引扫描与索引搜索)。

为了解决这个问题,我可以决定只使用 StartsWith() 来代替,使用 like 子句生成查询,例如:

WHERE NAME LIKE 'some_criterion%'

这使 SQL Server 能够使用索引查找并以牺牲某些功能为代价来提供性能。

我希望能够为用户提供一个选择:默认使用 StartsWith 的行为,但如果用户想要使用 Contains() 进行搜索的“增加灵活性”,则应该使用。

我试过什么

我认为这是微不足道的,并继续在字符串上实现了一个扩展方法。但当然,LINQ 不接受这一点并抛出异常。

现在,我当然可以继续使用 if 或 switch 语句并为每种情况创建一个查询,但我更愿意“在更高级别”或更一般地解决这个问题。简而言之:由于实际应用程序的复杂性,使用 if 语句来区分用例是不可行的。这会导致大量的重复和混乱。我真的很希望能够以某种方式封装不同的行为(包含、StartsWith、EndsWith)。

问题

我应该在哪里寻找或寻找什么?这是 IQueryables 可组合性的一个案例吗?我很纳闷!

4

3 回答 3

8

Rather than overcomplicate things, how about just using an if statement?

var query = from person in container.people 
            select person;

if (userWantsStartsWith)
{
    query = from p in query
            where p.Name.Contains(some_criterion)
            select p;
}
else
{
    query = from p in query
            where p.Name.StartsWith(some_criterion)
            select p;
}

Update

If you really need something more complex try looking at LinqKit. It allows you to do the following.

var stringFunction = Lambda.Expression((string s1, string s2) => s1.Contains(s2));

if (userWantsStartsWith)
{
    stringFunction = Lambda.Expression((string s1, string s2) => s1.StartsWith(s2));
}

var query = from p in container.people.AsExpandable()
            where stringFunction.Invoke(p.Name, some_criterion)
            select p;

I believe this fulfils your requirement of

I'd really like to be able to encapsulate the varying behavior (Contains, StartsWith, EndsWith) somehow.

于 2013-03-18T14:10:06.177 回答
2

You can dynamically alter the query before enumerating it.

var query = container.people.AsQueryable();

if (contains)
{
    query = query.Where(p => p.Name.Contains(filter));
}
else
{
    query = query.Where(p => p.Name.StartsWith(filter));
}
于 2013-03-18T14:12:29.957 回答
0

Try dis:

var result = (from person in container.people select person)
                .Where(p => some_bool_variable ? p.Name.Contains(some_criterium) : p.Name.StartsWith(some_criterium));

The real life queries are quite huge and unioned with several others. This, like my problem states, isn't te solution I'm looking for

Sinse your queries are huge: can't you just define stored procedure that handles everything and call it with specific to query parameters (probably several stored procedures that are called by main, e.g. one of em searches by Name, another - by Age, have different sort order and so on to keep code clear)?

于 2013-03-18T14:12:52.980 回答