如何创建可重用的表达式树?

时间:2015-10-22 09:06:51

标签: .net lambda expression-trees

我有这个表达式树:

    internal static IEnumerable<ElemType> WhereIn<ElemType>(this IEnumerable<ElemType> query, string filterFieldName, string[] values)
    {
        ParameterExpression lambdaParam = Expression.Parameter(typeof(ElemType), "p");
        MethodCallExpression paramToString = Expression.Call(Expression.PropertyOrField(lambdaParam, filterFieldName), typeof(object).GetMethod("ToString"));
        MethodInfo mi = typeof(Enumerable).GetMethods().Where(x => string.Equals(x.Name, "Contains", StringComparison.OrdinalIgnoreCase)).Single(x => x.GetParameters().Length == 2).MakeGenericMethod(typeof(string));
        Expression<Func<string[]>> array = () => values;
        MethodCallExpression contains = Expression.Call(mi, array.Body, paramToString);
        LambdaExpression lambdaExp = Expression.Lambda<Func<ElemType, bool>>(contains, lambdaParam);
        Func<ElemType, bool> lambda = (Func<ElemType, bool>)lambdaExp.Compile();

        return query.Where(lambda);
    }

现在,当使用像query.WhereIn("propName", new string[] {"aaa", "bbb"})之类的东西调用它时,我并不总是希望代码创建一个新的表达式树,因为这样做非常耗时。但.Compile()函数没有提供允许我使用编译的lambda的参数。 (或者,我只是不明白如何正确使用它。)

如何重写上面的表达式树以允许我缓存编译结果并为生成的编译lambda提供字符串数组?

2 个答案:

答案 0 :(得分:0)

您应该创建和管理自己的缓存 缓存的关键应该是属性名称。 关键概念是创建一个关闭函数,引用数组(咖喱函数)

这是您在运行时基本上需要创建的内容 获取数组并返回其他函数的函数,该函数匹配相同的参数并返回linq where function的函数。

#include

在运行时编译表达式树并使用属性名作为缓存键的整个解决方案。

let inputString = "PRICE                             9.00\nTAX                               3.54\nTOTAL                             12.54\n\nCASH                              12.54\n\nTHANK YOU FOR SHOPPING!\nReceipt Code: 1HI 12D0 00A 0024" //want to grab this 14 character code"
let searchString = "Receipt Code: "

if let firstFound = inputString.rangeOfString(searchString)?.first {
    let code = inputString.substringWithRange(firstFound.advancedBy(searchString.characters.count)..<firstFound.advancedBy(searchString.characters.count+17))
        .stringByReplacingOccurrencesOfString(" ", withString: "")   // "1HI12D000A0024"
}

答案 1 :(得分:0)

fast-member怎么样? 然后你的方法是:

private static Func<string, string[], Func<ElemType, bool>> CreatePredicate() 
{
    var accessor = TypeAccessor.Create(ElemType);
    return (fieldName, arr) => (elem => arr.Contains((string)accessor[elem, fieldName]);
}

你可以这样做:

var filterFactory = CreatePredicate()
query.Where(filterFactory("MyProperty", new[] { "aaa", "bbb" }));