开发者

How can I factor out clauses in a parameterized Expression?

Hey all. I'm trying to optimize a Linq to Entities call by statically caching and reusing a compiled query. The query checks the same thing for a variable number of filter arguments, and the only way to compile query arguments like that is to explicitly use a number of arguments (rather than some Contains()-type logic, which in SQL can't be parameterized).

This works great and gives me a major performance boost. The problem is that the code is UGLY. I repeat the same chunk of code a number of times for each of the possible parameters. I.e:

           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.Strategi开发者_运维问答es.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);

I thought I'd be able to refactor this by dynamically creating the filter expressions, but it's seeming impossible. I want to do something like this:

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

But of course that won't compile because filterExpression is an expression and can't be called into like that (and it can't just be a Func because Linq to Entities won't recognize it as a translatable method).

I also can't capture the parameters in closures outside the expression, because if I reuse the compiled expression, the values from the last call will be hard-coded and re-used. I.e., that's not a parameterized query.

Am I stuck writing out the whole thing for every term? I'd like to support a maximum of 14. Is it possible to factor out clauses that take parameters in this way?


Check out Predicate Builder. The docs are pretty good.

I've used it to support dozens of predicates and the resulting code is small when well-factored.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜