参数化 Linq 表达式帮助

发布于 2024-10-26 08:12:35 字数 1369 浏览 2 评论 0原文

我想做一个具有如下签名的方法:

Expression<Func<TSource, bool>> CreatePropertyFilter<TSource>(Expression<Func<TSource, string>> selector, string value, TextMatchMode matchMode);

基本上,它需要一个属性选择器(例如:p = p.Name)、一个字符串值和一个可以是 StartsWith 的枚举值EndsWith包含精确;用于文本匹配选项。

如何以 LINQ2Entities 可以理解的方式实现该方法?我已经使用嵌套调用表达式实现了该方法,如下所示:

Expression<Func<string, bool>> comparerExpression;

switch (matchMode)
{
    case TextMatchMode.StartsWith:
       comparerExpression = p => p.StartsWith(value);
       break;
    case TextMatchMode.EndsWith:
       comparerExpression = p => p.EndsWith(value);
       break;
    case TextMatchMode.Contains:
       comparerExpression = p => p.Contains(value);
       break;
    default:
       comparerExpression = p => p.Equals(value);
       break;
}

var equalityComparerParameter = Expression.Parameter(typeof(IncomingMail), null);
var equalityComparerExpression = Expression.Invoke(comparerExpression, Expression.Invoke(selector, equalityComparerParameter));
var equalityComparerPredicate = Expression.Lambda<Func<IncomingMail, bool>>(equalityComparerExpression, equalityComparerParameter);

问题是 Linq2Entities 不支持调用表达式。

对此有什么建议吗?

谢谢!

I want to do a method with a signature like this:

Expression<Func<TSource, bool>> CreatePropertyFilter<TSource>(Expression<Func<TSource, string>> selector, string value, TextMatchMode matchMode);

Basically, it takes a property selector (ex: p = p.Name), a string value and a enum value that can be StartsWith, EndsWith, Contains, Exact; for text matching options.

How can I implement the method in a way that LINQ2Entities can understand? I already implemented the method using nested invocation expressions like this:

Expression<Func<string, bool>> comparerExpression;

switch (matchMode)
{
    case TextMatchMode.StartsWith:
       comparerExpression = p => p.StartsWith(value);
       break;
    case TextMatchMode.EndsWith:
       comparerExpression = p => p.EndsWith(value);
       break;
    case TextMatchMode.Contains:
       comparerExpression = p => p.Contains(value);
       break;
    default:
       comparerExpression = p => p.Equals(value);
       break;
}

var equalityComparerParameter = Expression.Parameter(typeof(IncomingMail), null);
var equalityComparerExpression = Expression.Invoke(comparerExpression, Expression.Invoke(selector, equalityComparerParameter));
var equalityComparerPredicate = Expression.Lambda<Func<IncomingMail, bool>>(equalityComparerExpression, equalityComparerParameter);

The problem is that Linq2Entities doesn't support Invocation expressions.

Any advice on this?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

尾戒 2024-11-02 08:12:35

本质上,给定一个选择器:

input => input.Member

您当前正在构造一个谓词表达式,例如:

input => selector(input).Method(value)

相反,使用其主体(a MemberExpression)“扩展”选择器表达式,以构造一些东西就像:

input => input.Member.Method(value) 

这看起来像:

private static Expression<Func<TSource, bool>> CreatePropertyFilter<TSource>
    (Expression<Func<TSource, string>> selector, 
     string value, 
     TextMatchMode matchMode)
{
    // Argument-checking here.    

    var body = selector.Body as MemberExpression;

    if (body == null)
        throw new ArgumentException("Not a MemberExpression.");    

    // string.StartsWith / EndsWith etc. depending on the matchMode.
    var method = typeof(string)
                 .GetMethod(GetMethodName(matchMode), new[] { typeof(string) });

    // input.Member.method(value)
    var compEx = Expression.Call(body, method, Expression.Constant(value));

    // We can reuse the parameter of the source.
    return Expression.Lambda<Func<TSource, bool>>(compEx, selector.Parameters);
}

翻译方法是:

// I really don't like this enum.
// Why not explicitly include Equals as a member?
private static string GetMethodName(TextMatchMode mode)
{
    switch (mode)
    {
        case TextMatchMode.StartsWith:
            return "StartsWith";

        case TextMatchMode.EndsWith:
                return "EndsWith";

        case TextMatchMode.Contains:
            return "Contains";

        default:
            return "Equals";
    }    
}

Essentially, given a selector:

input => input.Member

You are currently constructing a predicate expression like:

input => selector(input).Method(value)

Instead, 'expand' out the selector expression by using its body (a MemberExpression), to construct something like:

input => input.Member.Method(value) 

This would look like:

private static Expression<Func<TSource, bool>> CreatePropertyFilter<TSource>
    (Expression<Func<TSource, string>> selector, 
     string value, 
     TextMatchMode matchMode)
{
    // Argument-checking here.    

    var body = selector.Body as MemberExpression;

    if (body == null)
        throw new ArgumentException("Not a MemberExpression.");    

    // string.StartsWith / EndsWith etc. depending on the matchMode.
    var method = typeof(string)
                 .GetMethod(GetMethodName(matchMode), new[] { typeof(string) });

    // input.Member.method(value)
    var compEx = Expression.Call(body, method, Expression.Constant(value));

    // We can reuse the parameter of the source.
    return Expression.Lambda<Func<TSource, bool>>(compEx, selector.Parameters);
}

Where the translating method is:

// I really don't like this enum.
// Why not explicitly include Equals as a member?
private static string GetMethodName(TextMatchMode mode)
{
    switch (mode)
    {
        case TextMatchMode.StartsWith:
            return "StartsWith";

        case TextMatchMode.EndsWith:
                return "EndsWith";

        case TextMatchMode.Contains:
            return "Contains";

        default:
            return "Equals";
    }    
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文