【问题标题】:Dynamically Building Linq to Entities Lambda Expression with Concatenated Properties使用串联属性动态构建 Linq 到实体 Lambda 表达式
【发布时间】:2013-11-30 05:44:54
【问题描述】:

我最近遇到了为 Kendo UI 网格控件实现服务器端过滤的需求。由于 linq to entity lambda 表达式中的比较运算符在编译时是未知的,因此有必要动态构建表达式。我以前从来没有需要这样做,但我做了足够多的挖掘来弄清楚如何从表达式树构建简单的 lambda 表达式。例如,我为一个简单的过滤器(例如people.Where(person => person.LastName == "Doe"))构建表达式,如下所示:

ParameterExpression filterParam = Expression.Parameter(typeof(Person), "person");
MemberExpression left = Expression.Property(filterParam, "LastName");
ConstantExpression right = Expression.Constant("Doe", typeof(string));
BinaryExpression predicate = Expression.Equal(left, right);
Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>(predicate, filterParam);

IQueryable<Person> filtered = people.Where(lambda);

好的,那么问题来了。我遇到了需要做与上面类似的事情,除了按全名而不是姓氏过滤。我的数据库中没有用于全名的字段——只是单独的名字和姓氏字段,所以我不能为此引用单个属性。我需要连接表达式中的两个属性,以便生成的 lambda 看起来像:

people.Where(person => (person.firstName + person.LastName) == "JohnDoe")

我还没有找到一个示例来展示如何从表达式树动态构建一个像这样的 lambda。我假设它一定是可能的——它看起来不像那种疯狂的场景,是吗?

编辑:我也有兴趣知道怎么做:

people.Where(person => (person.FirstName + " " + person.LastName) == "John Doe")

Edit2:解决这个问题的方法是对国王的回答进行非常简单的改变:

ParameterExpression filterParam = Expression.Parameter(typeof(Person), "person");
MemberExpression first = Expression.Property(filterParam, "FirstName");
MemberExpression last = Expression.Property(filterParam, "LastName");
Type strType = typeof(string);

MethodCallExpression concat = Expression.Call(typeof(string).GetMethod("Concat", new [] { strType, strType, strType }),
    first, Expression.Constant(" ", strType), last);

ConstantExpression right = Expression.Constant("John Doe", strType);
BinaryExpression predicate = Expression.Equals(concat, right);
Expression<Func<Person, bool>> lambda = Expression.Lambda<Func<Person, bool>>(predicate, filterParam);

IQueryable<Person> filtered = people.Where(lambda);

【问题讨论】:

  • 了解如何构建给定表达式的一个好方法是使用 LinqPad(免费)。只需将表达式创建为变量并将其转储 (Expression> e = p => ...; p.Dump()
  • 感谢您的提示。我得试试看。

标签: c# lambda linq-to-entities


【解决方案1】:

您可以尝试添加MethodCallExpression 以使用string.Concat 方法:

var firstName = Expression.Property(filterParam, "FirstName");
var lastName = Expression.Property(filterParam, "LastName");
var concat = Expression.Call(typeof(string).GetMethod("Concat", new [] { typeof(string), typeof(string) }),
                             firstName, lastName);
BinaryExpression predicate = Expression.Equal(concat, right);
//....

【讨论】:

  • 非常好,稍加修改就可以像魅力一样工作。我使用您那里的 GetMethod 重载得到System.Reflection.AmbiguousMatchException。将其更改为 GetMethod("Concat", new [] { typeof(string), typeof(string) }) 可以解决此问题。您能否编辑您的答案以使用包含参数类型数组的重载,以便我接受。谢谢!
【解决方案2】:

为了使这更容易和可重用,我确实通过使用PropertyTranslatorQueryInterceptor 在实体模型上定义了额外的属性。

使用此代码,您可以像这样在实体模型上定义 FullName 属性:

public class Person
{
    private static readonly CompiledExpressionMap<Employee, string> FullNameExpr =
        DefaultTranslationOf<Employee>.Property(e => e.FullName).Is(e => e.FirstName + " " + e.LastName);

    public string FirstName { get; set; }

    public string LastName { get; set; }

    [NotMapped]
    public string FullName
    {
        get
        {
            return FullNameExpr.Evaluate(this);
        }
    }
}

只需在如下查询中使用此 Fullname 属性:

IQueryable<Person> filtered = people
    .InterceptWith(new PropertyVisitor()).AsQueryable()
    .Where(p => p.FullName == "John Doe");

我不确定您是否可以在您的设计中实现此功能,但如果可能,请研究此选项。

您可以查看示例项目here

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多