【问题标题】:Iterate through property and creating an Expression<Func<>> with the result遍历属性并使用结果创建一个 Expression<Func<>>
【发布时间】:2018-08-22 10:36:09
【问题描述】:

我不习惯使用表达式函数,但我的问题是: 我将属性名称作为字符串获取,然后我需要将其转换为适当的表达式。

目前我正在做这样的事情:

if (string.Equals(propertyString, "customerNo", StringComparison.InvariantCultureIgnoreCase))
{
    return _repo.DoSomething(x=>x.CustomerNo);
}
if (string.Equals(propertyString, "customerName", StringComparison.InvariantCultureIgnoreCase))
{
    return _repo.DoSomething(x => x.CustomerName);
}

使用 repo 函数是这样的:

public IEnumerable<ICustomer> DoSomething(Expression<Func<IObjectWithProperties, object>> express)
{
    //Do stuff
}

我想做的是像这样使用反射:

var type = typeof(IObjectWithProperties);
PropertyInfo[] properties = type.GetProperties();

foreach (PropertyInfo property in properties)
{
    if(string.Equals(property.Name,propertyString,StringComparison.InvariantCultureIgnoreCase))
        return _repo.DoSomething(x => x.PROPERTY);
}

但我想不出一种从 propertyinfo 生成表达式 func 的方法

编辑:Mong Zhu 的回答,我可以使用该属性创建一个表达式。

我需要这个表达式的原因是我试图在 iqueryable 中动态设置 orderby。

public IEnumerable<Customer> List(Expression<Func<IObjectWithProperties, object>> sortColumn)
{
    using (var context = _contextFactory.CreateReadOnly())
    {
        return context.Customers.OrderBy(sortColumn).ToList();
    }
}

使用这样的答案:

public Customer Test(string sortColumn){

        var type = typeof(IObjectWithProperties);
        PropertyInfo[] properties = type.GetProperties();

        foreach (PropertyInfo property in properties)
        {
            if (string.Equals(property.Name, sortColumn, StringComparison.InvariantCultureIgnoreCase))
            {
                Expression<Func<IObjectWithProperties, object>> exp = u =>
                (
                    u.GetType().InvokeMember(property.Name, BindingFlags.GetProperty, null, u, null)
                );

                return _customerRepository.List(exp);
            }
        }
}

我收到一个错误:

System.InvalidOperationException:从范围“”引用的“IObjectWithProperties”类型的变量“u”,但未定义

编辑:

Customer 返回类型继承 IObjectWithProperties:

public class Customer: IObjectWithProperties
{
     //properties
}

【问题讨论】:

  • “我得到一个属性的名称作为字符串”你从哪里得到它?用户输入?你能影响来源吗?
  • 来自控制器,遗憾的是我无法控制这个
  • 可能 Customer 是一个 partial class,作为现有数据库模型的扩展?
  • 没错是的
  • 好的,我学到了很多关于表达式树的知识。从适用性和特别是可读性的角度来看,我建议您继续使用 if/else 方法。我想它可以解决,但工作量很大,我猜可读性会受到影响

标签: c# reflection lambda expression


【解决方案1】:

好的,在四处挖掘之后,我在this answer 找到了适用于 EF 的有效解决方案。

需要稍微修改一下

private static Expression<Func<T, object>> ToLambda<T>(string propertyName)
{
    var parameter = Expression.Parameter(typeof(T));
    var property = Expression.Property(parameter, propertyName);
    return Expression.Lambda<Func<T, object>>(property, parameter);
}

调用如下所示:

var type = typeof(IObjectWithProperties);
PropertyInfo[] properties = type.GetProperties();

foreach (PropertyInfo property in properties)
{
    if (string.Equals(property.Name, propertyString, StringComparison.InvariantCultureIgnoreCase))
    {
        var result = DoSomething(ToLambda<IObjectWithProperties>(property.Name));
    }
}

我将假设Customer 是一个部分类,它实现了接口IObjectWithProperties 和对现有数据库表的扩展。所以你的 orderby 方法应该是这样的:

public IEnumerable<Customer> DoSomething(Expression<Func<IObjectWithProperties, object>> sortColumn)
{
    using (var context = _contextFactory.CreateReadOnly())
    {
        return context.Customers.OrderBy(sortColumn).Cast<Customer>().ToList(); 
    }          
}

您需要在这里做的重要事情是调用Compile(),这将允许转换为sql语句并发送到服务器进行查询。 由于您使用接口作为Func 编译器的输入参数 似乎无法推断出你的部分类实现了这个接口。 因此,需要进一步显式调用Cast&lt;Customer&gt;() 来建立正确的返回类型。

我希望这是可以理解的,并且可以帮助您解决第二个问题

此解决方案还将 OrderBy 子句转换为 SQL。

免责声明:

不幸的是,它适用于string 属性,但到目前为止不适用于Int32。 我仍在尝试找出原因。

编辑:

同时我在this answer David Specht 中找到了另一个解决方案

这个扩展类真的可以用作复制粘贴,它适用于任何一种类型。这是您需要的重要代码:

public static class IQueryableExtensions
{
    public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
    {
        return CallOrderedQueryable(query, "OrderBy", propertyName, comparer);
    }

    public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName, IComparer<object> comparer = null)
    {
        return CallOrderedQueryable(query, "OrderByDescending", propertyName, comparer);
    }

    /// <summary>
    /// Builds the Queryable functions using a TSource property name.
    /// </summary>
    public static IOrderedQueryable<T> CallOrderedQueryable<T>(this IQueryable<T> query, string methodName, string propertyName,
            IComparer<object> comparer = null)
    {
        var param = Expression.Parameter(typeof(T), "x");

        var body = propertyName.Split('.').Aggregate<string, Expression>(param, Expression.PropertyOrField);

        return comparer != null
            ? (IOrderedQueryable<T>)query.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable),
                    methodName,
                    new[] { typeof(T), body.Type },
                    query.Expression,
                    Expression.Lambda(body, param),
                    Expression.Constant(comparer)
                )
            )
            : (IOrderedQueryable<T>)query.Provider.CreateQuery(
                Expression.Call(
                    typeof(Queryable),
                    methodName,
                    new[] { typeof(T), body.Type },
                    query.Expression,
                    Expression.Lambda(body, param)
                )
            );
    }
}

您的订购方法看起来就像这样:

public IEnumerable<Customer> DoSomething(string propertyName)
{
    using (var context = _contextFactory.CreateReadOnly())
    {                
        return context.Customers.OrderBy(propertyName).ToList();
    }
}

【讨论】:

  • 如果我使用一个类,这似乎可以工作,但我的 IObjectWithProperties 是一个接口
  • 忽略我的评论,错误在代码的其他地方
  • 让我稍微扩展一下我的问题,因为这会导致我使用表达式的问题
  • 此方法仍然将其转换为 ienumrable,如果您查看您的建议生成的 sql 与我只是执行 .OrderBy(x=>x.customerNo),我可以看到您的建议省略了来自 sql 的 ORDER BY,并在内存中执行排序
  • 非常感谢。我会试一试,如果它不起作用,我会坚持 if / else :)
猜你喜欢
  • 2012-05-29
  • 1970-01-01
  • 2017-03-22
  • 2019-01-11
  • 1970-01-01
  • 1970-01-01
  • 2021-07-29
  • 1970-01-01
  • 2020-10-03
相关资源
最近更新 更多