C#谈到魔术串入lambda表达式 [英] C# Turning magic string into lambda expression

查看:93
本文介绍了C#谈到魔术串入lambda表达式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一组扩展方法,允许在LINQ 的OrderBy()方法使用魔法字符串。我所知道的第一个问题将是为什么,但它是一个通用存储库的一部分,是有灵活性,以便字符串可以从UI发送,直接使用。

I have a set of extension methods that allow for using magic strings in the LINQ OrderBy() methods. I know the first question will be why, but it's part of a generic repository and is there for flexibility so that strings can be sent from the UI and used directly.

我有工作,如果你在一个神奇的字符串,表示你要查询的主要实体的财产通过,但我无法使它更加通用的,因此它可以处理多级魔渊字符串。例如:

I have it working if you pass in a magic string that represents a property on the main entity you are querying, but I'm having trouble making it more generic so it can handle multiple levels deep magic string. For example:

IQueryable<Contact> contacts = GetContacts();

contacts.OrderByProperty("Name"); // works great

// can't figure out how to handle this
contacts.OrderByProperty("ContactType.Name");

下面是我到目前为止的代码:

Here is the code that I have so far:

public static class LinqHelpers
{
    private static readonly MethodInfo OrderByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderBy" && method.GetParameters().Length == 2);
    private static readonly MethodInfo OrderByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "OrderByDescending" && method.GetParameters().Length == 2);
    private static readonly MethodInfo ThenByMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "ThenBy" && method.GetParameters().Length == 2);
    private static readonly MethodInfo ThenByDescendingMethod = typeof(Queryable).GetMethods().Single(method => method.Name == "ThenByDescending" && method.GetParameters().Length == 2);

    public static IOrderedQueryable<TSource> ApplyOrdering<TSource>(IQueryable<TSource> source, string propertyName, MethodInfo orderingMethod)
    {
        var parameter = Expression.Parameter(typeof(TSource), "x");
        var orderByProperty = Expression.Property(parameter, propertyName);

        var lambda = Expression.Lambda(orderByProperty, new[] { parameter });

        var genericMethod = orderingMethod.MakeGenericMethod(new[] { typeof(TSource), orderByProperty.Type });

        return (IOrderedQueryable<TSource>)genericMethod.Invoke(null, new object[] { source, lambda });
    }

    public static IOrderedQueryable<TSource> OrderByProperty<TSource>(this IQueryable<TSource> source, string propertyName)
    {
        return ApplyOrdering(source, propertyName, OrderByMethod);
    }

    public static IOrderedQueryable<TSource> OrderByDescendingProperty<TSource>(this IQueryable<TSource> source, string propertyName)
    {
        return ApplyOrdering(source, propertyName, OrderByDescendingMethod);
    }

    public static IOrderedQueryable<TSource> ThenByProperty<TSource>(this IOrderedQueryable<TSource> source, string propertyName)
    {
        return ApplyOrdering(source, propertyName, ThenByMethod);
    }

    public static IOrderedQueryable<TSource> ThenByDescendingProperty<TSource>(this IOrderedQueryable<TSource> source, string propertyName)
    {
        return ApplyOrdering(source, propertyName, ThenByDescendingMethod);
    }
}



我敢肯定,我需要拆分 propertyName的的时间,然后用这些配件组装起来更复杂的表达,涉及 MemberExpression 再财产,但我挣扎。在正确的方向任何帮助或指示,将不胜感激。

I'm pretty sure I need to split the propertyName on the period and then use those parts to build up a more complicated Expression that involves a MemberExpression and then a Property but I'm struggling. Any help or pointing in the right direction would be appreciated.

推荐答案

我写我自己的谓词建设者类型的事情而回。我试图适应代码张贴在这里。这将返回一个表达式来访问属性,可以用来建立更复杂的表达式 - 只要确保表达的所有组件使用完全相同的参数对象实例

I wrote my own predicate builder type thing a while back. I attempted to adapt the code for posting here. This returns an expression to access a property, and can be used to build up more complicated expressions - just make sure that all the components of the expression use the exact same param object instance.

这不会在您的代码一滴工作。它会需要一些轻微的改动,使之成为您的使用,我认为工作

This won't work as a drop in for your code. It'll will require some slight adaptations to make it work for your use I think.

这输出参数=> (param.Child.IntProperty == 42)

您可以使用谓词在where子句中的变量。比方说,你有一个列表<家长和GT; 名为家长,你可以调用 parents.Where (谓语)

You could use the predicate variable in a where clause. Let's say you had a List<Parent> called parents, you could call parents.Where(predicate).

public class Parent {
    public string StringProperty { get; set; }

    public Child Child { get; set; }
}

public class Child {
    public int IntProperty { get; set; }
}

internal class Program {

    private static void Main(string[] args) {
        var param = Expression.Parameter(typeof(Parent), "param");
        var accessExpression = GetAccessExpression(param, "Child.IntProperty", typeof(Parent));
        var constantExpression = Expression.Constant(42);
        var condition = Expression.Equal(accessExpression, constantExpression);
        var predicate = Expression.Lambda<Func<Parent, bool>>(condition, param);

        Console.WriteLine(predicate.ToString());
    }

    /// <summary>
    /// Returns an Expression that represents member access for the specified property on the specified type. Uses recursion
    /// to find the full expression.
    /// </summary>
    /// <param name="property">The property path.</param>
    /// <param name="type">The type that contains the first part of the property path.</param>
    /// <returns></returns>
    private static Expression GetAccessExpression(Expression param, string property, Type type) {
        if (property == null)
            throw new ArgumentNullException("property");
        if (type == null)
            throw new ArgumentNullException("type");

        string[] propPath = property.Split('.');
        var propInfo = type.GetProperty(propPath[0]);

        if (propInfo == null)
            throw new Exception(String.Format("Could not find property '{0}' on type {1}.", propPath[0], type.FullName));

        var propAccess = Expression.MakeMemberAccess(param, type.GetProperty(propPath[0]));

        if (propPath.Length > 1)
            return GetAccessExpression(propAccess, string.Join(".", propPath, 1, propPath.Length - 1), type.GetProperty(propPath[0]).PropertyType);
        else
            return propAccess;
    }
}

这篇关于C#谈到魔术串入lambda表达式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆