如何在参数化表达式中分解出子句?

时间:2011-04-28 23:21:22

标签: c# linq entity-framework linq-to-entities expression-trees

嘿所有人。我正在尝试通过静态缓存和重用已编译的查询来优化Linq to Entities调用。对于可变数量的过滤器参数,查询检查相同的事情,并且编译这样的查询参数的唯一方法是显式地使用多个参数(而不是某些包含() - 类型逻辑,这在SQL中不能参数化)。

这很有效,并且给我带来了重大的性能提升。问题是代码是UGLY。我为每个可能的参数多次重复相同的代码块。即:

           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 to Entities不会将它识别为可翻译的方法)。

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

我是不是每次都写出了整篇文章?我想支持最多14个。是否可以分解以这种方式获取参数的子句?

1 个答案:

答案 0 :(得分:1)

结帐Predicate Builder。文档非常好。

我已经用它来支持几十个谓词,并且当得到很好的因素时,结果代码很小。