2011-04-28 31 views
1

嘿,所有。我试图通过静态缓存和重新使用已编译的查询来优化Linq to Entities调用。查询为可变数目的过滤器参数检查同样的事情,编译查询参数的唯一方法就是显式使用一些参数(而不是一些Contains() - 类型逻辑,它们在SQL中不能参数)。如何分解参数化表达式中的子句?

这很好,给了我一个重大的性能提升。问题是代码是丑陋的。对于每个可能的参数,我重复相同的代码块数次。即:

  Expression<Func<Entities, string, string, string, string, IQueryable<Instrument>>> query = 
(context, searchTerm0, searchTerm1, searchTerm2, searchTerm3) => 
       context.Instruments 
        .Where(
         (searchTerm0 == null || 
          instr.FullName.IndexOf(searchTerm0) > -1 || 
          instr.ShortName.IndexOf(searchTerm0) > -1 || 
          instr.Strategies.OrderBy(st => st.Level).Select(st => st.Name).Take(2).Any(strat => strat.IndexOf(searchTerm0) > -1)) 
         && 
         (searchTerm1 == null || 
          instr.FullName.IndexOf(searchTerm1) > -1 || 
          instr.ShortName.IndexOf(searchTerm1) > -1 || 
          instr.Strategies.OrderBy(st => st.Level).Select(st => st.Name).Take(2).Any(strat => strat.IndexOf(searchTerm1) > -1)) 
         && 
         (searchTerm2 == null || 
          instr.FullName.IndexOf(searchTerm2) > -1 || 
          instr.ShortName.IndexOf(searchTerm2) > -1 || 
          instr.Strategies.OrderBy(st => st.Level).Select(st => st.Name).Take(2).Any(strat => strat.IndexOf(searchTerm2) > -1)) 
         && 
         (searchTerm3 == null || 
          instr.FullName.IndexOf(searchTerm3) > -1 || 
          instr.ShortName.IndexOf(searchTerm3) > -1 || 
          instr.Strategies.OrderBy(st => st.Level).Select(st => st.Name).Take(2).Any(strat => strat.IndexOf(searchTerm3) > -1)) 
       .Take(50); 

我想我可以通过动态创建过滤器表达式来重构这个,但这似乎是不可能的。我想要做这样的事情:

var filterExpression = (instr, searchTerm) => 
     searchTerm == null || 
     instr.FullName.IndexOf(searchTerm) > -1 || 
     instr.ShortName.IndexOf(searchTerm) > -1 || 
     instr.Strategies.OrderBy(st => st.Level).Select(st => st.Name).Take(2).Any(strat => strat.IndexOf(searchTerm) > -1); 

    Expression<Func<Entities, string, string, string, string, IQueryable<Instrument>>> query = (context, searchTerm0, searchTerm1, searchTerm2, searchTerm3) => 
     context.Instruments 
      .Where(i => filterExpression(i, searchTerm0)) 
      .Where(i => filterExpression(i, searchTerm1)) 
      .Where(i => filterExpression(i, searchTerm2)) 
      .Where(i => filterExpression(i, searchTerm3)) 
     .Take(50); 

当然,这不能编译,因为filterExpression是一个表达式,不能叫成这样(和它不能只是一个Func键,因为LINQ到的,但实体不会将其识别为可翻译的方法)。

我也无法捕获表达式外的闭包中的参数,因为如果我重新使用编译的表达式,最后一次调用的值将被硬编码并重新使用。即,这不是一个参数化查询。

我为每个学期写出了全部内容吗?我想最多支持14个。是否可以将这些带参数的子句分解出来?

回答

1

结账Predicate Builder。文档非常好。

我已经用它来支持数十个谓词,并且在考虑良好时代码很小。

+0

不确定Predicate Builder如何帮助我。我想要做的是创建一个表达式并编译一次,然后用不同的参数重新使用它。除非我错过了某些东西,否则Predicate Builder示例使用闭包中捕获的过滤器值,即它们不是表达式的参数,将在随后使用编译后的查询时重新使用。我明白这是正确的吗? – dks1983 2011-04-29 14:45:44

+0

啊......我明白你在找什么。你是正确的,Predicate Builder不会为你做到这一点。但是,我不认为你可以在LINQ to EF中执行编译的方法。实质上,所有谓词都转换为T-SQL。编译后的查询无法由LINQ转换为EF提供程序,并且最终你最终会使用Predicate Builder。 – 2011-04-29 18:28:58

+0

是的,我在顶端的工作方式 - 我可以编译竞争表达式,并用不同的参数成功重用它 - 但最终我不得不写出所有该死的东西来支持多达十几个参数。 – dks1983 2011-04-29 22:16:04