遍历属性并使用结果创建Expression <func <>>

时间:2018-08-22 10:36:10

标签: c# reflection lambda expression

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

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

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);
}

使用回购功能是这样的:

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',但未定义

编辑:

客户返回类型继承了IObjectWithProperties:

public class Customer: IObjectWithProperties
{
     //properties
}

1 个答案:

答案 0 :(得分: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<Customer>()调用才能建立正确的返回类型。

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

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

免责声明:

不幸的是,它可以与string属性一起使用,但是到目前为止,不适用于Int32。 我仍在努力找出原因。

编辑:

与此同时,我在this answerDavid 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();
    }
}