【问题标题】:Modify linq query at runtime在运行时修改 linq 查询
【发布时间】:2013-03-18 14:05:20
【问题描述】:

问题陈述

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

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 可组合性的案例吗?我很困惑!

【问题讨论】:

  • 标准。标准的单数。
  • 那将是“标准”。
  • 啊哈,好的。没有意识到。将适用于我自己语言的规则过度扩展到英语;)
  • 我认为,您正在寻找在运行时生成 lambda。我必须说这需要更多的工作、更多的测试等。请参阅这个相关问题:stackoverflow.com/questions/2297502/…
  • 那么这些cmets值得一些东西。 (:

标签: c# linq entity-framework extension-methods


【解决方案1】:

与其让事情过于复杂,不如只使用 if 语句?

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;
}

更新

如果您真的需要更复杂的东西,请尝试查看LinqKit。它允许您执行以下操作。

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;

我相信这满足您的要求

我真的很想能够封装不同的行为 (包含、StartsWith、EndsWith)以某种方式。

【讨论】:

  • 哇,我只是想写同样的......是的,我同意 - 保持简单。
  • @Doc:+1 更简单
  • 现实生活中的查询非常庞大,并且与其他几个查询结合在一起。就像我的问题状态一样,这不是我正在寻找的解决方案。
  • 您的评论说您不想为每个案例构建一个新查询。这不是这样做的,它是根据您的条件构建查询。您不需要全新的查询,您只需添加基于 if 语句的 where 过滤器。比玩 lambda 简单得多。
  • 唯一不同的是 StartsWith/Contains/EndsWith,并且在很多不同的位置都如此。我宁愿以某种方式封装这种行为。这个解决方案对于现实生活场景是不可行的。在每种情况下都可以搜索多个列等等。
【解决方案2】:

您可以在枚举之前动态更改查询。

var query = container.people.AsQueryable();

if (contains)
{
    query = query.Where(p => p.Name.Contains(filter));
}
else
{
    query = query.Where(p => p.Name.StartsWith(filter));
}

【讨论】:

    【解决方案3】:

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

    现实生活中的查询非常庞大,并且与其他几个查询结合在一起。就像我的问题状态一样,这不是我正在寻找的解决方案

    您的查询量很大:您不能只定义处理所有内容并使用特定查询参数调用它的存储过程(可能是几个由 main 调用的存储过程,例如一个按名称搜索的 em,另一个 - by年龄,有不同的排序顺序等以保持代码清晰)?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-30
      • 1970-01-01
      • 1970-01-01
      • 2010-10-02
      相关资源
      最近更新 更多