从字符串属性名称创建通用表达式

Posted

技术标签:

【中文标题】从字符串属性名称创建通用表达式【英文标题】:Create Generic Expression from string property name 【发布时间】:2013-08-21 16:13:25 【问题描述】:

我有一个名为 sortColumn 的变量,它包含我想要对查询结果进行排序的列的文本。我还有一个通用存储库,它将包含我要排序的字段的表达式作为参数。我似乎无法从字符串属性名称获取表达式。

所以我拥有的通用存储库包含以下方法

public IEnumerable<TEntity> Get<TOrderBy>(Expression<Func<TEntity, bool>> criteria,
                                          Expression<Func<TEntity, TOrderBy>> orderBy, int pageIndex,
                                          int pageSize,
                                          bool isAssendingOrder = true,
                                          EnumDeletePolicy deletePolicy = EnumDeletePolicy.ExcludeDeleted)

注意这个Get的第二个参数是Expression-Func-TEntity, TOrderBy。正如我提到的,我有一个名为 sortColumn 的变量,它包含我的 TEntity 对象上的属性的字符串,我需要将此字符串转换为可以传递给 Get 方法的表达式。

这是我现在拥有的。

        var parameter = Expression.Parameter(typeof(IContract));
        var memberExpression = Expression.Property(parameter, data.SortColumn);
        var lambdaExpression = Expression.Lambda(memberExpression, parameter);

它创建了一个 LambdaExpression 类型的对象。此 LambdaExpression 的实际类型是 Expression-Func-IContract、字符串(或属性的任何类型 sortColumn)。如果我调用 Get 方法并传入此 LambdaExpression 并将其显式转换为 Expression 类型,那么它将正常工作。问题是我不知道 Expression 类型是什么,它可能是字符串、int、int? 等。这完全取决于 sortColumn 属性中特定的属性类型。

你能帮我最后一次跳转到正确的表达式类型吗?

根据 Marc 的建议进行编辑: 我几乎有这个工作,实际上是专门基于它正在工作的问题,但我还有 1 个问题。

作为我查询的实体类型的 IContract 实际上继承自 IRelationship。如果我从 IContract 接口指定一个字段,则上面的代码有效。如果我从 IRelationship 接口指定一个字段,则以下行将失败。

        var memberExpression = Expression.Property(parameter, data.SortColumn);

如果我尝试以下类似的操作,以便从 IRelationship 中获取 MemberExpression,但基于 IContract 构建 Lambda,我会从存储库中收到错误消息。

        var parameter = Expression.Parameter(typeof(IRelationship));
        var memberExpression = Expression.Property(parameter, data.SortColumn);
        var orderBy = Expression.Lambda(memberExpression, Expression.Parameter(typeof(IContract)));

我得到的错误是“参数''未绑定在指定的 LINQ to Entities 查询表达式中。”

让它工作的最后一个表达式是这个

        var parameter = Expression.Parameter(typeof(IContract));
        var memberExpression = Expression.Property(parameter, typeof(IRelationship), data.SortColumn);
        var orderBy = Expression.Lambda(memberExpression, parameter);

所以我需要在 memberExpression 行中指定中间参数,也就是说在继承的关系接口中查找属性

【问题讨论】:

你想对表达式做什么?有办法使用dynamic 让它翻转到最合适的泛型重载,基本上避免MakeGenericMethod。有什么用?例如:IQueryable&lt;SomeType&gt; filtered = Queryable.Where(source, (dynamic)expression); 【参考方案1】:

你需要使用正确的泛型重载——这意味着你必须使用MakeGenericMethod;但是,您也可以使用dynamic 来避免在此处使用MakeGenericMethod,例如(在这种情况下通过Where,但重要的是它是如何工作的):

IQueryable<Foo> source = new[]  new Foo  Bar = 123  .AsQueryable();
Expression<Func<Foo,bool>> typed =  x=>x.Bar == 123;

LambdaExpression untyped = typed;
IQueryable<Foo> filtered = Queryable.Where(source, (dynamic)untyped);

注意:您不能在此处使用 extension 方法 - 因此您需要使用 Queryable.*

对于使用您的代码的OrderBy 示例:

var parameter = Expression.Parameter(typeof(Foo));
var memberExpression = Expression.Property(parameter, "Bar");
var lambdaExpression = Expression.Lambda(memberExpression, parameter);
LambdaExpression untyped = lambdaExpression;

IQueryable<Foo> sorted = Queryable.OrderBy(source, (dynamic)untyped);

var all = sorted.ToArray();

重新编辑:

var parameter = Expression.Parameter(typeof(IRelationship));
var memberExpression = Expression.Property(
    Expression.Convert(parameter, typeof(IContract)), data.SortColumn);
var orderBy = Expression.Lambda(memberExpression, parameter);

【讨论】:

但是我需要调用的方法需要一个 Expression-Func 而不是一个动态的,所以我不能传入一个动态的。 @PaulCavacas Queryable.WhereQueryable.OrderBy also 采用 Expression-Func,而不是动态;这不是问题。关键是这里使用dynamic 使它工作。魔术。 我几乎让它工作了。剩下的一个问题。详情请咨询。 我没有为 IContract 类型定义实例属性“RelationshipNumber”。 RelationshipNumber 在 IRelationship 界面上 @Paul then... 你不需要提及IContract 完全 - 只需Expression.Property(parameter, data.SortColumn) - 在最后一行传递parameter

以上是关于从字符串属性名称创建通用表达式的主要内容,如果未能解决你的问题,请参考以下文章

如何从字符串为深层属性创建表达式树/lambda

从字符串中提取 HTML 标记名称

使用正则表达式从字符串中删除属性值不是特定值的所有 xml 节点

手机号码的通用正则表达式?

创建属性值数组,其中属性名称以特定字符串结尾

正则表达式从字符串中提取用户名/名称